Supongamos que tengo una tabla de clientes y una tabla de compras. Cada compra pertenece a un cliente. Quiero obtener una lista de todos los clientes junto con su última compra en una declaración SELECT. cual es la mejor practica? ¿Algún consejo sobre la creación de índices?
Utilice estos nombres de tabla / columna en su respuesta:
- cliente: id, nombre
- compra: id, customer_id, item_id, date
Y en situaciones más complicadas, ¿sería beneficioso (en términos de rendimiento) desnormalizar la base de datos colocando la última compra en la tabla de clientes?
Si se garantiza que la identificación (de compra) se ordenará por fecha, ¿se pueden simplificar las declaraciones usando algo como LIMIT 1
?
Respuestas:
Este es un ejemplo del
greatest-n-per-group
problema que ha aparecido regularmente en StackOverflow.Así es como generalmente recomiendo resolverlo:
Explicación: dada una fila
p1
, no debe haber una filap2
con el mismo cliente y una fecha posterior (o en el caso de empates, una posteriorid
). Cuando descubrimos que eso es cierto, entoncesp1
es la compra más reciente para ese cliente.En cuanto a los índices, que crearía un índice compuesto en
purchase
más de las columnas (customer_id
,date
,id
). Eso puede permitir que la unión externa se realice utilizando un índice de cobertura. Asegúrese de probar en su plataforma, porque la optimización depende de la implementación. Use las funciones de su RDBMS para analizar el plan de optimización. Por ejemplo,EXPLAIN
en MySQL.Algunas personas usan subconsultas en lugar de la solución que muestro arriba, pero creo que mi solución hace que sea más fácil resolver los lazos.
fuente
También puedes intentar hacer esto usando una sub selección
El select debe unirse a todos los clientes y su última fecha de compra.
fuente
INNER JOIN
a aLEFT OUTER JOIN
.purchase
tabla es la fecha y el customer_id, pero la consulta solicita todos los campos de la tabla.No ha especificado la base de datos. Si es uno que permite funciones analíticas, puede ser más rápido usar este enfoque que el GRUPO BY (definitivamente más rápido en Oracle, probablemente más rápido en las últimas ediciones de SQL Server, no conozco otros).
La sintaxis en SQL Server sería:
fuente
Otro enfoque sería usar una
NOT EXISTS
condición en su condición de unión para probar compras posteriores:fuente
AND NOT EXISTS
parte en palabras fáciles?Encontré este hilo como una solución a mi problema.
Pero cuando los probé, el rendimiento fue bajo. A continuación es mi sugerencia para un mejor rendimiento.
Esperamos que esto sea útil.
fuente
top 1
yordered it by
MaxDatedesc
Si está utilizando PostgreSQL, puede utilizarlo
DISTINCT ON
para encontrar la primera fila de un grupo.Documentos de PostgreSQL: distintivo en
Tenga en cuenta que los
DISTINCT ON
campos (aquícustomer_id
) deben coincidir con los campos más a la izquierda de laORDER BY
cláusula.Advertencia: esta es una cláusula no estándar.
fuente
Intenta esto, te ayudará.
He usado esto en mi proyecto.
fuente
Probado en SQLite:
La
max()
función de agregado asegurará que se seleccione la última compra de cada grupo (pero se supone que la columna de fecha está en un formato en el que max () proporciona la última, que normalmente es el caso). Si desea manejar compras con la misma fecha, puede usarlasmax(p.date, p.id)
.En términos de índices, usaría un índice en la compra con (customer_id, date, [cualquier otra columna de compra que desee devolver en su selección]).
El
LEFT OUTER JOIN
(a diferencia deINNER JOIN
) se asegurará de que los clientes que nunca hayan realizado una compra también estén incluidos.fuente
Por favor intente esto,
fuente