PostgreSQL
 sql >> Baza danych >  >> RDS >> PostgreSQL

Wiele wywołań array_agg() w jednym zapytaniu

DISTINCT jest często stosowany do naprawy zapytań, które są zepsute od środka, co często jest powolne i/lub nieprawidłowe. Nie mnóż wierszy na początku, wtedy nie musisz sortować niechcianych duplikatów na końcu.

Łączenie z wieloma n-tabelami ("ma wiele") jednocześnie mnoży wiersze w zestawie wyników. To jak CROSS JOIN lub produkt kartezjański przez proxy :

  • Dwa LEFT JOINS SQL dają niepoprawny wynik

Istnieje wiele sposobów uniknięcia tego błędu.

Najpierw agreguj, dołącz później

Z technicznego punktu widzenia zapytanie działa tak długo, jak dołączysz do jednego tabela z wieloma wierszami naraz przed agregacją:

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;

Najlepiej też podać klucz podstawowy id i GROUP BY to, ponieważ name i age niekoniecznie są wyjątkowe. Możesz przez pomyłkę połączyć dwóch pracowników.

Możesz jednak dokonać agregacji w podzapytaniu przed dołączasz, to jest lepsze, chyba że masz selektywne WHERE warunki dotyczące 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;

Lub agreguj oba:

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;

Ostatni z nich jest zazwyczaj szybszy, jeśli odzyskasz wszystkie lub większość wierszy w tabelach podstawowych.

Zauważ, że używając JOIN a nie LEFT JOIN usuwa z wyniku pracowników, którzy nie mają adresu lub brak dni roboczych. To może być zamierzone lub nie. Przełącz na LEFT JOIN zachować wszystkie pracowników w wyniku.

Skorelowane podzapytania / łączenie BOCZNE

Dla małego wyboru , zamiast tego rozważyłbym skorelowane podzapytania:

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

Lub, w Postgresie 9.3 lub nowszym, możesz użyć LATERAL dołącza do tego:

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
  • Jaka jest różnica między LATERAL a podzapytanie w PostgreSQL?

Każde zapytanie zachowuje wszystkie pracowników w wyniku.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Znajdź n najbliższych sąsiadów dla danego punktu za pomocą PostGIS?

  2. Pobieranie nazwy bieżącej funkcji wewnątrz funkcji za pomocą plpgsql

  3. Dynamicznie generuj kolumny dla tabeli krzyżowej w PostgreSQL

  4. KOPIUJ z dynamiczną nazwą pliku

  5. Dlaczego nie mogę używać aliasów kolumn w następnym wyrażeniu SELECT?