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 BYmệ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ứaHAVINGmệnh đề, ngay cả khi không có bất kỳ lệnh gọi tổng hợp nào hoặcGROUP BYmệ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ó
categoryidnhỏ 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
LIMIThoặcORDER BYmệnh đề cho một nhánh riêng lẻ củaUNIONtruy vấn. -
Bạn chỉ cần tham gia vào bảng
categorycho 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ủaUNIONtruy 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.categoryidtớicategory.idvà cảcontents.categoryidvà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