Suponha que eu tenha uma tabela de clientes e uma tabela de compras. Cada compra pertence a um cliente. Desejo obter uma lista de todos os clientes, juntamente com sua última compra, em uma instrução SELECT. Qual é a melhor prática? Algum conselho sobre a criação de índices?
Use estes nomes de tabela / coluna na sua resposta:
- cliente: id, nome
- compra: id, customer_id, item_id, date
E em situações mais complicadas, seria benéfico (em termos de desempenho) desnormalizar o banco de dados colocando a última compra na tabela de clientes?
Se for garantido que o ID (compra) seja classificado por data, as instruções podem ser simplificadas usando algo como LIMIT 1
?
Respostas:
Este é um exemplo do
greatest-n-per-group
problema que apareceu regularmente no StackOverflow.Aqui está como eu geralmente recomendo resolvê-lo:
Explicação: dada uma linha
p1
, não deve haver linhap2
com o mesmo cliente e uma data posterior (ou, no caso de empates, posteriormenteid
). Quando achamos que isso é verdade, entãop1
é a compra mais recente para esse cliente.Em relação índices, eu criar um índice composto em
purchase
sobre as colunas (customer_id
,date
,id
). Isso pode permitir que a junção externa seja feita usando um índice de cobertura. Teste sua plataforma, pois a otimização depende da implementação. Use os recursos do seu RDBMS para analisar o plano de otimização. Por exemplo,EXPLAIN
no MySQL.Algumas pessoas usam subconsultas em vez da solução mostrada acima, mas acho que minha solução facilita a resolução de vínculos.
fonte
Você também pode tentar fazer isso usando um sub-select
A seleção deve ingressar em todos os clientes e na data da última compra.
fonte
INNER JOIN
para aLEFT OUTER JOIN
.purchase
tabela são a data e o customer_id, mas a consulta solicita todos os campos da tabela.Você não especificou o banco de dados. Se alguém permitir funções analíticas, pode ser mais rápido usar essa abordagem do que a abordagem GROUP BY (definitivamente mais rápida no Oracle, provavelmente mais rápida nas edições finais do SQL Server, não conheço outras).
A sintaxe no SQL Server seria:
fonte
Outra abordagem seria usar uma
NOT EXISTS
condição em sua condição de associação para testar compras posteriores:fonte
AND NOT EXISTS
parte em palavras fáceis?Encontrei este tópico como uma solução para o meu problema.
Mas quando os experimentei, o desempenho foi baixo. Abaixo está a minha sugestão para um melhor desempenho.
Espero que isso seja útil.
fonte
top 1
eordered it by
MaxDatedesc
Se você estiver usando o PostgreSQL, poderá
DISTINCT ON
encontrar a primeira linha de um grupo.PostgreSQL Docs - Distinto em
Observe que os
DISTINCT ON
campos - aquicustomer_id
- devem corresponder aos campos mais à esquerda no campoORDER BY
cláusula.Advertência: Esta é uma cláusula fora do padrão.
fonte
Tente isso, vai ajudar.
Eu usei isso no meu projeto.
fonte
Testado em SQLite:
A
max()
função agregada garantirá que a compra mais recente seja selecionada de cada grupo (mas assume que a coluna da data esteja em um formato no qual max () forneça a mais recente - o que normalmente é o caso). Se você quiser lidar com compras com a mesma data, poderá usarmax(p.date, p.id)
.Em termos de índices, eu usaria um índice na compra com (customer_id, date, [qualquer outra coluna de compra que você deseje retornar no seu select]).
O
LEFT OUTER JOIN
(ao contrário deINNER JOIN
) garantirá que os clientes que nunca fizeram uma compra também sejam incluídos.fonte
Por favor, tente isso,
fonte