sql >> Database teknologi >  >> RDS >> PostgreSQL

Flere array_agg()-kald i en enkelt forespørgsel

DISTINCT bruges ofte til reparationsforespørgsler, der er rådne indefra, og det er ofte langsomt og/eller forkert. Lad være med at gange rækker til at begynde med, så behøver du ikke sortere uønskede dubletter fra til sidst.

Sammenføjning til flere n-tabeller ("har mange") på én gang multiplicerer rækker i resultatsættet. Det er ligesom en CROSS JOIN eller kartesisk produkt ved proxy :

  • To SQL LEFT JOINS giver et forkert resultat

Der er forskellige måder at undgå denne fejl på.

Samle først, tilmeld dig senere

Teknisk set virker forespørgslen, så længe du tilslutter dig one tabel med flere rækker ad gangen, før du samler:

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 er også bedst at inkludere den primære nøgle id og GROUP BY det, fordi name og age er ikke nødvendigvis unikke. Du kan ved en fejl slå to medarbejdere sammen.

Men du kan samle i en underforespørgsel før du tilmelder dig, det er overlegent, medmindre du har selektiv WHERE betingelser for 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 samle begge dele:

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 sidste er typisk hurtigere, hvis du henter alle eller de fleste af rækkerne i basistabellerne.

Bemærk, at du bruger JOIN og ikke LEFT JOIN fjerner medarbejdere fra resultatet, som ikke har nogen adresse eller ingen arbejdsdage. Det er måske ikke meningen. Skift til LEFT JOIN at beholde alle medarbejdere i resultatet.

Korrelerede underforespørgsler / LATERAL join

For et lille udvalg , ville jeg overveje korrelerede underforespørgsler i stedet for:

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 nyere kan du bruge LATERAL slutter sig til 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
  • Hvad er forskellen mellem LATERAL og en underforespørgsel i PostgreSQL?

Begge forespørgsler bevarer alle medarbejdere i resultatet.



  1. Tips og tricks til at navigere i PostgreSQL-fællesskabet

  2. SQL GROUP BY- 3 nemme tips til at gruppere resultater som en professionel

  3. 2 måder at få referatet fra en DateTime-værdi i Oracle Database

  4. Amazon EC2, mysql afbryder start, fordi InnoDB:mmap (x bytes) mislykkedes; fejl nr 12