在n天内统计不同的用户

时间:2018-11-28 13:22:13

标签: postgresql postgresql-9.3 window-functions

我的表由两个字段组成,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时代而非时间戳可能会有所帮助,但这只是一个疯狂的猜测。任何建议都将受到欢迎。

1 个答案:

答案 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秒之内)。