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 BYda 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;