Prezentace se nahrává, počkejte prosím

Prezentace se nahrává, počkejte prosím

Rekurzivní SQL Petr Čermák Michal Danihelka. Osnova Úvod do problematiky Rekurzivní SQL v Oraclu Řešení rekurzivních úloh bez podpory rekurzivního SQL.

Podobné prezentace


Prezentace na téma: "Rekurzivní SQL Petr Čermák Michal Danihelka. Osnova Úvod do problematiky Rekurzivní SQL v Oraclu Řešení rekurzivních úloh bez podpory rekurzivního SQL."— Transkript prezentace:

1 Rekurzivní SQL Petr Čermák Michal Danihelka

2 Osnova Úvod do problematiky Rekurzivní SQL v Oraclu Řešení rekurzivních úloh bez podpory rekurzivního SQL Rekurzivní SQL v DB/2 Použití DB/2 UDF

3 Úvod do problematiky

4 Co je to rekurzivní dotaz? Dotaz je rekurzivní, pokud se záznamy odkazují sami na sebe pomocí primárního nebo cizího klíče. Vazba může být přímá, nebo i přes několik tabulek

5 Použití rekurzivních dotazů Obecně kdekoliv mají data hierarchickou strukturu Uspořádání organizace Struktura výrobku Rodinná hierarchie

6 Divergentní hierarchie Každý uzel má nejvýše jednoho otce Reprezentace tabulkou s poli Klíč, Otec a nějaká hodnota přiřazená objektu Kořen možno označit například Klíč = Otec

7 Konvergentní hierarchie Uzel může mít víc předků, nesmí však být svým otcem / dítětem (acyklický graf) Jedna tabulka nestačí Potřebujeme dvě – Objekty(PKey, Num) a Vztahy(PKey, CKey, Parametr vztahu)

8 Rekurzivní hierarchie Graf může obsahovat i cykly Reprezentace stejná jako v předchozím případu Riziko zacyklení

9 Další dělení Vyvážené Hladiny většinou různých typů Bývají divergentní Nevyvážené Hladiny prvků stejného druhu

10 Podpora rekurze v SQL ANSI SQL nepodporuje dotazování popsaným způsobem ani výpočet obecně rekurzivních funkcí podpora je zahrnuta v „chystaném“ ANSI SQL3 (SQL:1999) náznaky implementace v ORACLE a DB2

11 Vlastnosti rekurzivních dotazů vše na jeden dotaz nepřehlednost výhodné pouze když využiji velkou část výsledkové sady

12 Jak se obejít bez rekurze? Použití cursorů, cyklů,... Ztráta možnosti optimalizace Kód není tak “elegantní”

13 Požadavky kladené na rekurzi Při procházení grafem můžeme získat: dosažitelnost vyčíslitelnost cest spojování cest výpočet obecně rekurzivní funkce při průchodu vrcholem nebo hranou

14 Příklad: (vztahy mezi součástmi) P1 P2P3P4 P5P6P7 P8P

15 Dosažitelnost Z daného vrcholu, nebo množiny vrcholů, chceme zjistit všechny dosažitelné potomky “Z jakých částí se skládá výrobek P1?”

16 Vyčíslitelnost cest “Zobraz celou strukturu výrobku P1 se všemi jeho částmi” P1 (1) P2 (2) P3 (2) P4 (2) P5 (6) P6 (4) P7 (6) P8 (8) P9 (4) P6 (2) P8 (4) P9 (4) 12

17 Spojování cest “Jaké jsou části výrobku P1 a kolik jich je třeba k jeho sestavení?” P1 (1) P2 (2) P3 (2) P4 (2) P5 (6) P6 (6) P7 (6) P8 (12) P9 (6)

18 Rekurzivní SQL v Oraclu

19 Co Oracle podporuje? dosažitelnost vyčíslitelnost cest jednu rekurzivní funkci level

