Detta är det största problemet per grupp, och det är en mycket vanlig SQL-fråga.
Så här löser jag det med ytterskarvar:
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;
Jag antar den primära nyckeln för item
tabellen är item_id
, och att det är en monotont ökande pseudokey. Det vill säga ett större värde i item_id
motsvarar en nyare rad i item
.
Så här fungerar det:för varje föremål finns det ett antal andra föremål som är nyare. Till exempel finns det tre objekt nyare än det fjärde senaste objektet. Det finns noll artiklar nyare än det allra senaste objektet. Så vi vill jämföra varje objekt (i1
) till uppsättningen objekt (i2
) som är nyare och har samma kategori som i1
. Om antalet av dessa nyare objekt är mindre än fyra, i1
är en av dem vi inkluderar. Annars ska du inte inkludera det.
Det fina med denna lösning är att den fungerar oavsett hur många kategorier du har, och fortsätter att fungera om du ändrar kategorierna. Det fungerar även om antalet artiklar i vissa kategorier är färre än fyra.
En annan lösning som fungerar men förlitar sig på MySQL användarvariabler:
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 introducerade stöd för SQL-standardfönsterfunktioner. Nu kan vi lösa den här typen av problem på det sätt som andra RDBMS gör:
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;