2011-04-18 147 views
45

我使用countgroup by得到用户的数量登记的每一天:计数累计

SELECT created_at, COUNT(email) 
    FROM subscriptions 
GROUP BY created at; 

结果:

created_at count 
----------------- 
04-04-2011 100 
05-04-2011 50 
06-04-2011 50 
07-04-2011 300 

我想要得到的累计总用户的每一个而不是日。我如何得到这个?

created_at count 
----------------- 
04-04-2011 100 
05-04-2011 150 
06-04-2011 200 
07-04-2011 500 

回答

77

有了更大的数据集,window functions是执行这些类型的查询的最有效的方式 - 该表将被扫描一次,而不是每次约会一次,就像自加入一样。它看起来也更简单。 :) PostgreSQL 8.4及以上版本支持窗口功能。

这是什么样子:

SELECT created_at, sum(count(email)) OVER (ORDER BY created_at) 
FROM subscriptions 
GROUP BY created_at; 

这里OVER创建窗口; ORDER BY created_at意味着它必须总结created_at顺序的计数。


编辑:如果你想在一天之内删除重复的邮件,你可以使用sum(count(distinct email))。不幸的是,这不会删除跨越不同日期的重复。

如果你想删除所有重复,我认为最简单的是使用子查询和DISTINCT ON。这要归功于电子邮件到他们的最早日期(因为我被created_at升序顺序排序,它会选择最早的企业之一):

SELECT created_at, sum(count(email)) OVER (ORDER BY created_at) 
FROM (
    SELECT DISTINCT ON (email) created_at, email 
    FROM subscriptions ORDER BY email, created_at 
) AS subq 
GROUP BY created_at; 

如果您创建(email, created_at)索引,此查询不应该太慢了。


(如果你想测试,这是我创建的样本数据集)

create table subscriptions as 
    select date '2000-04-04' + (i/10000)::int as created_at, 
      '[email protected]' || (i%700000)::text as email 
    from generate_series(1,1000000) i; 
create index on subscriptions (email, created_at); 
+0

这是很棒的intgr,只有我的订阅表包含大量重复的电子邮件行。所以'over'所做的是'count'数字,但是我仍然需要重新计算每个后续日期的唯一电子邮件。 – Khairul 2011-04-18 09:53:00

+0

我用“DISTINCT ON”子查询更新了我的答案。它仍然比Andriy的答案快得多 - 可以在几秒钟内处理一百万行 - 但可能更复杂。 – intgr 2011-04-18 13:55:29

+0

关于generate_series函数的不错提示! – 2011-04-19 06:33:48

6

用途:

SELECT a.created_at, 
     (SELECT COUNT(b.email) 
      FROM SUBSCRIPTIONS b 
     WHERE b.created_at <= a.created_at) AS count 
    FROM SUBSCRIPTIONS a 
2
SELECT 
    s1.created_at, 
    COUNT(s2.email) AS cumul_count 
FROM subscriptions s1 
    INNER JOIN subscriptions s2 ON s1.created_at >= s2.created_at 
GROUP BY s1.created_at 
+0

我试过'sum(s2。计数)'和控制台提供了一个错误:'聚合函数调用不能嵌套' – Khairul 2011-04-18 06:43:34

+0

我的意思是说'COUNT(s2.email)',对不起。请参阅我编辑的解决方案。 – 2011-04-18 06:49:18

+0

感谢哥们!我正在处理一个更复杂的查询,并且你的结构很容易理解(并因此实现)。 – Khairul 2011-04-18 07:20:29

2

我想,你希望每天只有一排,你想仍表现出天没有任何订阅(假设没有人认购某一特定日期,你想以前一天的余额显示该日期?)。如果是这样的话,你可以使用“与”功能:

with recursive serialdates(adate) as (
    select cast('2011-04-04' as date) 
    union all 
    select adate + 1 from serialdates where adate < cast('2011-04-07' as date) 
) 
select D.adate, 
(
    select count(distinct email) 
    from subscriptions 
    where created_at between date_trunc('month', D.adate) and D.adate 
) 
from serialdates D 
+0

谢谢,''带''功能也可能有用。学到了新东西。 – Khairul 2011-04-18 07:53:03

+2

而不是serialdates你可以使用内置函数:'generate_series(timestamp'2011-04-04',timestamp'2011-04-07',interval'1 day')' – intgr 2011-04-18 13:53:13

-3

,最好的办法是有一个日历表: 日历( 日期日期, 月INT , 季度INT, 一半INT, 周INT, 年INT )

然后,ç加入此表为您需要的字段进行汇总。

+1

这与获得跑步总数无关。 – 2014-07-18 09:58:52