DISTINCT ON
thường đơn giản nhất và nhanh nhất cho việc này trong PostgreSQL .
(Để tối ưu hóa hiệu suất cho một số khối lượng công việc nhất định, hãy xem bên dưới.)
SELECT DISTINCT ON (customer)
id, customer, total
FROM purchases
ORDER BY customer, total DESC, id;
Hoặc ngắn hơn (nếu không rõ ràng) với số thứ tự của cột đầu ra:
SELECT DISTINCT ON (2)
id, customer, total
FROM purchases
ORDER BY 2, 3 DESC, 1;
Nếu total
có thể là NULL (không ảnh hưởng gì cả, nhưng bạn sẽ muốn khớp với các chỉ mục hiện có):
...
ORDER BY customer, total DESC NULLS LAST, id;
Những điểm chính
DISTINCT ON
là một phần mở rộng PostgreSQL của tiêu chuẩn (trong đó chỉ có DISTINCT
trên toàn bộ SELECT
danh sách được xác định).
Liệt kê bất kỳ số lượng biểu thức nào trong DISTINCT ON
mệnh đề, giá trị hàng kết hợp xác định các bản sao. Hướng dẫn sử dụng:
Rõ ràng, hai hàng được coi là khác biệt nếu chúng khác nhau ở ít nhất một giá trị cột. Các giá trị rỗng được coi là bằng nhau trong so sánh này.
Nhấn mạnh đậm của tôi.
DISTINCT ON
có thể được kết hợp với ORDER BY
. Biểu thức hàng đầu trong ORDER BY
phải nằm trong tập hợp các biểu thức trong DISTINCT ON
, nhưng bạn có thể sắp xếp lại thứ tự giữa những thứ đó một cách tự do. Ví dụ.
Bạn có thể thêm bổ sung biểu thức thành ORDER BY
để chọn một hàng cụ thể từ mỗi nhóm đồng nghiệp. Hoặc, như hướng dẫn sử dụng:
DISTINCT ON
(các) biểu thức phải khớp vớiORDER BY
ngoài cùng bên trái biểu thức).ORDER BY
mệnh đề thường sẽ chứa (các) biểu thức cộng xác định mức độ ưu tiên mong muốn của các hàng vớiDISTINCT ON
nhóm.
Tôi đã thêm id
là mục cuối cùng để phá vỡ mối quan hệ:
"Chọn hàng có id
nhỏ nhất từ mỗi nhóm chia sẻ total
cao nhất . "
Để sắp xếp các kết quả theo cách không đồng ý với thứ tự sắp xếp xác định đầu tiên cho mỗi nhóm, bạn có thể lồng truy vấn phía trên trong một truy vấn bên ngoài với một ORDER BY
khác . Ví dụ.
Nếu total
có thể là NULL, bạn có lẽ nhất muốn hàng có giá trị khác rỗng lớn nhất. Thêm NULLS LAST
như đã chứng minh. Xem:
- Sắp xếp theo ASC cột, nhưng giá trị NULL trước?
SELECT
danh sách không bị ràng buộc bởi các biểu thức trong DISTINCT ON
hoặc ORDER BY
theo bất kỳ cách nào. (Không cần thiết trong trường hợp đơn giản ở trên):
-
Bạn không cần phải bao gồm bất kỳ biểu thức nào trong
DISTINCT ON
hoặcORDER BY
. -
Bạn có thể bao gồm bất kỳ biểu thức nào khác trong
SELECT
danh sách. Đây là công cụ để thay thế các truy vấn phức tạp hơn nhiều bằng các truy vấn con và các hàm tổng hợp / cửa sổ.
Tôi đã thử nghiệm với Postgres phiên bản 8.3 - 13. Nhưng tính năng này đã có ít nhất kể từ phiên bản 7.1, vì vậy về cơ bản luôn luôn.
Chỉ mục
hoàn hảo chỉ mục cho truy vấn trên sẽ là một chỉ mục nhiều cột bao trùm cả ba cột theo trình tự phù hợp và với thứ tự sắp xếp phù hợp:
CREATE INDEX purchases_3c_idx ON purchases (customer, total DESC, id);
Có thể quá chuyên biệt. Nhưng hãy sử dụng nó nếu hiệu suất đọc cho truy vấn cụ thể là rất quan trọng. Nếu bạn có DESC NULLS LAST
trong truy vấn, hãy sử dụng chỉ mục tương tự trong chỉ mục để thứ tự sắp xếp phù hợp và chỉ mục có thể áp dụng được.
Hiệu quả / Tối ưu hóa hiệu suất
Cân nhắc chi phí và lợi ích trước khi tạo các chỉ mục phù hợp cho từng truy vấn. Tiềm năng của chỉ mục trên phần lớn phụ thuộc vào phân phối dữ liệu .
Chỉ mục được sử dụng vì nó cung cấp dữ liệu được sắp xếp trước. Trong Postgres 9.2 trở lên, truy vấn cũng có thể được hưởng lợi từ chỉ quét chỉ mục nếu chỉ mục nhỏ hơn bảng bên dưới. Tuy nhiên, chỉ mục phải được quét toàn bộ.
Đối với vài hàng cho mỗi khách hàng (số lượng cao trong cột customer
), điều này rất hiệu quả. Thậm chí nhiều hơn như vậy nếu bạn vẫn cần đầu ra được sắp xếp. Lợi ích giảm dần khi số lượng hàng trên mỗi khách hàng ngày càng tăng.
Tốt nhất, bạn có đủ work_mem
để xử lý bước sắp xếp liên quan trong RAM và không tràn ra đĩa. Nhưng nói chung là đặt work_mem
quá cao có thể có tác dụng phụ. Xem xét SET LOCAL
cho các truy vấn đặc biệt lớn. Tìm số lượng bạn cần với EXPLAIN ANALYZE
. Đề cập đến " Đĩa: "trong bước sắp xếp cho thấy cần thêm:
- Tham số cấu hình work_mem trong PostgreSQL trên Linux
- Tối ưu hóa truy vấn đơn giản bằng cách sử dụng ĐẶT HÀNG THEO ngày tháng và văn bản
Đối với nhiều hàng cho mỗi khách hàng (số lượng thấp trong cột customer
), quét chỉ mục lỏng lẻo (còn gọi là "bỏ qua quét") sẽ hiệu quả hơn (nhiều), nhưng điều đó không được triển khai cho đến Postgres 14. (Một triển khai dành cho quét chỉ chỉ mục đang được phát triển cho Postgres 15. Xem tại đây và tại đây.)
Đối với bây giờ, có kỹ thuật truy vấn nhanh hơn để thay thế cho điều này. Đặc biệt nếu bạn có một bàn riêng chứa những khách hàng duy nhất, đó là trường hợp sử dụng điển hình. Nhưng cũng có thể nếu bạn không:
- SELECT DISTINCT chậm hơn mong đợi trên bảng của tôi trong PostgreSQL
- Tối ưu hóa truy vấn GROUP BY để truy xuất hàng mới nhất cho mỗi người dùng
- Tối ưu hóa truy vấn tối đa theo nhóm
- Truy vấn N hàng liên quan cuối cùng trên mỗi hàng
Điểm chuẩn
Xem câu trả lời riêng.