Mysql
 sql >> Base de Dados >  >> RDS >> Mysql

Como SELECIONAR os quatro itens mais recentes por categoria?


Este é o maior problema de n por grupo e é uma pergunta SQL muito comum.

Aqui está como eu resolvo isso com associações externas:
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;

Estou assumindo a chave primária do item tabela é item_id , e que é uma pseudochave monotonicamente crescente. Ou seja, um valor maior em item_id corresponde a uma linha mais recente em item .

Veja como funciona:para cada item, há alguns outros itens que são mais recentes. Por exemplo, há três itens mais recentes que o quarto item mais recente. Há zero itens mais recentes do que o item mais recente. Então, queremos comparar cada item (i1 ) ao conjunto de itens (i2 ) que são mais recentes e têm a mesma categoria que i1 . Se o número desses itens mais recentes for menor que quatro, i1 é um dos que incluímos. Caso contrário, não inclua.

A beleza dessa solução é que ela funciona independentemente de quantas categorias você tenha e continua funcionando se você alterar as categorias. Também funciona mesmo que o número de itens em algumas categorias seja inferior a quatro.

Outra solução que funciona, mas depende do recurso de variáveis ​​de usuário do 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;

O MySQL 8.0.3 introduziu suporte para funções de janela padrão SQL. Agora podemos resolver esse tipo de problema da mesma forma que outros RDBMS fazem:
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;