20 Příklad: (struktura výrobku) CREATE TABLE Parts (part# CHAR(5) PRIMARY KEY, part_name CHAR(4) NOT NULL, assembly_time INTEGER NOT NULL CHECK (VALUE >= 0), category_id INTEGER NOT NULL, FOREIGN KEY category_id REFERENCES Category); CREATE TABLE Usage (parent_part# CHAR(5) NOT NULL, component_part# CHAR(5) NOT NULL, quantity INTEGER NOT NULL, PRIMARY KEY(parent_part#, component_part#), FOREIGN KEY parent_part# REFERENCES Parts, FOREIGN KEY component_part# REFERENCES Parts);

21 CONNECT BY, START WITH Klauzule START WITH definuje počáteční podmínku pro dotaz CONNECT BY specifikuje vazbu mezi rodičovskými řádky a potomky (pomocí operátoru PRIOR)

22 Příklad: (zobraz strukturu výrobku – vyčíslitelnost cest) SELECT LEVEL, parent_part#, component_part# FROM Usage CONNECT BY PRIOR component_part# = parent_part# START WITH parent_part# = ‘P1’ Výsledek dotazu není tabulka, výsledek je nutně uspořádaný Omezení -- nemožnost počítat libovolnou obecně rekurzivní funkci omezení na používání spojení v rek. dotazech

23 Algoritmus zpracování dotazu 1) Zjisti řádky splňující podmínku v START WITH 2) Najdi všechny potomky vzhledem k řádkům z kroku 2 nebo předchozího kroku 3 splňující podmínku v CONNECT BY 3) Opakuj krok 3 dokud dostáváš nové řádky 4)Eliminuj všechny řádky, které nesplňují podmínku ve WHERE 5) Vrať zbylé řádky.

24 Pořadí vrácených řádků Řádky jsou vraceny v preorderu tj. nejdříve rodičovský vrchol, pak teprve podstromy.

25 Příklad: (Dosažitelnost) SELECT DISTINCT component_part# FROM (viz. select pro zobrazení struktury)

26 Příklad: (obcházení joinu v connect by – součástky s popisem) SELECT part#, category_name FROM Parts,Category WHERE Parts.category_id = Category.category_id AND part# IN (SELECT component_part# FROM Usage START WITH parent_part# = ‘P1’ CONNECT BY PRIOR componet_part# = parent_part#);

27 Příklad: (obcházení joinu v connect by – dosažitelné dvojice) SELECT DISTINCT PX.part#, PX.part_name#, PY.part#, PY.part_name# FROM Parts PX,Parts PY WHERE PY.part# IN (SELECT component_part# FROM Usage START WITH parent_part# = PX.part# CONNECT BY PRIOR componet_part# = parent_part#) ORDER BY PX.part#, PY.part#;

28 Rozšíření Oracle 9i – podpora join SELECT employee_name, manager_name, dept_name FROM employee, dept WHERE employee.deptno = dept.deptno START WITH employee_name = 'KING' CONNECT BY PRIOR employee_id = manager_id;

29 Rozšíření Oracle 9i – třídění řádků s daným předkem SELECT employee_name, manager_name, dept_name FROM employee, dept WHERE employee.deptno = dept.deptno START WITH employee_name = 'KING' CONNECT BY PRIOR employee_id = manager_id ORDER SIBLINGS BY hire_date;

30 Rozšíření Oracle 9i - cesty SELECT employee_name, SYS_CONNECT_BY_PATH(employee_name, '/') "PATH" FROM employee START WITH employee_name = 'KING' CONNECT BY PRIOR employee_id = manager_id;

31 Rozšíření Oracle 10g – společný předek SELECT DISTINCT CONNECT_BY_ROOT assembly_id, CONNECT_BY_ROOT assembly_name FROM bill_of_materials WHERE part_number = 1019 START WITH parent_assembly IS NULL CONNECT BY parent_assembly = PRIOR assembly_id; CONNECT_BY_ROOTASSEMBLY_ID CONNECT_BY_ROOTASSEMBLY 100 Automobile

