Връщане само на минути с активност
Най-кратък
SELECT DISTINCT
date_trunc('minute', "when") AS minute
, count(*) OVER (ORDER BY date_trunc('minute', "when")) AS running_ct
FROM mytable
ORDER BY 1;
Използвайте date_trunc() , връща точно това, от което се нуждаете.
Не включвайте id в заявката, тъй като искате да GROUP BY минути филийки.
count() обикновено се използва като обикновена агрегатна функция. Добавяне на OVER клауза го прави функция прозорец. Пропуснете PARTITION BY в дефиницията на прозореца - искате текущ брой за всички редове . По подразбиране това се брои от първия ред до последния партньор на текущия ред, както е дефинирано от ORDER BY . Ръководството:
Опцията за рамкиране по подразбиране е
RANGE UNBOUNDED PRECEDING, което е същото катоRANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW. СORDER BY, това задава рамката да бъде всички редове от началото на дяла до последнияORDER BYна текущия ред връстник.
И това се случва точно това, от което се нуждаете.
Използвайте count(*) вместо count(id) . По-добре отговаря на вашия въпрос („брой редове“). Обикновено е малко по-бързо отколкото count(id) . И макар че можем да приемем, че id е NOT NULL , не е посочено във въпроса, така че count(id) е грешно , строго погледнато, защото NULL стойностите не се отчитат с count(id) .
Не можете да GROUP BY минути срезове на същото ниво на заявка. Агрегатните функции се прилагат преди функции на прозореца, функцията на прозореца count(*) ще вижда само 1 ред в минута по този начин.
Можете обаче да SELECT DISTINCT , защото DISTINCT се прилага след функции на прозореца.
ORDER BY 1 е просто стенография за ORDER BY date_trunc('minute', "when") тук.1 е позиционна препратка към 1-вия израз в SELECT списък.
Използвайте to_char() ако трябва да форматирате резултата. Като:
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");
Най-бърз
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;
Много като горното, но:
Използвам подзаявка за агрегиране и преброяване на редове в минута. По този начин получаваме 1 ред в минута без DISTINCT във външния SELECT .
Използвайте sum() като агрегатна функция на прозореца сега, за да добавите броя от подзаявката.
Открих, че това е значително по-бързо с много редове в минута.
Включете минути без активност
Най-кратък
@GabiMe попита в коментар как да получите един ред за всички minute във времевата рамка, включително тези, при които не е настъпило събитие (няма ред в основната таблица):
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;
Генерирайте ред за всяка минута във времевата рамка между първото и последното събитие с generate_series() - тук директно въз основа на обобщени стойности от подзаявката.
LEFT JOIN до всички времеви марки, съкратени до минутите и отчитане. NULL стойности (където не съществува ред) не се добавят към текущия брой.
Най-бърз
С 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;
Отново, обобщавайте и броете редове в минута в първата стъпка, то пропуска необходимостта от по-късно DISTINCT .
Различно от count() , sum() може да върне NULL . По подразбиране е 0 с COALESCE .
С много редове и индекс на "when" тази версия с подзаявка беше най-бърза сред няколко варианта, които тествах с 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;