Praktický příklad na „posun meta“: Agenda Číselníky, část 1

Takřka v každém evidenčním systému se vyskytují tzv. číselníky, někdy zvané také jako „kódovníky“, anglicky např. jako „Code List“. Jedná se o velmi jednoduché entity obsahující „kód + text“ (případně zkratka apod.), které pomocí odkazu přidělují daným prvkům vlastnosti jako hodnotu kódu. Většinou se tak děje ve scénáři výběrem od obsluhy. Každému kódu je přiřazen význam, což vyjadřuje obsah atributu text zobrazitelný obsluze. Jako příklady uveďme číselník barev, číselník typů bankovních služeb, číselník typů osob atd. Existuje několik možných analytických návrhů, jak tyto entity navrhnout a poté realizovat. V tomto článku si tyto možnosti uvedeme a také upozorníme na tzv. „posun meta“ (vysvětlení jevu nazvaném jako „posun meta“ viz kniha Analytické modelování v praxi zdarma zde).

Nechť chceme implementovat agendu číselníků „kód text“. Jako příklad zvolme dva číselníky: „Typ bankovních služeb“ (dále také „Typ BS“) a „Typ osob“ (dále také „Typ OS“).

Vyjmenujme si varianty řešení.

1. Varianta: Číselníky natvrdo (varianta „hard coded“)

Jako první variantu si uveďme způsob v posunu meta nejníže, tedy variantu „natvrdo“ („hard coded“). V tom případě vznikne řešení podle pravidla „co číselník, to analytická třída a následně tabulka“. Znamená, že s každým novým číselníkem se založí nová třída a poté i nová tabulka (tj. CREATE TABLE pro každý číselník se svým názvem).
Analytický model této varianty pak vypadá v našem příkladu takto:

Každá třída číselníku je multiinstanční, každá stojí samostatně a dává vzniknout itemům daného typu obsahujících kód a text.

2. Varianta: Posun meta s opětovnou použitelností

Zkusme si napsat jako příklad několik málo instancí ze tříd našich číselníků Typ BS a Typ OS. Současně si tyto třídy očíslujeme pro přehlednost takto:

Všimněme si, že „hlavičky“ číselníků (tj. neodsazené řádky) se chovají také jako prvky číselníku. Když si odmyslíme spodní prvky a ponecháme jen hlavičky, tak vznikne také číselník „kód text“:

1; TYP BS
2; TYP OS

Model tříd můžeme tedy navrhnout jako dvojúrovňový strom, analytický model je následující:

Všimněme si omezující podmínky, že se jedná o strom pouze se dvěma úrovněmi (tj. „hlavička“ a „itemy“).

Mapováním do relační databáze 1:1 dostáváme jednu tabulku, do které jsou umístěny prvky obou úrovní (tj. instance číselníků a itemy číselníků se vyskytují spolu v jedné tabulce), přičemž vazba mezi child a parent prvkem může být např. přes sloupec „autoincrement id“ a cizí klíč idParent takto:

Vrcholy stromu, tj. samotné číselníky (tj. „hlavičky“) nemají již nadřízeného Parenta a proto mají hodnotu vazebního klíče idParent = null.

Pozn. vazbu lze realizovat také přes kód a cizí klíč KódParenta. V tom případě má parent prvek KódParenta = 0. Hodnoty ve sloupci kód nejsou unikátní, proto vazba child itemu na vlastníka bude vyjádřena nejenom shodou klíčů kódů, ale také dodatečnou podmínkou, že kódParenta u Parenta = 0, tj. že vlastníkem itemu je „hlavička“. Tento způsob se mi však nejeví jako příliš vhodný, protože mohou nastat problémy, pokud se začne řešit problematika historie číselníku (např. kód itemu dané barvy aut se historicky změní, ale jedná se historicky o tutéž barvu).

Uvedená varianta reprezentuje oproti předešlému řešení „natvrdo“ ukázkový příklad posunu meta. Všimněme si, že to, co se ve variantě 1 (tj. varianta „hard coded“ = „co číselník, to třída“) vyskytovalo v názvech tříd (v našem příkladu Typ Osoby a Typ BS), tak to se ve druhé variantě umístilo do hodnot instancí. To již není na úrovni tříd, ale na úrovni instancí a hodnot, došlo tedy k posunu od tříd do instancí.