32 Rozšíření Oracle 10g – jsem list? – část 1 SELECT ASSEMBLY_ID, RPAD(' ', 2*(LEVEL-1)) || assembly_name assembly_name, quantity, CONNECT_BY_ISLEAF FROM bill_of_materials WHERE LEVEL <= 2 START WITH assembly_id = 100 CONNECT BY parent_assembly = PRIOR assembly_id; ASSEMBLY_IDASSEMBLY_NAMEQUANTITYCONNECT_BY_ISLEAF 100Automobile0 110Combustion Engine10 120Body10 130Interior10

33 Rozšíření Oracle 10g – jsem list? – část 2 SELECT ASSEMBLY_ID, RPAD(' ', 2*(LEVEL-1)) || assembly_name assembly_name, quantity, CONNECT_BY_ISLEAF FROM bill_of_materials WHERE LEVEL = 2 START WITH assembly_id = 110 CONNECT BY parent_assembly = PRIOR assembly_id ; ASSEMBLY_IDASSEMBLY_NAMEQUANTITYCONNECT_BY_ISLEAF 111Piston61 112Air Filter11 113Spark Plug61 114Block11 115Starter System10

34 Rozšíření Oracle 10g – cykly SELECT RPAD(' ', 2*(LEVEL-1)) || assembly_name assembly_name, quantity, CONNECT_BY_ISCYCLE FROM bill_of_materials START WITH assembly_id = 100 CONNECT BY NOCYCLE parent_assembly = PRIOR assembly_id; ASSEMBLY_ NAME QUANTITYCONNECT_ BY_ISCYCL E Automobile0 Combustio n Engine 10 Piston60 Air Filter10 Spark Plug61 Block10

35 Řešení rekurzivních úloh bez podpory rekurzivního SQL

36 Formáty pro tabulky jakožto výsledky dotazů – část 1 Na rozdíl od oracla nesmějí mít tyto tabulky žádné číslování, aby šli používat v poddotazech Formát dostupnosti (kam se všude dostanu z vrcholu) – stačí binární tabulka obsahující kořen a syna parent_part#component_part#

37 Formáty pro tabulky jakožto výsledky dotazů – část 2 Formát vyčíslování cest (struktura s počtem podčástí) – použijeme následující tabulku seq#levelparent_part#componet_part#max_subtree_seq#total_quantify Formát spojování cest (seznam následníků s počtem) – použijeme následující tabulku parent_part#componet_part#total_quantify

38 Kterak použít ANSI SQL k vyhodnocení rekurzivních dotazů – část 1 Dostupnost SELECT DISTINCT parent_part#, component_part# FROM PartsPathEnum WHERE parent_part# = ‘P1’ Vyčíslování cest SELECT * FROM PartsPathEnum WHERE parent_part# = ‘P1’ ORDER BY squence#;

39 Kterak použít ANSI SQL k vyhodnocení rekurzivních dotazů – část 2 Spojování cest SELECT parent_part#, component_part#, SUM(total_quantity) FROM PartsPathEnum WHERE parent_part# = ‘P1’ GROUP BY parent_part#,component_part#;

