Khó khăn cụ thể tại đây:Các truy vấn có một hoặc nhiều hàm tổng hợp trong SELECT
danh sách và không có GROUP BY
mệnh đề tạo ra chính xác một hàng, ngay cả khi không tìm thấy hàng nào trong bảng bên dưới.
Bạn không thể làm gì trong WHERE
mệnh đề ngăn chặn hàng đó. Bạn phải loại trừ một hàng như vậy sau thực tế , tức là trong HAVING
hoặc trong một truy vấn bên ngoài.
Theo tài liệu:
Nếu một truy vấn chứa các lệnh gọi hàm tổng hợp, nhưng không có
GROUP BY
mệnh đề, nhóm vẫn xảy ra:kết quả là một hàng nhóm duy nhất (hoặc có thể không có gì cả, nếu hàng đơn thì bị loại bỏ bởiHAVING
). Điều này cũng đúng nếu nó chứaHAVING
mệnh đề, ngay cả khi không có bất kỳ lệnh gọi tổng hợp nào hoặcGROUP BY
mệnh đề.
Cần lưu ý rằng việc thêm GROUP BY
mệnh đề chỉ có một biểu thức hằng số (nói cách khác là hoàn toàn vô nghĩa!) cũng hoạt động. Xem ví dụ bên dưới. Nhưng tôi không muốn sử dụng thủ thuật đó, ngay cả khi nó ngắn, rẻ và đơn giản, bởi vì nó hầu như không rõ ràng là gì.
Truy vấn sau chỉ cần một lần quét bảng đơn lẻ và trả về 7 danh mục hàng đầu được sắp xếp theo số lượng. Nếu ( và chỉ khi ) có nhiều danh mục hơn, phần còn lại được tóm tắt thành 'Khác':
WITH cte AS (
SELECT categoryid, count(*) AS data
, row_number() OVER (ORDER BY count(*) DESC, categoryid) AS rn
FROM contents
GROUP BY 1
)
( -- parentheses required again
SELECT categoryid, COALESCE(ca.name, 'Unknown') AS label, data
FROM cte
LEFT JOIN category ca ON ca.id = cte.categoryid
WHERE rn <= 7
ORDER BY rn
)
UNION ALL
SELECT NULL, 'Others', sum(data)
FROM cte
WHERE rn > 7 -- only take the rest
HAVING count(*) > 0; -- only if there actually is a rest
-- or: HAVING sum(data) > 0
-
Bạn cần phá vỡ mối quan hệ nếu nhiều danh mục có thể có cùng số lượng trên xếp hạng 7/8. Trong ví dụ của tôi, các danh mục có
categoryid
nhỏ hơn giành chiến thắng trong một cuộc đua như vậy. -
Dấu ngoặc đơn được yêu cầu bao gồm
LIMIT
hoặcORDER BY
mệnh đề cho một nhánh riêng lẻ củaUNION
truy vấn. -
Bạn chỉ cần tham gia vào bảng
category
cho 7 danh mục hàng đầu. Và thường rẻ hơn nếu tổng hợp trước và tham gia sau trong kịch bản này. Vì vậy, không tham gia truy vấn cơ sở trong CTE (biểu thức bảng chung) có têncte
, chỉ tham gia vàoSELECT
đầu tiên củaUNION
truy vấn, rẻ hơn. -
Không chắc tại sao bạn cần
COALESCE
. Nếu bạn có khóa ngoại tại chỗ từcontents.categoryid
tớicategory.id
và cảcontents.categoryid
vàcategory.name
được định nghĩaNOT NULL
(giống như họ có thể nên như vậy), sau đó bạn không cần nó.
GROUP BY true
Điều này cũng sẽ hoạt động:
...
UNION ALL
SELECT NULL , 'Others', sum(data)
FROM cte
WHERE rn > 7
GROUP BY true;
Và tôi thậm chí còn nhận được các kế hoạch truy vấn nhanh hơn một chút. Nhưng đó là một vụ hack khá kỳ quặc ...
SQL Fiddle thể hiện tất cả.
Câu trả lời liên quan với giải thích thêm cho UNION ALL
/ LIMIT
kỹ thuật:
- Tính tổng kết quả của một vài truy vấn và sau đó tìm 5 truy vấn hàng đầu trong SQL