< Agregace pokračování | SQL postupně | Dotazy na to co není >
SQL kód nějakého dotazu můžeme uzavřít do závorek a pak zakomponovat do dalšího dotazu. Ten vnitřní, zakomponovaný, dotaz je pak poddotazem vnějšího dotazu. Takový kód se interpretuje tak, že výsledek poddotazu stojí na místě, kde je poddotaz ve vnějším dotazu. Často použitím poddotazu ztížíme nebo narušíme práci optimalizátoru, někdy však poddotaz skutečně potřebujeme.
Většinou se jedná o dosti složité úlohy:
Příklad: Pro každého zákazníka vypište datum a částku jeho poslední objednávky.
select LOG, DAT, sum(VCEN) as CASTKA
from ZAK left join OBJ o1 on (ZAK.LOG=o1.ZAK) left join POLOZ using (CISO)
where DAT=(select max(DAT) from OBJ o2 where o2.ZAK=ZAK.LOG)
or DAT is null
group by LOG, DAT
order by 1; |
Poddotaz select max(DAT) from OBJ o2 where o2.ZAK=ZAK.LOG najde poslední datum objednávy konkrétního zákazníka. Je to tzv. korelovaný poddotaz, protože ZAK.LOG odkazuje do vnějšího dotazu. Následující formulace však může být lépe optimalizována: |
select LOG, MAXDAT, sum(VCEN) as CASTKA
from (select ZAK,max(DAT) as MAXDAT from OBJ group by ZAK ) MAXY right join ZAK on (MAXY.ZAK=ZAK.LOG) left join OBJ on
(ZAK.LOG=OBJ.ZAK and MAXY.MAXDAT=OBJ.DAT) left join POLOZ using (CISO)
group by LOG, MAXDAT
order by 1; |
Poddotaz select ZAK,max(DAT) as MAXDAT from OBJ group by ZAK vypočítává poslední datum objednávky pro každého zákazníka. (Protože se potřebujeme na výsledek tohoto poddotazu odkazovat v podmínce propojení, musíme ho nějak pojmenovat – jako v tomto případě MAXY .) Zde již nemáme korelovaný poddotaz, takže nenutíme databázový engine provádět operace v určitém pořadí, ale volbu necháme na něm. |
Příklad: Pro každé zboží vypočítejme jeho podíl na celkové tržbě v jeho kategorii.
select KAT, KOD, NAZ, 100*sum(VCEN)/KAT_TRZB as PROCENTNI_PODIL
from ZBOZ left join POLOZ using(KOD) left join
(select KAT,sum(VCEN) as KAT_TRZB from ZBOZ left join POLOZ using(KOD) group by KAT) using (KAT)
group by KAT,KOD,NAZ,KAT_TRZB
order by 1,4 DESC; |
Poddotaz select KAT,sum(VCEN) as KAT_TRZB from ZBOZ left join POLOZ using(KOD) group by KAT nejprve pro každou kategorii, ve které máme nějaké zboží, vypočítá celkovou tržbu. (Podle synatxe Oracle jsou všechna pole v GROUP BY klauzuli takto nutná.) |
Příklad: Pro každého zákazníka porovnejte jeho nákup za roky 2008 a 2009.
select LOG, R2008, R2009, 100*R2008/R2009 as PROCENTNI_PODIL
from
(select ZAK, sum(VCEN) as R2008
from OBJ join POLOZ using (CISO) where extract(year from DAT)=2008 group by ZAK) R08
right join ZAK on (R08.ZAK=ZAK.LOG)
left join
(select ZAK, sum(VCEN) as R2009
from OBJ join POLOZ using (CISO) where extract(year from DAT)=2009 group by ZAK) R09 on (R09.ZAK=ZAK.LOG); |
Když potřebujeme porovnat dvě agregace počítané podle různých podmínek, je univerzálním řešením propojení dvou poddotazů. |
| Běžné úlohy lze většinou řešit bez poddotazů. V některých případech se může zdát formulace s poddotazem příjemnější, ne vždy je však vhodná: |
Příklad: Vypište názvy zboží z kategorie, v jejímž popisu se vyskytuje "minerální".
select NAZ
from ZBOZ
where KAT=(select KAT from KAT where POPK like '%minerální%'); |
Tento kód je sice ekvivalentní s |
select NAZ
from ZBOZ join KAT using (KAT)
where POPK like '%minerální%'; |
ale mnohému se může zdát první verze přirozenější. |
V předchozím příkladu se využilo, že příslušná kategorie je jen jedna. Pozor však na následující
Příklad: Vypište e-maily zákazníků, kteří si nechávají dovézt objednávky rozvozem (DOPR=2) .
select distinct EMAIL
from ZAK
where LOG in (select ZAK from OBJ where DOPR=2); |
Toto není nejlepší formulace, v závislsti na použitém optimalizátoru (v systému jiném než Oracle) může být značně pomalejší než: |
select distinct EMAIL
from ZAK join OBJ on (LOG=ZAK)
where DOPR=2; |
Zvláštní kapitolu věnujeme dotazům, ve kterých hledáme objekty, o kterých v databázi není žádný záznam určitých vlastností. O tom viz následující>.
< Agregace pokračování | SQL postupně | Dotazy na to co není >