DISTINCT
thường được áp dụng để sửa chữa các truy vấn bị lỗi từ bên trong và điều đó thường chậm và / hoặc không chính xác. Không nhân các hàng ở đầu, sau đó bạn không phải sắp xếp các hàng trùng lặp không mong muốn ở cuối.
Việc tham gia vào nhiều bảng n ("có nhiều") cùng một lúc sẽ nhân các hàng trong tập kết quả. Nó giống như một CROSS JOIN
hoặc sản phẩm Descartes theo proxy :
- Hai SQL LEFT JOINS tạo ra kết quả không chính xác
Có nhiều cách khác nhau để tránh sai lầm này.
Tổng hợp trước, tham gia sau
Về mặt kỹ thuật, truy vấn hoạt động miễn là bạn tham gia vào một bảng có nhiều hàng cùng một lúc trước khi bạn tổng hợp:
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;
Tốt nhất bạn nên bao gồm khóa chính id
và GROUP BY
nó, bởi vì name
và age
không nhất thiết phải là duy nhất. Bạn có thể hợp nhất hai nhân viên do nhầm lẫn.
Nhưng bạn có thể tổng hợp trong một truy vấn con trước bạn tham gia, điều đó tốt hơn trừ khi bạn có WHERE
có chọn lọc điều kiện về 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;
Hoặc tổng hợp cả hai:
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;
Cái cuối cùng thường nhanh hơn nếu bạn truy xuất tất cả hoặc hầu hết của các hàng trong bảng cơ sở.
Lưu ý rằng sử dụng JOIN
chứ không phải LEFT JOIN
xóa nhân viên khỏi kết quả không có địa chỉ hoặc không có ngày làm việc. Điều đó có thể được dự định hoặc không. Chuyển sang LEFT JOIN
để giữ lại tất cả kết quả là nhân viên.
Truy vấn con có liên quan / tham gia LATERAL
Để có một lựa chọn nhỏ , Thay vào đó, tôi sẽ xem xét các truy vấn con tương quan:
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
Hoặc, với Postgres 9.3 trở lên, bạn có thể sử dụng LATERAL
tham gia vì điều đó:
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
- Sự khác biệt giữa LATERAL và một truy vấn con trong PostgreSQL là gì?
Truy vấn giữ lại tất cả kết quả là nhân viên.