PostgreSQL
 sql >> база данни >  >> RDS >> PostgreSQL

Множество извиквания на array_agg() в една заявка

DISTINCT често се прилага за поправяне на заявки, които са гнили отвътре и това често е бавно и/или неправилно. Не умножавайте редовете като начало, след което не е нужно да сортирате нежеланите дубликати в края.

Присъединяването към множество n-таблици („има много“) наведнъж умножава редовете в набора от резултати. Това е като CROSS JOIN или декартов продукт чрез пълномощник :

  • Две SQL LEFT JOINS дават неправилен резултат

Има различни начини да избегнете тази грешка.

Първо обобщете, присъединете се по-късно

Технически, заявката работи, стига да се присъедините към едно таблица с няколко реда наведнъж, преди да агрегирате:

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;

Също така е най-добре да включите първичния ключ id и GROUP BY това, защото name и age не са непременно уникални. Можете да обедините двама служители по погрешка.

Но можете да агрегирате в подзаявка преди ако се присъедините, това е по-добро, освен ако нямате селективен WHERE условия за 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;

Или агрегирайте и двете:

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;

Последният обикновено е по-бърз, ако извлечете всички или повечето от редовете в основните таблици.

Имайте предвид, че използвате JOIN а не LEFT JOIN премахва служители от резултата, които нямат адрес или без работни дни. Това може или не е предназначено. Превключете към LEFT JOIN за задържаневсички служители в резултата.

Корелирани подзаявки / ЛАТЕРАЛНО присъединяване

За малък избор , вместо това бих разгледал корелирани подзаявки:

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

Или с Postgres 9.3 или по-нова версия, можете да използвате LATERAL се присъединява за това:

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
  • Каква е разликата между LATERAL и подзаявка в PostgreSQL?

Всяка заявка запазва всички служители в резултата.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. psycopg2 всъщност не вмъква данни

  2. Как използвате променливи в прост PostgreSQL скрипт?

  3. Напълно управляван PostgreSQL хостинг на AWS и Azure стартира навреме за наследени миграции

  4. Променете кодирането на базата данни PostgreSql

  5. Как мога да вмъкна JSON обект в Postgres с помощта на Java readyStatement?