计算 n 天内的不同用户数
Count distinct users over n-days
我的 table 包含两个字段,CalDay
时间戳字段,时间设置在 00:00:00 和 UserID
。
它们一起形成一个复合键,但重要的是要记住,每个给定的日历日都有很多行,并且给定的一天没有固定的行数。
基于这个数据集,我需要计算在一组 window 的时间内有多少不同的用户,比如说 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 纪元而不是时间戳会有所帮助,但这只是一个大胆的猜测。欢迎任何建议。
这个应该有效。无法评论速度,但应该比您当前的速度低很多。希望您在这两个领域都有索引。
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 组合构建大量 table 然后 grouping/aggregating,而是首先获取 "daily" 数据,然后加入 30 天。使连接更小并且 returns 快速(在我的系统上源 table 中的 45000 行不到 1 秒)。
我的 table 包含两个字段,CalDay
时间戳字段,时间设置在 00:00:00 和 UserID
。
它们一起形成一个复合键,但重要的是要记住,每个给定的日历日都有很多行,并且给定的一天没有固定的行数。
基于这个数据集,我需要计算在一组 window 的时间内有多少不同的用户,比如说 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 纪元而不是时间戳会有所帮助,但这只是一个大胆的猜测。欢迎任何建议。
这个应该有效。无法评论速度,但应该比您当前的速度低很多。希望您在这两个领域都有索引。
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 组合构建大量 table 然后 grouping/aggregating,而是首先获取 "daily" 数据,然后加入 30 天。使连接更小并且 returns 快速(在我的系统上源 table 中的 45000 行不到 1 秒)。