只返回有活动的分钟数
最短
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() 通常用作普通的aggregate function。附加OVER 子句使其成为window function。在窗口定义中省略 PARTITION BY - 你想要一个运行计数所有行。默认情况下,从ORDER BY 定义的当前行的第一行到最后一个对等方计数。 The manual:
默认框架选项是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 值不计入@987654347 @。
您不能在同一查询级别使用GROUP BY 分钟切片。聚合函数应用在之前窗口函数,窗口函数count(*) 这样每分钟只能看到 1 行。
但是,您可以使用SELECT DISTINCT,因为DISTINCT 是在 窗口函数之后应用的。
ORDER BY 1 只是此处ORDER BY date_trunc('minute', "when") 的简写。
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 行,而外部 SELECT 中没有 DISTINCT。
现在使用sum() 作为窗口聚合函数来将子查询的计数相加。
我发现每分钟有很多行,这要快得多。
包括没有活动的分钟数
最短
@GabiMe asked in a comment 如何在时间范围内为每个 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;