Đây là vấn đề lớn nhất-n-per-group và là một câu hỏi SQL rất phổ biến.
Đây là cách tôi giải quyết nó với các phép nối bên ngoài:
SELECT i1.*
FROM item i1
LEFT OUTER JOIN item i2
ON (i1.category_id = i2.category_id AND i1.item_id < i2.item_id)
GROUP BY i1.item_id
HAVING COUNT(*) < 4
ORDER BY category_id, date_listed;
Tôi giả sử là khóa chính của item
bảng là item_id
và đó là một sự giả tạo đơn điệu ngày càng tăng. Nghĩa là, một giá trị lớn hơn trong item_id
tương ứng với một hàng mới hơn trong item
.
Đây là cách hoạt động:đối với mỗi mục, có một số mục khác mới hơn. Ví dụ:có ba mục mới hơn mục mới nhất thứ tư. Không có mặt hàng nào mới hơn mặt hàng mới nhất. Vì vậy, chúng tôi muốn so sánh từng mục (i1
) vào tập hợp các mục (i2
) mới hơn và có cùng danh mục với i1
. Nếu số lượng các mục mới hơn đó ít hơn bốn, i1
là một trong những chúng tôi bao gồm. Nếu không, đừng bao gồm nó.
Cái hay của giải pháp này là nó hoạt động cho dù bạn có bao nhiêu danh mục và tiếp tục hoạt động nếu bạn thay đổi các danh mục. Nó cũng hoạt động ngay cả khi số lượng mục trong một số danh mục ít hơn bốn.
Một giải pháp khác hoạt động nhưng dựa vào tính năng biến người dùng MySQL:
SELECT *
FROM (
SELECT i.*, @r := IF(@g = category_id, @r+1, 1) AS rownum, @g := category_id
FROM (@g:=null, @r:=0) AS _init
CROSS JOIN item i
ORDER BY i.category_id, i.date_listed
) AS t
WHERE t.rownum <= 3;
MySQL 8.0.3 đã giới thiệu hỗ trợ cho các chức năng cửa sổ chuẩn SQL. Bây giờ chúng ta có thể giải quyết loại vấn đề này theo cách các RDBMS khác làm:
WITH numbered_item AS (
SELECT *, ROW_NUMBER() OVER (PARTITION BY category_id ORDER BY item_id) AS rownum
FROM item
)
SELECT * FROM numbered_item WHERE rownum <= 4;