40 Kde vzít tabulku vyčíslitelnosti cest? PL/SQL nebo jakýkoliv jazyk podporující rekurzi a SQL Doporučeno udělat index na kombinaci sloupců (parent_part#,component_part#), jelikož je tato tabulka typicky hodně veliká Není aktuální, ale v praxi většinou nevadí

41 Ořezávání dotazu SELECT PE1.* FROM PartsPathEnum PE1, PartsPathEnum PE2 WHERE PE1.parent_part# = ‘P1’ AND PE2.parent_part# = ‘P1’ AND PE2.component_part# = ‘P3’ AND (PE1.seq# NOT BETWEEN PE2.seq# AND PE2.max_subtree_seq#) ORDER BY PE1.seq#;

42 Rekurzivní SQL v DB/2

43 Úvodní příklad Úkol: Získat seznam potomků (i nepřímých) uzlu A. PKEYCKEYNUM AB1 AC5 AD20 CE33 DE44 DF5 FG5 HIERARCHY A B CD EF G

44 Řešení WITH PARENT (PKEY, CKEY) AS (SELECT PKEY, CKEY FROM HIERARCHY WHERE PKEY = ‘A‘ UNION ALL SELECT C.PKEY, C.CKEY FROM HIERARCHY C, PARENT P WHERE P.CKEY = C.PKEY ) SELECT PKEY, CKEY FROM PARENT; PKEYCKEY AB AC AD CE DE DF FG A B CD EF G

45 Řešení problému zacyklení Týká se rekurzivních hierarchií Dvě základní metody řešení: Zastavit procházení po určitém počtu hladin Udržovat si seznam navštívených uzlů a ignorovat dříve navštívené

46 Příklad 2 Seznam potomků s hloubkou, v jaké se nalézají. WITH PARENT (CKEY, LVL) AS (SELECT DISTINCT PKEY, 0 FROM HIERARCHY WHERE PKEY = ‘A‘ UNION ALL SELECT C.CKEY, P.LVL + 1 FROM HIERARCHY C, PARENT P WHERE P.CKEY = C.PKEY ) SELECT PKEY, CKEY FROM PARENT; A B CD EF G CKEYLVL A0 B1 C1 D1 E2 E2 F2 G3

47 Příklad 3 Seznam potomků hloubky max. 2 WITH PARENT (CKEY, LVL) AS (SELECT DISTINCT PKEY, 0 FROM HIERARCHY WHERE PKEY = ‘A‘ UNION ALL SELECT C.CKEY, P.LVL + 1 FROM HIERARCHY C, PARENT P WHERE P.CKEY = C.PKEY ) SELECT PKEY, CKEY FROM PARENT, WHERE LVL < 3; A B CD EF G CKEYLVL A0 B1 C1 D1 E2 E2 F2

48 Příklad 3 podruhé Seznam potomků hloubky max. 2 WITH PARENT (CKEY, LVL) AS (SELECT DISTINCT PKEY, 0 FROM HIERARCHY WHERE PKEY = ‘A‘ UNION ALL SELECT C.CKEY, P.LVL + 1 FROM HIERARCHY C, PARENT P WHERE P.CKEY = C.PKEY AND P.LVL + 1 < 3 ) SELECT PKEY, CKEY FROM PARENT; A B CD EF G CKEYLVL A0 B1 C1 D1 E2 E2 F2

49 Příklad 4 Výpis cesty, která má délku 4 WITH TEMP1 (CKEY, LVL) AS (SELECT DISTINCT PKEY, 1 FROM HIERARCHY WHERE PKEY = ‘A‘ UNION ALL SELECT C.CKEY, P.LVL + 1 FROM HIERARCHY C, TEMP1 P WHERE P.CKEY = C.PKEY AND P.LVL < 4 ), A B CD EF G TEMP2 (CKEY, LVL) AS (SELECT CKEY, LVL FROM TEMP1, WHERE LVL = 4 UNION ALL SELECT C.PKEY, D.LVL – 1 FROM HIERARCHY C, TEMP2 D WHERE D.CKEY = C.CKEY ) SELECT * FROM TEMP2; CKEYLVL G4 F3 D2 A1

50 Příklad 5 Všichni potomci uzlu A do 4. hladiny PKEYCKEY AB AC AD CE DA DE DF FG HIERARCHY A B CD EF G

51 Příklad 5 - řešení A B CD EF G WITH PARENT (CKEY, LVL) AS (SELECT DISTINCT PKEY, 0 FROM HIERARCHY WHERE PKEY = ‘A‘ UNION ALL SELECT C.CKEY, P.LVL + 1 FROM HIERARCHY C, PARENT P WHERE P.CKEY = C.PKEY AND P.LVL + 1 < 4 ) SELECT PKEY, CKEY FROM PARENT; CKEYLVL A0 B1 C1 D1 E2 A2 E2 F2 B3 C3 D3 G3

52 Příklad 5 – lepší řešení A B CD EF G WITH PARENT (CKEY, LVL) AS (SELECT DISTINCT PKEY, 0 FROM HIERARCHY WHERE PKEY = ‘A‘ UNION ALL SELECT C.CKEY, P.LVL + 1 FROM HIERARCHY C, PARENT P WHERE P.CKEY = C.PKEY AND P.LVL + 1 < 4 ), NO_DUPS (CKEY, LVL, NUM) AS (SELECT CKEY, MIN(LVL), COUNT(*) FROM PARENT GROUP BY CKEY ) SELECT CKEY, LVL, NUM FROM NO_DUPS; CKEYLVLNUM A02 B12 C12 D12 E22 F21 G31

53 Příklad 6 Udržování seznamu navštívených uzlů A B CD EF G CKEYLVLPATH A0A B1A>B C1A>C D1A>D E2A>C>E E2A>D>E F2A>D>F G3A>D>F>G WITH PARENT (CKEY, LVL, PATH) AS (SELECT DISTINCT PKEY, 0, VARCHAR(PKEY, 20) FROM HIERARCHY WHERE PKEY = ‘A‘ UNION ALL SELECT C.CKEY, P.LVL + 1, P.PATH || ‘>‘ || C.CKEY FROM HIERARCHY C, PARENT P WHERE P.CKEY = C.PKEY AND LOCATE(C.CKEY || ‘>‘, P.PATH) = 0 ) SELECT CKEY, LVL, PATH FROM PARENT;

54 Definice tabulky pro př. 6 CREATE TABLE HIERARCHY (PKEY CHAR(3) NOT NULL, CKEY CHAR(3) NOT NULL, CONSTRAINT TBX1 PRIMARY KEY (PKEY, CKEY), CONSTRAINT TBC1 CHECK (PKEY <> CKEY), CONSTRAINT TBC2 CHECK (LOCATE(‘>’, PKEY) = 0), CONSTRAINT TBC3 CHECK (LOCATE(‘>’, CKEY) = 0)); CREATE UNIQUE INDEX TBLE_X1 ON HIERARCHY (CKEY, PKEY);

55 Příklad 7 – Detekce cyklů A B CD EF G WITH PARENT (CKEY, LVL, PATH) AS (SELECT DISTINCT PKEY, VARCHAR(PKEY, 20) FROM HIERARCHY WHERE PKEY = ‘A‘ UNION ALL SELECT CASE WHEN LOCATE(C.CKEY || ‘>‘, P.PATH) > 0 THEN RAISE_ERROR(‘70001‘, ‘CHYBA: Graf obsahuje cyklus‘); ELSE C.CKEY END, P.PATH || ‘>‘ || C.CKEY FROM HIERARCHY C, PARENT P WHERE P.CKEY = C.PKEY ) SELECT CKEY, PATH FROM PARENT;

56 Př. 8 - Tvorba syntetických dat Ve spojení s INSERT CREATE TABLE NUMBERS(COUNTER INTEGER, RANDOM INTEGER) INSERT INTO NUMBERS(COUNTER, RANDOM) WITH TEMP(N) AS (VALUES (1) UNION ALL SELECT N + 1 FROM TEMP WHERE N < 1000 ) SELECT N, INTEGER(RAND() * 1000) FROM TEMP;

57 Rekurze s počítáním Komponenty křídla letadla: Křídlo PříčkaKřidélkoPodvozek Pant Nýt

58 Příklad 9 PARTSUBPARTQTY KřídloPříčka5 KřídloKřidélko1 KřídloPodvozek1 KřídloNýt100 PříčkaNýt10 KřidélkoPant2 KřidélkoNýt5 PodvozekPant3 PodvozekNýt8 PantNýt4 Počet nýtů WITH WINGPARTS(SUBPART, QTY) AS (SELECT SUBPART, QTY FROM COMPONENTS WHERE PART = ‘Křídlo‘ UNION ALL SELECT C.SUBPART, P.QTY * C.QTY FROM WINGPARTS P, COMPONENTS C WHERE P.SUBPART = C.PART ) SELECT SUBPART, SUM(QTY) AS QTY FROM WINGPARTS WHERE SUBPART = ‘Nýt‘; COMPONENTS

59 Příklad 9 - Výsledek SUBPARTQTY Příčka5 Křidélko1 Podvozek1 Nýt100 Nýt50 Pant2 Nýt5 Pant3 Nýt Křídlo PříčkaKřidélkoPodvozek Pant Nýt

60 Použití DB/2 UDF

61 Kde rek. SQL DB/2 nestačí Problém: Nelze psát korelované rekurzivní poddotazy rekurzivních dotazů Příklad korelovaného poddotazu: SELECT POSSIBLE.ENAME, POSSIBLE.DEPT, POSSIBLE.SALARY FROM EMPLOYEE POSSIBLE WHERE SALARY > (SELECT AVG (SALARY) FROM EMPLOYEE AVERAGE WHERE POSSIBLE.DEPT = AVERAGE.DEPT);

62 Příklad 10 Sporty HobbyProf. FotbalBasket Uživatelé Skupina ASkupina B Skupina B1 Franta Karel Dokument 1 Dokument 2 Dokument 3

63 Příklad 10 - dotaz Potřebujeme zjistit, na jaké dokumenty ze stromu sportů má uživatel Karel přístup a ke kterým předmětům náleží. Musíme rozvinout hierarchii sportů pro získání dokumentů a pro každý dokument z hierarchie práv zjistit, zda na něj má Karel právo. Řešení - UDF

64 Příklad 10 – datový model item_id parent_id item_name grp subgrp grpname document_id name Tabulky: Subject, Group, Document, Subject_Document, Group_Document Subject Group Document item_id parent_id document_id grp subgrp document_id Subject_Document Group_Document

65 Příklad 10 - Strategie Rozvineme hierarchii sportů -> získáme dokumenty (vnější rekurzivní dotaz)* Pro každý dokument z hierarchie sportů rozvineme skupiny uživatelů a zjistíme, zda se v nich nachází Karel (UDF s parametry ID dokumentu a uživatel) SELECT za * na každý vrácený dokument zavolá zmíněnou UDF

66 CREATE FUNCTION ISELIGIBLE (Doc Integer, Uname character (64)) RETURNS INTEGER LANGUAGE SQL READS SQL DATA NO EXTERNAL ACTION NOT DETERMINISTIC RETURN WITH Main (subgrp, grpname) AS (SELECT subgrp, grpname FROM Group WHERE (subgrp) IN (SELECT subgrp FROM Group_Document WHERE document_id=Doc ) UNION ALL SELECT C.subgrp, C.grpname FROM Group C, Main M WHERE M.subgrp = C.grp ) SELECT COUNT(*) FROM main WHERE Ucase(grpname)=Ucase(Uname) Příklad 10 - UDF

67 Příklad 10 – Výsledný dotaz WITH Rpl (parent_id, item_id, item_name) AS (SELECT ROOT.parent_id, ROOT.item_id, ROOT.item_name FROM Subject ROOT WHERE ROOT.parent_id = 1 UNION ALL SELECT CHILD.parent_id, CHILD.item_id, CHILD.item_name FROM RPL PARENT, Subject CHILD WHERE PARENT.item_id = CHILD.parent_id ) SELECT DISTINCT Rpl.parent_id, Rpl.item_id, Rpl.item_name, document.document_id, document.name, ISELIGIBLE(document.document_id, char('Karel')) eligible FROM (Rpl JOIN Subject_Document sd ON Rpl.item_id = sd.item_id ) JOIN document ON document.document_id = sd.document_id;


Stáhnout ppt "Rekurzivní SQL Petr Čermák Michal Danihelka. Osnova Úvod do problematiky Rekurzivní SQL v Oraclu Řešení rekurzivních úloh bez podpory rekurzivního SQL."

Podobné prezentace


Reklamy Google