Това е най-големият проблем-n-на-група и е много често срещан SQL въпрос.
Ето как го разрешавам с външни съединения:
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;
Предполагам, че е първичният ключ на item
таблицата е item_id
, и че това е монотонно нарастващ псевдоключ. Тоест по-голяма стойност в item_id
съответства на по-нов ред в item
.
Ето как работи:за всеки артикул има известен брой други елементи, които са по-нови. Например, има три елемента, по-нови от четвъртия най-нов елемент. Има нула артикули, по-нови от най-новия артикул. Така че искаме да сравним всеки елемент (i1
) към набора от елементи (i2
), които са по-нови и имат същата категория като i1
. Ако броят на тези по-нови елементи е по-малък от четири, i1
е един от тези, които включваме. В противен случай не го включвайте.
Красотата на това решение е, че работи без значение колко категории имате и продължава да работи, ако промените категориите. Също така работи, дори ако броят на елементите в някои категории е по-малък от четири.
Друго решение, което работи, но разчита на функцията за потребителски променливи на 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 въведе поддръжка за SQL стандартни прозоречни функции. Сега можем да решим този вид проблем по начина, по който го правят другите RDBMS:
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;