sql >> Databasteknik >  >> RDS >> PostgreSQL

Flera array_agg()-anrop i en enda fråga

DISTINCT används ofta för att reparera frågor som är ruttna från insidan, och det är ofta långsamt och/eller felaktigt. Multiplicera inte rader till att börja med, då behöver du inte sortera ut oönskade dubbletter i slutet.

Att gå med i flera n-tabeller ("har många") samtidigt multiplicerar rader i resultatuppsättningen. Det är som en CROSS JOIN eller kartesisk produkt via proxy :

  • Två SQL LEFT JOINS ger felaktigt resultat

Det finns olika sätt att undvika detta misstag.

Aggregera först, gå med senare

Tekniskt sett fungerar frågan så länge du går med i one tabell med flera rader åt gången innan du aggregerar:

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;

Det är också bäst att inkludera primärnyckeln id och GROUP BY det, eftersom name och age är inte nödvändigtvis unika. Du kan slå samman två anställda av misstag.

Men du kan samla i en underfråga före du går med, det är överlägset om du inte har selektiv WHERE villkor för 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;

Eller slå ihop båda:

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;

Den sista är vanligtvis snabbare om du hämtar alla eller de flesta av raderna i bastabellerna.

Observera att du använder JOIN och inte LEFT JOIN tar bort anställda från resultatet som inte har någon adress eller inga arbetsdagar. Det kanske är meningen eller inte. Byt till LEFT JOIN för att behålla alla anställda i resultatet.

Korrelerade underfrågor / LATERAL join

För ett litet urval , skulle jag överväga korrelerade underfrågor istället:

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

Eller, med Postgres 9.3 eller senare, kan du använda LATERAL går med för det:

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
  • Vad är skillnaden mellan LATERAL och en underfråga i PostgreSQL?

Båda sökfrågorna behåller alla anställda i resultatet.



  1. Ställ in språket som används för datum- och tidsfunktioner i MariaDB

  2. Hur man tar bort virtuell maskin från VirtualBox

  3. Postgresql 9.2 pg_dump versionen matchar inte

  4. Hur arbetar man med PGpoint för geolokalisering med PostgreSQL?