Tento rozdíl se projeví například v těchto dvou situacích:

a. Druhá varianta s posunem meta je dynamická v tom smyslu, že uživatel může dynamicky přidat nový číselník bez nutnosti měnit kód. Pokud bychom tuto funkcionalitu vyžadovali i v prvém řešení, museli bychom v běhu programu volat „CREATE TABLE“, což je vlastně meta-programování.

b. Chceme uživateli vypsat všechny možné použitelné číselníky (kódovníky) a on by si měl vybrat číselník. Ve variantě 2 s posunem meta se jedná o jednoduchý příkaz SELECT nad danou tabulkou s podmínkou pro cizí klíč idParent = null. V první variantě bychom se museli posunout do meta-prostoru, protože vypisujeme seznam tabulek, což by se dalo vyřešit například „pseudo-systémovou“ tabulkou, která by obsahovala seznam názvů tabulek a „imitovala“ by tak systémovou tabulku v meta-prostoru databáze.

3. Varianta: Posun meta, ale se dvěma třídami

Příklad b. uvedený v předešlém odstavci vede k možnému třetímu řešení, které je podobně jako varianta 2 posunem meta, ale nevyužívá opětovnou použitelnost na 100% (tj. „něco se v řešení opakuje“). V tomto případě se nepoužije jedna třída, ale dvě třídy: Jedna vyjadřuje samotné číselníky a druhá jejich itemy jako vlastněné prvky, vyjádřeno modelem tříd takto:

Omezující podmínku dvou úrovní již nemusíme nasazovat, je to již vlastnost modelu tříd jako takového, protože číselníky mají itemy a itemy již nemají žádné děti.

Při mapování do RDB vzniknou dvě tabulky, jedna pro seznam číselníků (umístíme do jedné tabulky) a itemy jako jejich děti (umístíme do tabulky druhé). Oproti předešlému řešení se opakuje struktura „kód text“, takže její obsloužení se bude programovat dvakrát pro obě třídy (což si myslím není až tak tragické).

Závěr první části

Máme zde před sebou tři řešení. Za svou dvacetiletou praxi jsem měl možnost spolupracovat se stovkami firem v ČR a SR, takže jsem se v praxi setkal se všemi třemi implementacemi.


Uveřejněno

v

od

Značky:

Komentáře

