Dies ist das Größt-n-pro-Gruppen-Problem und eine sehr häufige SQL-Frage.
So löse ich es mit äußeren Verknüpfungen:
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;
Ich nehme den Primärschlüssel des item
an Tabelle ist item_id
, und dass es sich um einen monoton steigenden Pseudoschlüssel handelt. Das heißt, ein größerer Wert in item_id
entspricht einer neueren Zeile in item
.
So funktioniert es:Für jeden Artikel gibt es einige andere Artikel, die neuer sind. Beispielsweise sind drei Elemente neuer als das viertneueste Element. Es gibt null Artikel, die neuer sind als der allerneueste Artikel. Wir wollen also jedes Element vergleichen (i1
) zum Satz von Elementen (i2
), die neuer sind und dieselbe Kategorie wie i1
haben . Wenn die Anzahl dieser neueren Elemente weniger als vier beträgt, i1
ist einer von denen, die wir einschließen. Fügen Sie es andernfalls nicht hinzu.
Das Schöne an dieser Lösung ist, dass sie unabhängig davon funktioniert, wie viele Kategorien Sie haben, und weiterhin funktioniert, wenn Sie die Kategorien ändern. Es funktioniert auch, wenn die Anzahl der Elemente in einigen Kategorien weniger als vier beträgt.
Eine andere Lösung, die funktioniert, aber auf die MySQL-Benutzervariablenfunktion angewiesen ist:
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 hat die Unterstützung für SQL-Standardfensterfunktionen eingeführt. Jetzt können wir diese Art von Problem so lösen, wie es andere RDBMS tun:
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;