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 BYsorting中分区的所有行启动到当前行的最后一个同位体。

    而这正好是你所需要的。

  • 使用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的几个变种中最快的。