PostgreSQL:按分钟运行查询的行数

时间:2011-11-19 11:21:54

标签: sql postgresql datetime aggregate-functions window-functions

我需要查询每分钟直到该分钟的总行数。

到目前为止,我所能达到的最好成绩并非如此。它返回每分钟的计数,而不是每分钟的总计数:

SELECT COUNT(id) AS count
     , EXTRACT(hour from "when") AS hour
     , EXTRACT(minute from "when") AS minute
  FROM mytable
 GROUP BY hour, minute

1 个答案:

答案 0 :(得分:79)

仅返回活动

的分钟数

最短

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定义的当前行的第一行到最后一个对等计数。 I quote the manual

      

    默认框架选项是RANGE UNBOUNDED PRECEDING,即。{   与RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW相同。使用ORDER BY,   这会将帧设置为分区启动时的所有行   通过当前行的最后ORDER BY对等。

    恰好完全你需要的东西。

  • 使用count(*)而不是count(id)。它更适合您的问题(“行数”)。它通常比count(id)略微更快。而且,虽然我们可能会认为idNOT NULL,但问题中未指定,因此count(id) 错误,严格来说,因为NULL值不计入count(id)

  • 您无法在同一查询级别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;

与上述情况非常相似,但是:

  • 我使用子查询来聚合和计算每分钟的行数。这样,我们在外部DISTINCT没有SELECT的情况下每分钟获得1行。

  • 现在使用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;