DISTINCT
se často používá k opravě dotazů, které jsou shnilé zevnitř, a to je často pomalé a/nebo nesprávné. Na začátku řádky nenásobte, nemusíte pak na konci třídit nežádoucí duplikáty.
Spojení s více n-tabulkami („má mnoho“) najednou násobí řádky v sadě výsledků. Je to jako CROSS JOIN
nebo kartézský produkt prostřednictvím proxy :
- Dvě SQL LEFT JOINS vedou k nesprávnému výsledku
Této chybě se lze vyhnout různými způsoby.
Nejprve agregujte, připojte se později
Technicky dotaz funguje, pokud se připojíte k jednomu tabulka s více řádky najednou, než agregujete:
SELECT e.id, e.name, e.age, e.streets, arrag_agg(wd.day) AS days
FROM (
SELECT e.id, e.name, e.age, array_agg(ad.street) AS streets
FROM employees e
JOIN address ad ON ad.employeeid = e.id
GROUP BY e.id -- id enough if it is defined PK
) e
JOIN workingdays wd ON wd.employeeid = e.id
GROUP BY e.id, e.name, e.age;
Nejlepší je také zahrnout primární klíč id
a GROUP BY
to, protože name
a age
nejsou nutně jedinečné. Mohli byste omylem sloučit dva zaměstnance.
Ale můžete agregovat v dílčím dotazu před když se připojíte, je to lepší, pokud nemáte selektivní WHERE
podmínky na employees
:
SELECT e.id, e.name, e.age, ad.streets, arrag_agg(wd.day) AS days
FROM employees e
JOIN (
SELECT employeeid, array_agg(ad.street) AS streets
FROM address
GROUP BY 1
) ad ON ad.employeeid = e.id
JOIN workingdays wd ON e.id = wd.employeeid
GROUP BY e.id, e.name, e.age, ad.streets;
Nebo agregujte obojí:
SELECT name, age, ad.streets, wd.days
FROM employees e
JOIN (
SELECT employeeid, array_agg(ad.street) AS streets
FROM address
GROUP BY 1
) ad ON ad.employeeid = e.id
JOIN (
SELECT employeeid, arrag_agg(wd.day) AS days
FROM workingdays
GROUP BY 1
) wd ON wd.employeeid = e.id;
Poslední z nich je obvykle rychlejší, pokud načtete všechny nebo většinu řádků v základních tabulkách.
Všimněte si, že pomocí JOIN
a ne LEFT JOIN
odstraní z výsledku zaměstnance, kteří nemají žádnou adresu nebo žádné pracovní dny. To může a nemusí být zamýšleno. Přepněte na LEFT JOIN
zachovat vše zaměstnanci ve výsledku.
Korelované poddotazy / LATERAL spojení
Pro malý výběr , místo toho bych zvážil korelované poddotazy:
SELECT name, age
, (SELECT array_agg(street) FROM address WHERE employeeid = e.id) AS streets
, (SELECT arrag_agg(day) FROM workingdays WHERE employeeid = e.id) AS days
FROM employees e
WHERE e.namer = 'peter'; -- very selective
Nebo s Postgres 9.3 nebo novějším můžete použít LATERAL
za to se připojuje:
SELECT e.name, e.age, a.streets, w.days
FROM employees e
LEFT JOIN LATERAL (
SELECT array_agg(street) AS streets
FROM address
WHERE employeeid = e.id
GROUP BY 1
) a ON true
LEFT JOIN LATERAL (
SELECT array_agg(day) AS days
FROM workingdays
WHERE employeeid = e.id
GROUP BY 1
) w ON true
WHERE e.name = 'peter'; -- very selective
- Jaký je rozdíl mezi LATERAL a dílčím dotazem v PostgreSQL?
Oba dotazy zachovají vše zaměstnanci ve výsledku.