690 likes | 832 Views
Univerzální kvantifikátory v relačních databázích - algoritmy. Jan B URE Š Dung NGUYEN TIEN 2007. Obsah. Úvod terminologie, univerzální kvantifkátory a jejich použití Algoritmy klasifikace dat přehled zhodnocení Množinový pohled Literatura. Terminologie. Univerzální kvantifikátor
E N D
Univerzální kvantifikátory v relačních databázích - algoritmy Jan BUREŠ Dung NGUYEN TIEN 2007
Obsah • Úvod • terminologie, univerzální kvantifkátory a jejich použití • Algoritmy • klasifikace dat • přehled • zhodnocení • Množinový pohled • Literatura
Terminologie • Univerzální kvantifikátor univerzálním kvantifikátorem rozumíme kvantifikátor " používaný v relačních kalkulech univerzální kvantifikátor aplikovaný na proměnnou x formule f nám říká, že daná formule je pravdivá pro všechny hodnoty x zápis: " x : f(x)
Použití • V relačních databázích nachází použití v mnoha moderních aplikacích • analytické operace (OLAP) • data mining • „vnořené“ SELECTy = univerzální kvantifkátor
Implementace • V praxi bývá univerzální kvantifikátor implementován pomocí operátoru dělení (¸) relační algebry
Univerzální příklad • V průběhu referátu budeme používat následující jednoduchý příklad ze života • tabulka předmět • tabulka zápis • Tabulka předmět neobsahuje všechny předměty – je pouze jejich podmnožinou (např. množina předmětů určitého vyučujícího)
Operátor dělení • „Kteří studenti si zapsali všechny předměty?“ • Z tabulek je jasné, že odpovědí na dotaz je Božena • Operátor dělení vezme dvě tabulky – dělence a dělitele a vygeneruje tabulku podíl • Aby se záznam mohl objevit v tabulce podíl musí se v dělenci vyskytovat spárovaný se všemi hodnotami z dělitele
Cíle referátu Tento referát se zaměří především na algoritmy implementace operátoru dělení • přehled těchto algoritmů • jejich srovnání s ohledem na vstupní data • množinový pohled
„Nejlepší“ algoritmus • Každý autor o svém algoritmu pro univerzální kvantifikátory tvrdí, že je nejlepší • Který je skutečně nejlepší? • Žádný! • Každý se hodí pro jiná data • Rychlost algoritmů závisí na stavu vstupních dat
Klasifikace dat • Pro přehlednost a snadné srovnání algoritmů budeme brát v potaz několik různých stavů vstupních dat – „tříd“ • Na základě stavu dělitele a dělence (v našem příkladu tabulek předmět a zápis)
Setřídění X Sdružení • Sdružení (grouping) = GROUP BY • Setřídění = ORDER • Většině algortimů stačí požadavek sdružených dat, nepotřebují data setříděná • Setřídění je speciální případ sdružení (grouping) • Sdružení je tedy silnějším předpokladem
Třídy • Ne všechny kombinace stavů tabulek jsou nám „k něčemu“ • Některé kombinace nemají žádné speciální vlastnosti využitelné některým z námi probíraných algoritmů • Proto uvažujeme pouze 4 třídy dat
Klasifikace dat – třída 0 • Žádné sloupce nejsou sdružené • Neuspořádaná, náhodná data • Nejzákladnější, obsahuje všechny další důležité třídy (2, 5, 10)
Klasifikace dat – třída 2 • Tabulka zápis má data sdružena podle sloupce předmět_id • Obecně – data sdružená podle sloupce hodnot, na jejichž základě se dělí (dělenec) • Neobsahuje žádné další důležité třídy
Klasifikace dat – třída 5 • Tabulka zápis má data sdružena podle sloupce student_id • Obecně – data sdružena podle sloupce, který zbyde po dělení (podíl) • Obsahuje důležitou třídu 10
Klasifikace dat – třída 10 • Tabulka zápis má data sdružena podle sloupce student_id • Poté podle sloupce předmět_id • Ve stejném pořadí je sdružena i tabulka předmět podle předmět_id
Složitost algoritmů • Při analýze složitosti algoritmů je vstupem • Tabulka dělenec(dividend) S(Q, D) • Tabulka dělitel(divisor) T(D) • Q-množina odpovídá naší množině jmen studentů • D-množina odpovídá naší množině předmětů
Univerzální kvantifikátor v SQL • Užití „NOT EXISTS“ SELECT DISTINCT student_id FROM zapis AS z1 WHERE NOT EXISTS ( SELECT * FROM predmet AS p WHERE NOT EXISTS ( SELECT * FROM zapis AS z2 WHERE z2.student_id = z1.student_id AND z2.predmet_id = p.predmet_id)) • Co vyjadřuje select?
Univerzální kvantifikátor v SQL • Předchozí select hledá každého studenta, pro kterého neexistuje žádný předmět, který by nenavštěvoval • Užití kvantifikátoru „FOR ALL“ by bylo intuitivnější, ale není ve standardu
Univerzální kvantifikátor v SQL • Každý dotaz s univerzálním kvatifikátorem lze nahradit dotazem, který používá agregační funkce SELECT student_id FROM zapis GROUP BY student_id HAVING COUNT(DISTINCT predmet_id) = ( SELECT COUNT(DISTINCT predmet_id) FROM predmet)
Skalární algoritmy • Využívají přímou shodu řádků mezi tabulkami • Pro přesnost ponecháme názvy v angličtině • Nested-loops division (NLD) • Merge-sort division (MSD) • Merge-group division (MGD) • Classic hash-division (HD) • Transposed hash-division (HDT) • Hash-division for quotient groups (HDQ) • Transposed hash-division for quotient groups (HDTQ)
Nested-loops division (NLD) • Nejnaivnější algoritmus, ale protože nemá speciální požadavky na data, lze jej vždy použít
Nested-loops division (NLD) • Používá 2 sady datových struktur • Jednu pro uchování hodnot dělitele (předměty), tabulka seen_divisors • Druhou pro hodnoty kandidátů kvocientu, tabulka seen_quotients
Nested-loops division (NLD) • Algoritmus • Naplníme tabulku seen_divisors(předměty) • Pak zkoumáme dělenec • Pro každý řádek zjistíme, jestli je aktuální kvocient již v tabulce seen_quotients(jména studentů) • Pokud ne, přidáme do tabulky a pokračujeme iterativně a najdeme řádky se shodným kvocientem vnějšího cyklu • Pro každý takový řádek zkontrolujeme, jestli je jeho hodnota dělitele již v tabulce seen_divisors • Pokud ano, označíme tuto hodnotu • Pokud na konci jsou všechny jeho dělitelé označeny, vypíšeme aktuální kvocient(jméno studenta)
Nested-loops division (NLD) • Může být velmi neefektivní • Pro každý řádek dělence může projít až všechny řádky dělitele • Typická složitost O(|S|2)
Merge-sort division • Předpoklady • Dělitel T je vytříděný • Dělenec S je seskupený dle Q a každá skupina je uvnitř setříděná dle D jako T
Merge-sort division • Algoritmus • Předpoklad vzestupného třídění • Začneme prvním řádkem dělitele a dělence • Jestli dělitel je roven hodnotě D aktuálního řádku dělence, postoupíme na další řádek v obou tabulkách • Jestli D < dělitel, postoupíme na další řádek skupiny • Jestli už nejsou žádné řádky v kvocient skupině, ale aspoň jedna v děliteli, postoupíme na další skupinu • Jestli už nejsou žádné řádky v děliteli, našli jsme kvocient
Merge-sort division • Složitost O(|S| + |Q||T|)
Merge-group division • Zbobecněním MSD • Oba vstupy seskupeny,nemusí být setříděné
Merge-group division • Lze bezpečně přeskočit kvociet kandidáta, pokud aktuální hodnota Q > aktuální řádek dělitele, pokud je třídění vzestupné • Jelikož se setřídění nevyžaduje, nelze přeskočit na další skupinu při neshodě hodnot, jako při MSD
Merge-group division • Složitost O(|S| + |Q||T|)
Classic hash-division • 2 hašovací tabulky pro dělitele a kvocient • Podílová hašovací tabulka uchovává kandidáta a má bitmapu ke každému kandidátovi s jedním bitem pro každého dělitele
Classic hash-division • Algoritmus • Sestaví hašovací tabulku dělitele • Každý řádek s unikátním číslem index dělitele • Sestaví podílovou hašovací tabulku a doplní příslušné bity • Vypíše všechny podílové kandidáty se samými jedničkami
Classic hash-division • Složitost O(|S| + |T|)
Transposed hash-division • Mírná variace HD
Transposed hash-division • Uchovává bitmapu společně s řádkem v hašovací tabulce dělitele, na rozdíl od HD
Hash-division for quotient groups • Jde o optimalizaci HD • Dělenec je seskupován dle Q, tím tedy nepotřebujeme podílovou hašovací tabulku
Agregační algoritmy • Nepracují přímo s konkrétními hodnotami, místo nich pracují s počty řádek • Spočítají počet řádek náležejících každému „podílovému kandidátu“ (student v našem příkladu) a ten porovnávají s počtem řádek v děliteli (tabulka předmět v našem příkladu)
Agregační algoritmy • Nevýhody agregačních algoritmů: • nedokáží se vypořádat s duplicitními řádky (až na výjimky) • nedokáží se vypořádat s „NULL“ hodnotami • potřebují zajištěnou referenční integritu (v našem příkladě odstraněny řádky s předmět_id= Grafika)
Nested-Loops Counting Division(NLCD) • „Nejnaivnější“ z agregačních algoritmů • Nemá žádné požadavky na předzpracování dat (třída 0) • Stejně jako ostatní agregační algoritmy se nevypořádá s duplicitními řádky, nulovými řádky a potřebuje zajištěnou referenční integritu • Vyžaduje mnoho průchodů tabulkou
NLCD – princip • Používá dva cykly (nested loops) • Na počátku projde dělitele a spočítá řádky • Vnější cyklus postupně bere dělence řádek po řádku – pokud najde takový řádek, jehož podílového kandidáta ještě nemá ve své tabulce projitých kandidátů, přidá ho tam, nastaví počitadlo na 1 a vstoupí do vnitřního cyklu • Vnitřní cyklus projde celá data a najde všechny řádky s podílovým kandidátem, který se zrovna ověřuje – pokaždé zvedne počitadlo o 1. Pokud se počitadlo rovná počtu řádků dělitele, přidá podílového kandidáta do výstupu, poté se vrátí do vnějšího cyklu • Počet řádků se drží v globální proměnné
NLCD - příklad s1 = s2 = zapis while predmet.next() do dpocet++; while s1.next() do if s1.student_id not in podilhashtable then begin while s2.next() do if s1.student_id == s2.student_id then begin spocet++; if spocet == dpocet begin output s1.student_id; break; end; end; spocet = 1; insert s1.student_id into podilhashtable; end;
NLCD – shrnutí • Nemá žádné požadavky na data, je proto univerzální • Časová složitost je v nejhorším případe O(|S|2 + |T|), typicky pak O(|S|2), tedy stejná, jako u NLD • Paměťová náročnost je menší než u NLD – je potřeba si držet pouze projité podílové kandidáty, tedy O(|Q|), což je v nejhorším případě O(|S|)
Merge-Count Division(MCD) • Je mnohem rychlejší než NLCD, ale za cenu omezení jen na určitá data • Potřebuje data sdružená podle podílových kandidátů (třída 5) • Stačí mu jeden průchod tabulkou
MCD - princip • Na počátku opět projde tabulku dělitele a spočítá její řádky • Prochází postupně tabulku dělence – vždy, když narazí na nového podílového kandidáta, nastaví počitadlo na 1, při vstupu na každý další řádek jej pak o 1 zvýší – pokud se počitadlo rovná počtu řádku v děliteli, podílového kandidáta dá do výsledku
MCD - příklad dpocet = 0 while not predmet.isEmpty() do begin dpocet++; předmět.next(); end; if not zapis.isEmpty() then begin zapis.next(); podilovy_kandidat = zapis.student_id; end; while not zapis.isEmpty() do begin spocet = 0; while not zapis.isEmpty() and zapis.student_id == podilovy_kandidat do begin spocet++; zapis.next(); end; if spocet == dpocet then output podilovy_kandidat; if not zapis.isEmpty() podilovy_kandidat = zapis.student_id; end;
MCD - shrnutí • Časová složitost je v nejhorším případě O(|S| + |T|) – jeden průchod každou z tabulek • V průměrném případě pak O(|S|) • Náročnost na paměť je velmi nízká, potřebujeme pouze dvě proměnné na počítání řádků • Paměťová složitost je tedy O(1)