PostgreSQL
 sql >> Cơ Sở Dữ Liệu >  >> RDS >> PostgreSQL

Nhận n danh mục được nhóm lại và tổng hợp các danh mục khác thành một

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ởi HAVING ). Điều này cũng đúng nếu nó chứa HAVING mệnh đề, ngay cả khi không có bất kỳ lệnh gọi tổng hợp nào hoặc GROUP 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ặc ORDER BY mệnh đề cho một nhánh riêng lẻ của UNION 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ên cte , chỉ tham gia vào SELECT đầu tiên của UNION 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ới category.id và cả contents.categoryidcategory.name được định nghĩa NOT 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


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. PostgreSQL SHOW TABLES Tương đương (psql) 

  2. JPA tuôn ra so với cam kết

  3. SQLAlchemy create_all () không tạo bảng

  4. Cách Trunc () hoạt động trong PostgreSQL

  5. pg gem '0.14.0' trên Mountain Lion không thành công