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?
Всяка заявка запазва всички служители в резултата.