6 komentářů: „Praktický příklad na „posun meta“: Agenda Číselníky, část 1“

  1. Robert avatar
    Robert

    Každý číselník má mít svou tabulku.
    Zdůvodnění: Mnohem častěji než číselník budeme přidávat položku číselníku. A protože při přidání této položky rozhodně nechceme procházet celý program a hledat výskyt nějakého magického písmene (či id) a přídat k němu přes OR jiné písmeno, tak i položky číselníku musíme mít rozdělené podle vlastností. Nesouhlasím tedy ani s tím, že číselník má jen kód a popis.
    Například tu mám čísleník „Způsob zaplacení zakázky“. Ten začínal dvouma záznamama „Hotově“ a „Převodem“. Ale už tehdy jsem si v něm správně založil boolean sloupce „Generovat fakturu“ a „Generovat účtenku“ a nastavil je. Pak se přidal způsob placení „Kartou“. Boolean sloupce jsem nastavil stejně jako u Hotově. Pak přišel způsob uhrazení „Reklamace zdarma“. Hodnoty Generovat účteku i Generovat fakturu jsou u něj 0 a naopak přibyl sloupec „Nekontrolovat ziskovost“. Pak přišla položka „Vzorek zdarma“ atd… A to je jen hloupý číselník „Způsobů úhrady“
    Stavové číselníky mívají takových boolen sloupců ještě mnohem více třeba „Stav zakázky“ má kontrolních sloupců asi 8. Před překlopením zakázky do jistých stavů je potřeba kontrolovat, zda byl materiál odepsán ze skladu, před jinými je potřeba kontrolovat zda bylo zadánu, kdo práce na zakázce provrdl, před jinými stavy je potřeba kontrolovat zda byla zakázka uhracena, atd… A vyskytuje se v něm spousta položek se shodným stavem. Třeba stavy „Čeká na DPD“ a „Čeká na PPL“ jsou v kontrolních sloupcích samozřejmě stejné. Ale uživatel je i přesto potřebuje rozlišit a po příjezdu kurýrní služby vybrat ty správné zakázky.
    Osobně si tedy myslím, že vzhledem k tomu že aplikace bude časem růst a do číselníku se budou přidávat sloupce (dál třeba i barva každého stavu zakázku) tak je vhodné mít co číselník to tabulku.

  2. Jiří Matějček avatar
    Jiří Matějček

    Hezké příklady.

    Časem se zjistí, že nestačí jen „kód a text“, ale položky dostanou další vlastnosti. Při metamodelu začnou přibývat sloupečky nebo „specializované tabulky detailů“, a už to není tak čitelné, co k jakému číselníku patří. A ještě přibývající sloupečky mohou být dvojího druhu – buď přesně pojmenované, takže aspoň je zřejmý jejich význam, nebo „meta“ např. „text1“, „text2“, „number1“,…. a podle typu číselníku se do toho krmí hodnoty jakéhosi významu. Tím vznikne v db v datech mixle-v-pixle. Ve výrobních firmách mají v číselníkách typů i desetitisíce položek, pak už je to hodně zmixované. Ještě je řešením postavit nad meta-číselníky views a tím to zase „rozmotat“.

    Po těchto zkušenostech k meta-číselníku moc netíhnu. Navíc se pak na něj špatně odkazuje v dokumentaci, když taková „entita“ neexistuje a je jen virtuální. Na úrovni analytické by každý číselník stejně měl mít svojí třídu. Leda by to byl nějaký balík číselníků, a bylo by požadováno je dynamicky přidávat. Ale k tomu musí být i podpora na úrovni zdrojového kódu, jen založením se nový číselník nikde jinde v logice neprojeví.
    Takže preferuji „klasiku“, co číselník, to třída, to tabulka. Je to přehledné a dobře se stopují závslosti.

    Mimochodem pracujeme se systémem, který má asi 4000 tabulek, je tu mezi nimi i několik meta-řešení. Takže mám co srovnávat. Hledat v meta-strukturách, popisovat jejich změny, vysvětlovat kolegům z jiných oddělení (hlavně z DWH) „jak je to udělané“ a proč nemůžou ten „atribut“ najít, to je každodenní peklo. 🙂

    1. Zdenek Suchacek avatar

      Konkrétní příklad z praxe:

      1. Uživatel může vybírat pouze z určitých hodnot povolených na základě práv
      2. Číselník je tzv. Multilanguage, tudíž výsledná zobrazená hodnota musí být zobrazeno v jeho jazyku
      3. Filtrování povolených kombinací navázaných číselníku např. Stát/Kraj/Okres/Město

      Pracujeme v tzv. Blackboxu, konkrétně M/S Dynamics 365.

      1. RNDr. Ilja Kraval avatar

        Mohl byste prosím více rozvést tyto příklady v návaznosti na článek?
        Přiznám se, že mi není úplně jasná souvislost.
        Díky 🙂

        1. Peter avatar

          dovolim si odpovedat, ze suvis to nema, nakolko clanok je o definovani ciselnikov a pan Suchacek pise o dodatocnych atributoch a ich vybere z konkretnych hodnot

        2. vlnk avatar
          vlnk

          Také nevidím přímou souvislost. Spíš klasifikaci jako primitivního kódovníku vhodného pro I18N. V praxi se často (většinou) setkávám s názvem kódovník/číselník pro mnohem složitější struktury, jak uvádí ZS. Např. STAVY, kde se právě ukrývá minimálně ještě workflow a authorizace. Je tedy možné uvést vazbu na v článku popisovaný kódovník, ale jen pro uložení názvu. A tady vzniká dilema normalizované DB: volit nadbytečnost nebo přehlednost?
          Z hlediska programování a použitých zdrojů jasně vazba typu I18N, protože objekt jen několikrát mění svůj stav, ale tisíckrát se aktuální stav reportuje.

Napsat komentář

Vaše e-mailová adresa nebude zveřejněna. Vyžadované informace jsou označeny *