To jest największy problem w grupie i jest to bardzo częste pytanie dotyczące SQL.
Oto jak rozwiązuję to za pomocą złączeń zewnętrznych:
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;
Zakładam, że klucz podstawowy item
tabela to item_id
i że jest to monotonicznie rosnący pseudoklucz. Oznacza to większą wartość w item_id
odpowiada nowszemu wierszowi w item
.
Oto, jak to działa:dla każdego przedmiotu istnieje kilka innych, nowszych przedmiotów. Na przykład istnieją trzy elementy nowsze niż czwarty najnowszy element. Nie ma żadnych nowszych pozycji niż najnowsza pozycja. Dlatego chcemy porównać każdy element (i1
) do zestawu elementów (i2
), które są nowsze i należą do tej samej kategorii co i1
. Jeśli liczba tych nowszych pozycji jest mniejsza niż cztery, i1
jest jednym z tych, które uwzględniamy. W przeciwnym razie nie dołączaj go.
Piękno tego rozwiązania polega na tym, że działa ono niezależnie od liczby posiadanych kategorii i działa nadal, jeśli zmienisz kategorie. Działa to również wtedy, gdy liczba przedmiotów w niektórych kategoriach jest mniejsza niż cztery.
Inne rozwiązanie, które działa, ale opiera się na funkcji zmiennych użytkownika 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;
W MySQL 8.0.3 wprowadzono obsługę standardowych funkcji okien SQL. Teraz możemy rozwiązać ten problem w sposób, w jaki robią to inne 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;