Retorne apenas minutos com atividade
O mais curto
SELECT DISTINCT
date_trunc('minute', "when") AS minute
, count(*) OVER (ORDER BY date_trunc('minute', "when")) AS running_ct
FROM mytable
ORDER BY 1;
Use
date_trunc()
, ele retorna exatamente o que você precisa. Não inclua
id
na consulta, pois você deseja GROUP BY
fatias de minutos. count()
é normalmente usado como função agregada simples. Anexando um OVER
cláusula torna uma função de janela. Omitir PARTITION BY
na definição da janela - você deseja uma contagem em execução em todas as linhas . Por padrão, isso conta da primeira linha até o último par da linha atual, conforme definido por ORDER BY
. O manual:
A opção de enquadramento padrão éRANGE UNBOUNDED PRECEDING
, que é o mesmo queRANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
. ComORDER BY
,isso define o quadro para que todas as linhas da partição comecem até o últimoORDER BY
da linha atual par.
E isso é exatamente o que você precisa.
Use
count(*)
em vez de count(id)
. Ele se encaixa melhor na sua pergunta ("contagem de linhas"). Geralmente é um pouco mais rápido do que count(id)
. E, embora possamos supor que id
é NOT NULL
, não foi especificado na pergunta, então count(id)
está errado , estritamente falando, porque os valores NULL não são contados com count(id)
. Você não pode
GROUP BY
fatias de minutos no mesmo nível de consulta. As funções agregadas são aplicadas antes funções de janela, a função de janela count(*)
veria apenas 1 linha por minuto dessa maneira.Você pode, no entanto,
SELECT DISTINCT
, porque DISTINCT
é aplicado depois funções da janela. ORDER BY 1
é apenas um atalho para ORDER BY date_trunc('minute', "when")
aqui.1
é uma referência de referência posicional para a 1ª expressão no SELECT
Lista. Use
to_char()
se você precisar formatar o resultado. Como: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");
Mais 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;
Muito parecido com o acima, mas:
Eu uso uma subconsulta para agregar e contar linhas por minuto. Dessa forma, obtemos 1 linha por minuto sem
DISTINCT
no SELECT
externo . Use
sum()
como função de agregação de janela agora para somar as contagens da subconsulta. Achei isso substancialmente mais rápido com muitas linhas por minuto.
Incluir minutos sem atividade
O mais curto
@GabiMe perguntou em um comentário como obter uma linha para cada
minute
no período de tempo, incluindo aqueles em que nenhum evento ocorreu (sem linha na tabela 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;
Gere uma linha para cada minuto no intervalo de tempo entre o primeiro e o último evento com
generate_series()
- aqui diretamente baseado em valores agregados da subconsulta. LEFT JOIN
para todos os timestamps truncados para o minuto e contar. NULL
valores (onde não existe nenhuma linha) não são adicionados à contagem em execução. Mais rápido
Com CTE:
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;
Novamente, agregue e conte linhas por minuto na primeira etapa, omitindo a necessidade de
DISTINCT
posterior . Diferente de
count()
, sum()
pode retornar NULL
. Padrão para 0
com COALESCE
. Com muitas linhas e um índice em
"when"
esta versão com uma subconsulta foi a mais rápida entre algumas variantes que testei com o 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;