2

我有一个看起来像这样的表(在 Postgres 9.1 中):

CREATE TABLE actions (
  user_id: INTEGER,
  date:    DATE,
  action:  VARCHAR(255),
  count:   INTEGER
)

例如:

    user_id    |    date    |     action   | count
---------------+------------+--------------+-------
             1 | 2013-01-01 | Email        |     1
             1 | 2013-01-02 | Call         |     3
             1 | 2013-01-03 | Email        |     3
             1 | 2013-01-04 | Call         |     2
             1 | 2013-01-04 | Voicemail    |     2
             1 | 2013-01-04 | Email        |     2
             2 | 2013-01-04 | Email        |     2

我希望能够查看用户在一段时间内针对一组特定操作的总操作;例如,电话 + 电子邮件:

  user_id  | date        |  count  
-----------+-------------+---------
         1 | 2013-01-01  |       1
         1 | 2013-01-02  |       4
         1 | 2013-01-03  |       7
         1 | 2013-01-04  |      11
         2 | 2013-01-04  |       2

到目前为止,我创建的怪物看起来像这样:

SELECT
  date, user_id, SUM(count) OVER (PARTITION BY user_id ORDER BY date) AS count
FROM
  actions
WHERE
  action IN ('Call', 'Email') 
GROUP BY
  user_id, date, count;

这适用于单个动作,但似乎在同一天发生多个动作时会中断,例如,而不是预期的11on 2013-01-04,我们得到9

    date    |      user_id | count
------------+--------------+-------
 2013-01-01 | 1            |     1
 2013-01-02 | 1            |     4
 2013-01-03 | 1            |     7
 2013-01-04 | 1            |     9 <-- should be 11?
 2013-01-04 | 2            |     2

是否可以调整我的查询以解决此问题?我尝试在 上删除分组count,但 Postgres 似乎不喜欢这样:

column "actions.count" must appear in the GROUP BY clause
or be used in an aggregate function
LINE 2:      date, user_id, SUM(count) OVER (PARTITION BY user...
                                ^
4

3 回答 3

1

该表有一个名为“count”的列,SELECT 子句中的表达式别名为“count”,它是不明确的。

阅读文档:http ://www.postgresql.org/docs/9.0/static/sql-select.html#SQL-GROUPBY

如果有歧义,GROUP BY 名称将被解释为输入列名称而不是输出列名称。

这意味着,您的查询不按 SELECT 子句中评估的“计数”分组,而是按从表中获取的“计数”值分组。

此查询给出预期结果,请参阅SQL Fiddle

SELECT date, user_id, count
from (
   Select date, user_id, 
          SUM(count) OVER (PARTITION BY user_id ORDER BY date) AS count
  FROM actions
  WHERE
    action IN ('Call', 'Email') 
) alias
GROUP BY
  user_id, date, count;
于 2013-07-22T17:02:44.247 回答
1

断言

user_id目前尚不清楚您是否要排序date

也不清楚您是否要在结果列表中包含日期,因为在基表中没有行。在这种情况下,请参考这个密切相关的答案:
PostgreSQL: running count of rows for a query 'by minute'

修复名称

首先,我使用的是这个测试表而不是你有问题的表

CREATE TEMP TABLE actions (
  user_id integer,
  thedate    date,
  action  text,
  ct   integer
);

您使用保留字和函数名作为标识符(列名)是问题的一部分。

维修查询

结合聚合函数和窗口函数

由于首先应用聚合函数,因此您的原始查询将找到的行 foruser_id = 1thedate = '2013-01-04'合并为一个。你必须乘以count(*)得到实际的运行计数。

您可以在没有 subquery的情况下执行此操作,因为您可以组合聚合函数和窗口函数。首先应用聚合函数。您甚至可以在聚合函数的结果上创建一个窗口函数

SELECT thedate
     , user_id
     , sum(ct * count(*)) OVER (PARTITION BY user_id
                                ORDER BY thedate) AS running_ct
FROM   actions
WHERE  action IN ('Call', 'Email') 
GROUP  BY user_id, thedate, ct
ORDER  BY user_id, thedate;

或简化为:

...
 , sum(sum(ct)) OVER (PARTITION BY user_id
                      ORDER BY thedate) AS running_ct
...

这也应该是提出的解决方案中最快的。

在这里,内部sum()是一个聚合函数,而外部sum()是一个窗口函数——在聚合函数的结果之上。

或使用DISTINCT

另一种方法是使用DISTINCTorDISTINCT ON,因为它是窗口函数之后应用的:

DISTINCT- 这是可能的,因为running_ct无论如何都保证在这种情况下是相同的,因为对于窗口函数的默认帧定义,所有对等点都会立即求和。

SELECT DISTINCT
       thedate
     , user_id
     , sum(ct) OVER (PARTITION BY user_id ORDER BY thedate) AS running_ct
FROM   actions
WHERE  action IN ('Call', 'Email')
ORDER  BY thedate, user_id;

或简化为DISTINCT ON

SELECT DISTINCT ON (thedate, user_id)
...

->SQLfiddle演示所有变体。

于 2013-07-22T17:03:30.173 回答
1

此查询产生您正在寻找的结果:

SELECT DISTINCT   
  date, user_id, SUM(count) OVER (PARTITION BY user_id ORDER BY date) AS count 
  FROM actions
WHERE
  action IN ('Call', 'Email');

根据官方文档,默认窗口已经是您想要的,并且当电子邮件和电话在同一天发生时,“DISTINCT”消除了重复的行。

请参阅SQL 小提琴

于 2013-07-22T17:03:56.977 回答