Stromy a hierarchické struktury v SQL Mária Szabó, Jirka Zouhar, Gergely Jakab
Using the Node Type to Solve Problems with Hierarchies in DB2 Universal Database Mária Szabó
Základní pojmy Úvod do problematiky Koncept řešení Node type Výhody Příklady Node Type & Hierarchies in DB2
● Hiearchie ● Relační Db ● Node Type Pojmy
Node Type & Hierarchies in DB2 ● Před 30 lety – hierarchical db ● Svázánost se db strukturou ● Ukazatel – pointer ● Nákladné chyby ● Vícenásobné použití pro stejná data Problematika
Node Type & Hierarchies in DB2 Průměr účetní bilance pro každý region i odvětví 10 největších klientů Definovat více hierarchií Synchronizace dat
Node Type & Hierarchies in DB2 Pre-processing dat Myšlenka typu UZEL Série čísel Hladina – Level Indexování jako v SQL Uzel & Hierarchie
Node Type & Hierarchies in DB2 CREATE TABLE employee ( Employee_Id Integer, Manager_Id Integer, Last_Name varchar(30)... PRIMARY KEY(Employee_Id), FOREIGN KEY (Manager_Id) REFERENCES employee(Employee_Id); Kolik zaměstnanců pracuje pro jistého managera? SELECT COUNT (*) FROM employee emp, employee manager WHERE emp.Manager_Id = manager.Employee_Id AND manager.Last_Name = ‘Roy’; Pro každého zaměstnance zjistit počet podřízených !?!
Node Type & Hierarchies in DB2 ● Vícenásobné levely v managmentu ● SQL příkaz pro každý manager pro každý level ● Víckrát vnořovat Problém CREATE TABLE employee2 ( Employee_Id Node, Last_Name varchar(30),... PRIMARY KEY(Employee_Id);
Node Type & Hierarchies in DB2 1| |Joe| 2|1|Joe2|... 8|2|Joe8|... 14|3|Joe14| 1.0|Joe| 1.2|Joe2| |Joe8| |Joe14| NULL na místě managera šéf
Node Type & Hierarchies in DB2 ● Kolik zaměstnanců pracuje pod specifikovaným managerom? 1.Přímé potomky 2.Rekurzie dokud jsme neprošli všechny lidi 3.Rekurzivní dotaz nebo procedura WITH RPL (employee_Id, Manager_Id, Last_Name) AS (SELECT ROOT.employee_Id, ROOT.Manager_Id, ROOT.Last_Name FROM employee ROOT WHERE ROOT.employee_Id = 1 UNION ALL SELECT CHILD.EMployee_Id, CHILD.Manager_Id, CHILD.Last_Name FORM RPL PARENT, employee CHILD WHERE PARENT.Employee_Id = CHILD.Manager_Id ) SELECT COUNT(*) -1 FROM RPL; Pro EMPLOYEE table:
Node Type & Hierarchies in DB2 ● Kolik zaměstnanců pracuje pod špecifikovaným mangerom? SELECT COUNT(*) FROM Employee2 WHERE Employee_ID > NodeInput(‘1.2’) AND EMPLOYEE_ID < NodeInput(‘1.3’); Pro EMPLOYEE2 table: 1.3 > > 1.2 uzly, které padají pod spec. managerom
Node Type & Hierarchies in DB2 ÚroveňPočetRozdíl Test Každý manager 6 přímých potomků Lidi úplně na spodku nejsou managery pro 9331 řádků 37 násobné zrychlení tradiční přístup exponenciální char. uzlový přístup lineární charakteristika rozdíl se zvětšuje #levelu
Node Type & Hierarchies in DB2 Ancestors(Node) GetMember(Node,intege r) GetParent(Node) Graft(Node,Node,Node)Increment(Node)Increment(Node,integer) Graft(Node,Node,Node) IsChild(Node,Node) IsDescendant(Node,Nod e) IsParent(Node.Node) Length(Node)NewLevel(Node) NodeInput(varchar(180))NodeOutput(Node) Implementace typu UZEL - NODE CREATE DISTINCT TYPE Node AS varchar(64) FOR BIT DATA WITH COMPARISONS;
Node Type & Hierarchies in DB2 Indexování a SQL dotazy indexování typu uzel umístnit do WHERE klauzuli možnost i bez indexování scan table SELECT * FROM employee2 WHERE isDescendant(employee_Id, NodeInput(‘1.7.43’)) ORDER BY employee_Id Desc; Příklad vrátí všechny potomky uzlu
Node Type & Hierarchies in DB2 Příklad vrátí všechny potomky uzlu SELECT * FROM employee2 WHERE employee_Id > NodeInput(‘1.7.43’) AND employee_Id < NodeInput(‘1.7.44’) ORDER BY employee_Id Desc; Příklad vrátí pouze přímé potomky uzlu SELECT * FROM employee2 WHERE employee_Id > NodeInput(‘1.7.43’) AND employee_Id < NodeInput(‘1.7.44’) AND Length(employee_id) = 4 ORDER BY employee_Id Desc;
Node Type & Hierarchies in DB2 !Občas je zapotřeby přidat funkce pro zachování použitelnosti indexu! SELECT * FROM employee2 WHERE isAncestor(employee_Id, NodeInput(‘ ’)) ORDER BY employee_Id Desc; , , 1.7, 1.0 SELECT * FROM Employee2 WHERE Employee_Id IN ( SELECT x.col1 FROM TABLE(Ancestors(NodeInput(‘ ’))) AS x ) ORDER BY employee_Id Desc;
Node Type & Hierarchies in DB2 Hlavní výnos Obrovské tabulky rýchlá odezva Rozumné dotazy nezávislost na # požadavků
Děkuji Mária Szabó
Detaily hierarchii v DB Jirka Zouhar
Použití ● Ve složitějších případech je třeba oddělit hierarchii od dat ● Lze použít i více hierarchií ● Příklady: – Skladová evidence zásob – Hierarchické systémy zásad – Klasifikace objektů
Hierarchie zásad ● CREATE TABLE Policies( PolicyNumberNode, Descriptionvarchar(50)); ● CREATE TABLE Objects( Namevarchar(50), PolicyNumberNode); ● CREATE TABLE Safe_places( LocationNode, PolicyNumberNode, Descriptionvarchar(75), Administratorvarchar(30));
Hierarchie zásad ● Jaké objekty mají „malé“ zabezpečení? ● Jak zabezpečený je daný objekt? ● Mají všechny zóny korektní zabezpečení? ● Nejsou objekty v špatných zónách? ● SELECT * FROM Objects, Places WHERE Places.policyNumber NOT IN Ancestors(Objects.policyNumber)
Skladová evidence ● CREATE TABLE Parts ( PartNumberinteger, Namevarchar(30), Pricemoney); ● CREATE TABLE Components ( ComponentIdNode, Sequenceinteger, Quantityinteger, Namevarchar(30), PartNumberinteger);
Klasifikace Objektů ● CREATE TABLE materials ( – Namevarchar(30), – TypeNode); ● CREATE TABLE parts( – Namevarchar(30), – Typenode, – ComponentIdnode, – Priceinteger, – Providervarchar(30), – PartNumberingeter, – Descriptioninteger);
Klasifikace objektů 1Materiál 1.1.1mahagon 1.2železo1.1dřevo1.3kůže
Klasifikace objektů ● Najdi všechny součástky z mahagonu s cenou menší padesáti. ● SELECT name,desc,price FROM parts WHERE isDescendant(type, NodeInput( SELECT type FROM materials WHERE name='mahagon')) AND price < 50;
Funkce pro práci s Node Type ● Node nodeInput (varchar) Převede varchar na Node Type. ● Varchar nodeOutput (node) Převede Node Type na Varchar V dalším bude pro zjednodušení používán místo nodeInput ('1.3.5') zjednodušený zápis 1.3.5
Funkce pro práci s Node Type ● Node[] Ancestors (node) Vrátí seznam předků: Ancestors(1.25.3) = {1.25,1} ● Integer GetMember (Node, Integer) Vrátí číslo větve na dané úrovni: GetMember( ,2) = 3 ● Node getParent (node) Vrátí otce zadaného: getParent(1.5.4) = 1.5
Funkce pro práci s Node Type ● Node Increment (node) Vrátí další node v řadě: Incerment (1.2.22) = ● Node Increment (node, integer) Vrátí další node v řadě na dané úrovni: Increment (1.2.22, 2) = 1.3 ● Node newLevel (node) Vrátí nový node o úroveň níž: newLevel (1.2) = 1.2.1
Funkce pro práci s Node Type ● Integer isAncestor (node1, node2) Testuje, zda je node2 v podstromu node1: isAncestor (1.2, ) = 1 isAncestor (1.3, 1.2.4) = 0 ● Integer isDescendant(node,node) Opak isAncestor: isDescendant (node1, node2) = isAncestor (node2, node1)
Funkce pro práci s Node Type ● Integer isChild (node1, node2) Testuje, zda je node2 synem node1: isChild (1.2, 1.2.3) = 1 isChild (1.2, ) = 0 ● Integer isParent (node, node) Opak isChild ● Integer length(node) Zjistí úroveň node ve stromu: length ( ) = 4
Funkce pro práci s Node Type ● Node graft(node,node,node) Přesune větev danou třetím parametrem jako podstrom druhého parametru, první je otec přemísťovaného stromu. UPDATE Parts SET Part_num = Graft( 1.2, 1.5.6, Part_num) WHERE Part_num > AND Part_num <
Funkce pro práci s Node Type ● Graft (1.1.3, 1.4, ) =
Zdroje ● ibmdl/pub/software/dw/dm/db2/0302roy/ 0302roy.pdf ● ibmdl/pub/software/dw/dm/db2/0302roy/ DB2Node.zip
Hnízděné intervaly Gergely Jakab Nested intervals
Obsah ● Úvod ● Materializovaná cesta ● Vnořené množiny – vylepšení ● Vnořené intervaly – Základní myšlenka – Mapovaní – Normalizace – Funkce – Příklady: Test struktury
Úvod ● tranzitivní závislosti v relační databázi (hierarchické struktury) ● 4 dobře známé metody z nichž 2 prozkoumáme – materializovaná cesta – vnořené množiny ● Vadim Tropashko vymyslel smíšeninu těchto dvou metod (vnořené intervaly) ● implementace a příklady na Oracle
Skok zpátky : materializovaná cesta ● každý záznam obsahuje absolutní cestu k uzlu ● cesta = čísla uzlů od kořene oddělené separátorem ● jistá podobnost k absolutním cestám na UNIXu ● kompaktnější verze místo primárního klíče uzlů staví cestu z pořadového čísla uzlu mezi sourozenci
Dotazy u materializované cesty standardní dotazy s „LIKE“ : efektivní na podřízené NEefektivní na nadřízené chytré dotazování pro nadřízené: pomoci řetězcové funkce, která vrátí množinu cest všech nadřízených f(' ') = ('1.2.1', '1.2', '1') …WHERE super.path in f(this.path)
Vnořené množiny ● uchovává globální pozici uzlu ● pozice uzlu = 2 celé čísla definující interval [left, right], který obsahuje všechny potomky ● uzel p je předkem uzlu c právě když : p.left <= c.left <= c.right <= p.right ● nevýhody : – dotazy na nadřízené (hledání intervalů obsahující daný bod) – nestálá struktura – přidávání prvku ≈ přepočet půlku struktury – vhodné pouze pro statické množiny Albert (1,12) Bert (2,3) Chuck (4,11) Donna (5,6) Fred (9,10 ) Eddy (7,8)
Vnořené množiny - vylepšení ● původní pravidlo : – každý uzel (s pozicí [left, right]) má přesně (right - left - 1) / 2 potomků ● vylepšení od Joe Celko : – vypuštěním výše uvedeného pravidla nechat větší mezery mezi left a right pro přidávané potomky ● nepomůže, pokud left a right můžou být pouze celá čísla, protože za nějakou dobu se zaplní libovolně velká mezera
Vnořené intervaly - definice ● jde o zobecnění vnořených množin ● hranice intervalů již nemusí být celá čísla – potřebujeme hustší doménu – podle potřeby můžou být racionální nebo reálné čísla ● nadále platí : uzel p je předkem uzlu c právě když p.left <= c.left <= c.right <= p.right ● je několik způsobů jak umístit do struktury nový uzel
Vnořené intervaly - 1.způsob ● najdeme volný úsek uvnitř intervalu rodiče ● pak vkládaný uzel bude [(2*left1 + right1)/3, (left1 + 2*right1)/3] ● a pro další potomky toho rodiče ještě jsou k dispozici : a p.leftleft1 2*left1 + right1)/3(left1 + 2*right1)/3 right1p.right
Vnořené intervaly - 2.způsob ● 2-dimenzionální model : – osa x je right – osa y je left ● relace „býti podmnožinou“ je částečné uspořádání y x
Vnořené intervaly - mapování ● kořen zvolíme libovolně, např. [0,1] ● každý uzel pak reprezentuje jeden pravoúhlý trojúhelník pod diagonálou ● definujeme 2 důležité body: – bod konvergence do hloubky ● průsečík diagonály a vertikály protínající uzel – bod konvergence do šířky ● průsečík diagonály a horizontály protínající uzel y x b.k. do hloubky b.k. do šířky
Vnořené intervaly - mapování2 ● pro každý uzel pozice jeho : – prvního syna je v polovině úsečky mezi uzlem a b.k. do hloubky – dalších synů je v polovině úsečky mezi předchozím synem a b.k. do šířky ● např.: uzel 1.2 je [ ¼, ½ ]
Vnořené intervaly - symetrie ● naše hustá doména nebude doména reálných ani racionálních čísel, ale doména dvojic zlomků ● symetrie – každý podstrom má stejnou strukturu – syn je zmenšený rodič – obdoba fraktálů
Vnořené intervaly - normalizace ● kvůli zmíněné symetrie souřadnice uzlu nejsou na sobě nezávislé ● z jejich sumy lze vypočítat složky x a y ● namísto dvojic zlomků stačí pamatovat součet dvojic, tj. jeden zlomek, tj. 2 celá čísla ● tím jsme se dostali na úroveň vnořených množin v množství uchovávaných dat na jeden uzel
Vnořené intervaly - normalizace function x_numer( numer integer, denom integer ) RETURN integer IS ret_num integer; ret_den integer; BEGIN ret_num := numer+1; -- posun svisle nahoru na diagonálu ret_den := denom*2; -- souřadnice x je polovina podílu while floor(ret_num/2) = ret_num/2 loop -- redukce zlomku ret_num := ret_num/2; ret_den := ret_den/2; end loop; RETURN ret_num; -- pro čitatel -- RETURN ret_den; -- pro jmenovatel ve funkci x_denom() END;
Vnořené intervaly - normalizace function y_numer( numer integer, denom integer ) RETURN integer IS ret_num integer; ret_den integer; BEGIN ret_num := x_numer(numer, denom); -- získání čitatele x ret_den := x_denom(numer, denom); -- získání jmenovatele x while den < denom loop -- převést na společný jmenovatel ret_num := ret_num*2; ret_den := ret_den*2; end loop; ret_num := numer – ret_num; -- získat čitatel y while floor(ret_num/2) = ret_num/2 loop -- redukce zlomku ret_num := ret_num/2; ret_den := ret_den/2; end loop; RETURN ret_num;-- pro čitatel END; -- RETURN ret_den; -- pro jmenovatel ve funkci y_denom()
Vnořené intervaly - příklad1 SELECT x_numer(39,32) || '/' || x_denom(39,32), y_numer(39,32) || '/' || y_denom(39,32) FROM dual; 5/819/32 SELECT 5/8 + 19/32 FROM dual; == 39/32
Funkce – pozice rodiče function parent_numer( numer integer, denom integer ) RETURN integer IS ret_num integer; ret_den integer; BEGIN if numer = 3 then -- uzel nejvyšší úrovně return NULL; end if; ret_num := (numer-1)/2; -- posun svisle dolů ret_den := denom/2; -- posun vodorovně doprava while floor((ret_num-1)/4) = (ret_num-1)/4 loop ret_num := (ret_num+1)/2; ret_den := ret_den/2; end loop; RETURN ret_num; -- pro čitatel END; -- RETURN ret_den; -- pro jmenovatel
Funkce – pořadí mezi sourozenci function sibling_number( numer integer, denom integer ) RETURN integer IS ret_num integer; ret_den integer; ret integer; BEGIN -- uzel nejvyšší úrovně + posun svisle dolů jako v předchozím ret := 1;-- inicializace pořadí na 1 while floor((ret_num-1)/4) = (ret_num-1)/4 loop -- posun doprava if ret_num=1 and ret_den=1 then return ret;-- zkoumaný uzel je první syn end if; ret_num := (ret_num+1)/2; ret_den := ret_den/2; ret := ret+1;-- každá iterace znamená posun o 1 sourozence end loop; RETURN ret; -- pořadí mezi sourozenci END;
Vnořené intervaly - příklad2 Uzel má pozici 27/32 a uzel 2.1 (rodič předchozího) má 7/8 SELECT parent_numer(27,32) || '/' || parent_denom(27,32) FROM dual; 7/8 SELECT sibling_number(27,32) FROM dual; 2
Funkce – materializovaná cesta function path( numer integer, denom integer ) RETURN varchar2 IS BEGIN if numer is NULL then-- konec rekurze return ''; end if; RETURN --rekurzivně získá cestu k rodiči path( parent_numer(numer, denom), parent_denom(numer, denom) ) -- připojí pořadí aktualního uzlu mezi svými sourozenci || '.' || sibling_number(numer, denom); END; SELECT path(15,16) FROM dual;.2.1.1
Funkce – vzdálenost dvou uzlů function distance( num1 integer, den1 integer, num2 integer, den2 integer ) RETURN integer IS BEGIN if num1 is NULL then-- signalizuje, ze uzel num2/den2 není return ;-- předkem uzlu num1/den1 end if; if num1=num2 and den1=den2 then return 0;-- došli jsme do num2/den2 end if; RETURN1+distance(-- rekurze na rodiče + zvýšení délky cesty parent_numer(num1, den1), parent_denom(num1, den1), num2, den2 ); END; SELECT distance(27,32,3,4) FROM dual; 2
Funkce – čtení materializované cesty ● Funkce : function path_numer( path varchar2 ) RETURN integer function path_denom( path varchar2 ) RETURN integer parsují vstupní řetězec a na každé úrovni pomoci pozice rodiče a pořadí aktuálního uzlu mezi sourozenci vypočítávají pozici aktuálního uzlu SELECT path_numer('2.1.3') || '/' || path_denom('2.1.3') FROM dual; 51/64
Test struktury – definice schématu ● předpokládejme, že v tabulce emps máme uložené zaměstnance a jejich pozice získané z materializovaných cest v tabulce vpravo ● create table emps ( name varchar2(30), numer integer, denom integer ) ● tabulku naplníme sekvencí INSERTů tvaru : INSERT INTO emps VALUES ( 'FORD', path_numer('1.1.2'),path_denom('1.1.2') );
Test struktury – definice schématu ● z tabulky emps a ze zatím definovaných funkcí vytvoříme pohled, nad kterým se bude dobře dotazovat : CREATE OR REPLACE VIEW hierarchy AS SELECT name, numer, denom, y_numer(numer,denom) numer_left, y_denom(numer,denom) denom_left, x_numer(numer,denom) numer_right, x_denom(numer,denom) denom_right, path (numer,denom) path, distance(numer,denom,3,2) depth FROM emps;
Test struktury – procházka do hloubky Výsledek: LPAD('',3*DEPTH) || NAME KING CLARK MILLER BLAKE WARD ALLEN JONES FORD SMITH SCOTT ADAMS Dotaz : SELECT lpad(' ',3*depth)||name FROM hierarchy ORDER BY numer_left/ denom_left; Zdroj :
Test struktury – procházka do hloubky Výsledek: LPAD('',3*DEPTH) || NAME KING JONES SCOTT ADAMS FORD SMITH BLAKE ALLEN WARD CLARK MILLER Dotaz : SELECT lpad(' ',3*depth)||name FROM hierarchy ORDER BY numer_right / denom_right; Zdroj :
Test struktury – podřízené Výsledek: NAME SCOTT ADAMS FORD SMITH Dotaz : SELECT h1.name FROM hierarchy h1, hierarchy h2 WHERE h2.name = 'JONES' AND distance( h1.numer, h1.denom, h2.numer, h2.denom) > 0 ; Zdroj :
Test struktury – nadřízené Výsledek: NAME KING JONES Dotaz : SELECT h2.name FROM hierarchy h1, hierarchy h2 WHERE h1.name = 'FORD' AND distance( h1.numer, h1.denom, h2.numer, h2.denom) > 0 ; Zdroj :
Zdroje ● zdrojem pro referát sloužil článek od Vadim Tropashko : Trees in SQL: Nested Sets and Materialized Path ● navazující témata : Nested Intervals with Farey Fractions ● newsgroups : comp.database.theory comp.database.oracle