Ini adalah masalah terbesar-n-per-grup, dan ini adalah pertanyaan SQL yang sangat umum.
Inilah cara saya menyelesaikannya dengan gabungan luar:
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;
Saya mengasumsikan kunci utama dari item
tabelnya adalah item_id
, dan itu adalah pseudokey yang meningkat secara monoton. Artinya, nilai yang lebih besar di item_id
sesuai dengan baris yang lebih baru di item
.
Begini cara kerjanya:untuk setiap item, ada beberapa item lain yang lebih baru. Misalnya, ada tiga item yang lebih baru dari item terbaru keempat. Tidak ada item yang lebih baru dari item terbaru. Jadi kami ingin membandingkan setiap item (i1
) ke set item (i2
) yang lebih baru dan memiliki kategori yang sama dengan i1
. Jika jumlah item baru tersebut kurang dari empat, i1
adalah salah satu yang kami sertakan. Jika tidak, jangan sertakan.
Keindahan dari solusi ini adalah ia bekerja tidak peduli berapa banyak kategori yang Anda miliki, dan terus bekerja jika Anda mengubah kategori. Ini juga berfungsi bahkan jika jumlah item dalam beberapa kategori kurang dari empat.
Solusi lain yang berfungsi tetapi bergantung pada fitur variabel pengguna 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 memperkenalkan dukungan untuk fungsi jendela standar SQL. Sekarang kita dapat memecahkan masalah seperti ini seperti yang dilakukan RDBMS lainnya:
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;