sql >> Databáze >  >> RDS >> PostgreSQL

Více volání array_agg() v jednom dotazu

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.



  1. Návrh datového modelu pro systém rezervace hotelových pokojů

  2. Kde získám zdroj libpq?

  3. Uložit pole PHP do MySQL?

  4. Selhání addnode resolv.conf