我需要查询每分钟直到该分钟的总行数。
到目前为止,我所能达到的最好成绩并非如此。它返回每分钟的计数,而不是每分钟的总计数:
SELECT COUNT(id) AS count
, EXTRACT(hour from "when") AS hour
, EXTRACT(minute from "when") AS minute
FROM mytable
GROUP BY hour, minute
答案 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)
略微更快。而且,虽然我们可能会认为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
是对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;