Dette er det største problem pr. gruppe, og det er et meget almindeligt SQL-spørgsmål.
Sådan løser jeg det med udvendige sammenføjninger:
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;
Jeg antager den primære nøgle til item
tabellen er item_id
, og at det er en monotont stigende pseudokey. Det vil sige en større værdi i item_id
svarer til en nyere række i item
.
Sådan fungerer det:For hver vare er der et antal andre varer, der er nyere. For eksempel er der tre varer nyere end den fjerde nyeste vare. Der er nul varer nyere end den allernyeste vare. Så vi vil sammenligne hvert element (i1
) til sættet af elementer (i2
), der er nyere og har samme kategori som i1
. Hvis antallet af disse nyere elementer er mindre end fire, i1
er en af dem, vi medtager. Ellers skal du ikke inkludere det.
Det smukke ved denne løsning er, at den virker, uanset hvor mange kategorier du har, og fortsætter med at fungere, hvis du ændrer kategorierne. Det virker også, selvom antallet af varer i nogle kategorier er færre end fire.
En anden løsning, der virker, men er afhængig af MySQL-brugervariable-funktionen:
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 introducerede understøttelse af SQL standard vinduesfunktioner. Nu kan vi løse denne slags problemer, som andre 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;