Връщане само на минути с активност
Най-кратък
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;