我的表由两个字段组成,CalDay
是一个时间戳记字段,其时间设置为00:00:00,而UserID
。
它们在一起形成一个复合键,但是请记住,每个给定日历日有很多行,并且给定一天没有固定的行数。
基于此数据集,我需要计算在设定的时间范围内(例如30天)有多少不同的用户。
使用Postgres 9.3我不能使用COUNT(Distinct UserID) OVER ...
,也不能使用DENSE_RANK() OVER (... RANGE BETWEEN)
解决该问题,因为RANGE
仅接受UNBOUNDED
。
所以我采用了老式的方法,并尝试了一个标量子查询:
SELECT
xx.*
,(
SELECT COUNT(DISTINCT UserID)
FROM data_table AS yy
WHERE yy.CalDay BETWEEN xx.CalDay - interval '30 days' AND xx.u_ts
) as rolling_count
FROM data_table AS xx
ORDER BY yy.CalDay
从理论上讲,这应该起作用,对吗?我不确定,因为大约20分钟前我开始查询,它仍在运行。问题就出在这里,数据集仍然相对较小(25000行),但是会随着时间增长。我需要可以扩展并性能更好的东西。
我当时在想-也许-使用Unix时代而非时间戳可能会有所帮助,但这只是一个疯狂的猜测。任何建议都将受到欢迎。
答案 0 :(得分:1)
此应该起作用。无法评论速度,但应比您当前的速度小很多。希望您在这两个字段上都有索引。
SELECT t1.calday, COUNT(DISTINCT t1.userid) AS daily, COUNT(DISTINCT t2.userid) AS last_30_days
FROM data_table t1
JOIN data_table t2
ON t2.calday BETWEEN t1.calday - '30 days'::INTERVAL AND t1.calday
GROUP BY t1.calday
更新
使用大量数据对其进行了测试。上面的作品,但很慢。这样做的速度要快得多:
SELECT t1.*, COUNT(DISTINCT t2.userid) AS last_30_days
FROM (
SELECT calday, COUNT(DISTINCT userid) AS daily
FROM data_table
GROUP BY calday
) t1
JOIN data_table t2
ON t2.calday BETWEEN t1.calday - '30 days'::INTERVAL AND t1.calday
GROUP BY 1, 2
因此,它没有为所有的JOIN组合建立一个庞大的表,然后进行分组/汇总,而是首先获取了“每日”数据,然后加入了30天。保持连接小得多并快速返回(对于我系统上的源表中的45000行,在1秒之内)。