Devolver solo minutos con actividad
Más corto
SELECT DISTINCT
date_trunc('minute', "when") AS minute
, count(*) OVER (ORDER BY date_trunc('minute', "when")) AS running_ct
FROM mytable
ORDER BY 1;
Usa date_trunc()
, te devuelve exactamente lo que necesitas.
No incluyas id
en la consulta, ya que desea GROUP BY
rebanadas diminutas.
count()
se usa típicamente como una función agregada simple. Agregar un OVER
cláusula hace que sea una función de ventana. Omitir PARTITION BY
en la definición de la ventana:desea un conteo continuo sobre todas las filas . De forma predeterminada, eso cuenta desde la primera fila hasta el último par de la fila actual según lo definido por ORDER BY
. El manual:
La opción de encuadre predeterminada es RANGE UNBOUNDED PRECEDING
, que es lo mismo que RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
. Con ORDER BY
, esto configura el marco para que sean todas las filas desde el inicio de la partición hasta el último ORDER BY
de la fila actual compañero.
Y resulta que eso es exactamente lo que necesitas.
Usar count(*)
en lugar de count(id)
. Se ajusta mejor a su pregunta ("recuento de filas"). Generalmente es ligeramente más rápido que count(id)
. Y, aunque podríamos suponer que id
es NOT NULL
, no se ha especificado en la pregunta, por lo que count(id)
es incorrecto , estrictamente hablando, porque los valores NULL no se cuentan con count(id)
.
No puedes GROUP BY
porciones de minutos en el mismo nivel de consulta. Las funciones agregadas se aplican antes funciones de ventana, la función de ventana count(*)
solo vería 1 fila por minuto de esta manera.
Puede, sin embargo, SELECT DISTINCT
, porque DISTINCT
se aplica después funciones de ventana.
ORDER BY 1
es solo una abreviatura de ORDER BY date_trunc('minute', "when")
aquí.1
es una referencia de referencia posicional a la primera expresión en SELECT
lista.
Usa to_char()
si necesita formatear el resultado. Me gusta:
SELECT DISTINCT
to_char(date_trunc('minute', "when"), 'DD.MM.YYYY HH24:MI') AS minute
, count(*) OVER (ORDER BY date_trunc('minute', "when")) AS running_ct
FROM mytable
ORDER BY date_trunc('minute', "when");
Más rápido
SELECT minute, sum(minute_ct) OVER (ORDER BY minute) AS running_ct
FROM (
SELECT date_trunc('minute', "when") AS minute
, count(*) AS minute_ct
FROM tbl
GROUP BY 1
) sub
ORDER BY 1;
Muy parecido al anterior, pero:
Uso una subconsulta para agregar y contar filas por minuto. De esta forma obtenemos 1 fila por minuto sin DISTINCT
en el exterior SELECT
.
Usa sum()
como función agregada de ventana ahora para sumar los recuentos de la subconsulta.
Descubrí que esto es sustancialmente más rápido con muchas filas por minuto.
Incluir minutos sin actividad
Más corto
@GabiMe preguntó en un comentario cómo obtener una fila para cada minute
en el marco de tiempo, incluidos aquellos en los que no ocurrió ningún evento (sin fila en la tabla base):
SELECT DISTINCT
minute, count(c.minute) OVER (ORDER BY minute) AS running_ct
FROM (
SELECT generate_series(date_trunc('minute', min("when"))
, max("when")
, interval '1 min')
FROM tbl
) m(minute)
LEFT JOIN (SELECT date_trunc('minute', "when") FROM tbl) c(minute) USING (minute)
ORDER BY 1;
Genere una fila para cada minuto en el período de tiempo entre el primero y el último evento con generate_series()
- aquí directamente basado en valores agregados de la subconsulta.
LEFT JOIN
a todas las marcas de tiempo truncadas al minuto y conteo. NULL
los valores (donde no existe ninguna fila) no se suman al conteo acumulado.
Más rápido
Con CET:
WITH cte AS (
SELECT date_trunc('minute', "when") AS minute, count(*) AS minute_ct
FROM tbl
GROUP BY 1
)
SELECT m.minute
, COALESCE(sum(cte.minute_ct) OVER (ORDER BY m.minute), 0) AS running_ct
FROM (
SELECT generate_series(min(minute), max(minute), interval '1 min')
FROM cte
) m(minute)
LEFT JOIN cte USING (minute)
ORDER BY 1;
Nuevamente, agregue y cuente filas por minuto en el primer paso, omite la necesidad de DISTINCT
posterior .
Diferente de count()
, sum()
puede devolver NULL
. Predeterminado a 0
con COALESCE
.
Con muchas filas y un índice en "when"
esta versión con una subconsulta fue la más rápida entre un par de variantes que probé con Postgres 9.1 - 9.4:
SELECT m.minute
, COALESCE(sum(c.minute_ct) OVER (ORDER BY m.minute), 0) AS running_ct
FROM (
SELECT generate_series(date_trunc('minute', min("when"))
, max("when")
, interval '1 min')
FROM tbl
) m(minute)
LEFT JOIN (
SELECT date_trunc('minute', "when") AS minute
, count(*) AS minute_ct
FROM tbl
GROUP BY 1
) c USING (minute)
ORDER BY 1;