Возвращать только минуты с активностью
Кратчайший
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 спросил в комментарии как получить строку eone для каждые 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;