En una base de datos de transacciones que abarca miles de entidades durante 18 meses, me gustaría ejecutar una consulta para agrupar cada período de 30 días posible entity_id
con una SUMA de los montos de sus transacciones y COUNT de sus transacciones en ese período de 30 días, y devolver los datos de una manera que luego puedo consultar. Después de muchas pruebas, este código logra mucho de lo que quiero:
SELECT id, trans_ref_no, amount, trans_date, entity_id,
SUM(amount) OVER(PARTITION BY entity_id, date_trunc('month',trans_date) ORDER BY entity_id, trans_date ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS trans_total,
COUNT(id) OVER(PARTITION BY entity_id, date_trunc('month',trans_date) ORDER BY entity_id, trans_date ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS trans_count
FROM transactiondb;
Y usaré en una consulta más grande estructurada algo como:
SELECT * FROM (
SELECT id, trans_ref_no, amount, trans_date, entity_id,
SUM(amount) OVER(PARTITION BY entity_id, date_trunc('month',trans_date) ORDER BY entity_id, trans_date ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS trans_total,
COUNT(id) OVER(PARTITION BY entity_id, date_trunc('month',trans_date) ORDER BY entity_id, trans_date ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS trans_count
FROM transactiondb ) q
WHERE trans_count >= 4
AND trans_total >= 50000;
El caso que esta consulta no cubre es cuando los recuentos de transacciones abarcarían varios meses, pero aún se realizarían dentro de los 30 días de diferencia. ¿Es posible este tipo de consulta con Postgres? Si es así, agradezco cualquier aporte. Muchos de los otros temas discuten agregados "en ejecución ", no rodantes .
Actualizar
El CREATE TABLE
guión:
CREATE TABLE transactiondb (
id integer NOT NULL,
trans_ref_no character varying(255),
amount numeric(18,2),
trans_date date,
entity_id integer
);
Los datos de muestra se pueden encontrar aquí . Estoy ejecutando PostgreSQL 9.1.16.
La producción ideal incluiría SUM(amount)
y COUNT()
de todas las transacciones durante un período continuo de 30 días. Ver esta imagen, por ejemplo:
El resaltado de fecha verde indica lo que incluye mi consulta. El resaltado de la fila amarilla indica registros de lo que me gustaría formar parte del conjunto.
Lectura previa:
fuente
every possible 30-day period by entity_id
Quieres decir que el período puede comenzar cualquier día, entonces 365 períodos posibles en un año (no bisiesto)? ¿O solo desea considerar los días con una transacción real como inicio de un período individualmente para cualquieraentity_id
? De cualquier manera, proporcione la definición de la tabla, la versión de Postgres, algunos datos de muestra y el resultado esperado para la muestra.entity_id
en una ventana de 30 días a partir de cada transacción real. ¿Puede haber múltiples transacciones para el mismo(trans_date, entity_id)
o es esa combinación definida única? La definición de su tabla no tieneUNIQUE
restricciones o PK, pero parece que faltan restricciones ...id
la clave primaria. Puede haber múltiples transacciones por entidad por día.Respuestas:
La consulta que tienes
Puede simplificar su consulta usando una
WINDOW
cláusula, pero eso solo acorta la sintaxis, no cambia el plan de consulta.count(*)
, yaid
que sin duda se defineNOT NULL
?ORDER BY entity_id
ya que yaPARTITION BY entity_id
Sin embargo, puede simplificar aún más:
no agregue nada
ORDER BY
a la definición de ventana, no es relevante para su consulta. Entonces no necesita definir un marco de ventana personalizado, tampoco:Más simple, más rápido, pero aún así es una mejor versión de lo que tienes , con meses estáticos .
La consulta que quieras
... no está claramente definido, así que me basaré en estos supuestos:
Cuente las transacciones y la cantidad por cada período de 30 días dentro de la primera y última transacción de cualquiera
entity_id
. Excluya los períodos iniciales y finales sin actividad, pero incluya todos los períodos posibles de 30 días dentro de esos límites externos.Esto enumera todos los períodos de 30 días para cada uno
entity_id
con sus agregados y contrans_date
el primer día (incluido) del período. Para obtener valores para cada fila individual, únase a la tabla base una vez más ...La dificultad básica es la misma que se analiza aquí:
La definición de marco de una ventana no puede depender de los valores de la fila actual.
Y más bien llame
generate_series()
contimestamp
entrada:La consulta que realmente quieres
Después de la actualización y discusión de la pregunta:
acumule filas de la misma
entity_id
en una ventana de 30 días a partir de cada transacción real.Dado que sus datos se distribuyen escasamente, debería ser más eficiente ejecutar una autounión con una condición de rango , sobre todo porque Postgres 9.1 aún no tiene
LATERAL
uniones:SQL Fiddle.
Una ventana móvil solo podría tener sentido (con respecto al rendimiento) con datos para la mayoría de los días.
Esto no agrega duplicados
(trans_date, entity_id)
por día, pero todas las filas del mismo día siempre se incluyen en la ventana de 30 días.Para una tabla grande, un índice de cobertura como este podría ayudar bastante:
La última columna
amount
solo es útil si obtiene escaneos de solo índice. De lo contrario, déjalo caer.Pero de todos modos no se usará mientras selecciona toda la tabla. Soportaría consultas para un pequeño subconjunto.
fuente
column "t0.amount" must appear in the GROUP BY clause...