PostgreSQL:以分钟为单位运行查询的行数
我需要每分钟查询到那一刻的总计数。
我迄今为止所能达到的最好效果并不能解决问题。 它返回每分钟计数,而不是每分钟的总计数:
SELECT COUNT(id) AS count , EXTRACT(hour from "when") AS hour , EXTRACT(minute from "when") AS minute FROM mytable GROUP BY hour, minute
只有几分钟的活动
最短
不会比这更简单:
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() 。 它给你正是你所需要的。 在使用
timestamptz
,请注意,“当日”的开始由当前时区设置定义。 -
请勿在查询中包含
id
,因为您希望GROUP BY
分钟切片。 -
count()
主要用作简单的聚合函数 。 附加一个OVER
子句使其成为一个窗口函数 。 在窗口定义中忽略PARTITION BY
– 您希望在所有行上运行计数 。 默认情况下,从ORDER BY
定义的当前行的第一行到最后一个对象计数。 我引用手册 :默认的成帧选项是
RANGE UNBOUNDED PRECEDING
,它与RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW;
它将框架设置为ORDER BY
sorting中分区的所有行启动到当前行的最后一个同位体。而这正好是你所需要的。
-
使用
count(*)
而不是count(id)
。 它更适合你的问题(“行数”)。 它通常比count(id)
稍快 。 而且,虽然我们可能会认为id
NOT NULL
,但在问题中没有指定,严格来说count(id)
是错误的 。 -
您不能在相同的查询级别进行
GROUP BY
分钟切片。 在窗口函数之前应用聚合函数,窗口函数count(*)
每分钟只能看到1行。
但是,您可以SELECT DISTINCT
,因为在窗口函数之后应用了DISTINCT
。 -
ORDER BY 1
只是这里的ORDER BY date_trunc('minute', "when")
缩写。
1
作为引用SELECT
子句中第一个expression式的位置参数。 -
如果需要美化结果,请使用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;
很像以上,但:
-
我使用子查询折叠和每分钟计数行。
-
这样,我们在外部查询中每分钟获得不同的行,不需要
DISTINCT
步骤。 -
现在使用
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") , '1 min') FROM tbl ) m(minute) LEFT JOIN (SELECT date_trunc('minute', "when") FROM tbl) c(minute) USING (minute) ORDER BY 1;
-
使用
generate_series()
在第一个和最后一个事件之间的时间范围内为每一分钟生成一行。 在一个子查询中将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(c.minute_ct) OVER (ORDER BY m.minute), 0) AS running_ct FROM (SELECT generate_series(date_trunc('minute', min("when")) ,max(minute), '1 min') AS minute FROM cte) m LEFT JOIN cte c USING (minute) ORDER BY 1;
很像以上,但:
-
再次,在第一步中每分钟折行数,省略稍后的
DISTINCT
。 -
不同于
count()
,sum()可以返回NULL
。 所以我把它包装在COALESCE中,而不是0。
由于每分钟有很多行和几行,并且带有子查询的这个版本的索引应该更快:
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"), '1 min') AS minute FROM tbl) m LEFT JOIN ( SELECT date_trunc('minute', "when") AS minute , count(*) AS minute_ct FROM tbl GROUP BY 1 ) c USING (minute) ORDER BY 1;
- 这是我用Postgres 9.1 – 9.4testing的几个变种中最快的。