【问题标题】:Grouping of PostgreSQL dataPostgreSQL 数据分组
【发布时间】:2015-06-14 17:25:20
【问题描述】:

我有一个 postgresql 表,其中包含按日期/时间记录的事件。该表包含ideventtimestamp 列。

我的输出必须是这样的:

'Day', '1st Timers', '2nd Timers', '3rd Timers', '3+ Timers'

第一个计时器是第一次完成该事件的所有 id。 第二个计时器是第二次完成该事件的所有 id。等等等等。

这可以使用单个 SQL 查询吗?

编辑:根据请求的示例数据和输出

user_id date                event
1       09/03/15 14:08      opened
2      10/03/15 14:08       opened
1      11/03/15 14:08       opened
4      14/03/15 14:08       opened
1      15/03/15 14:08       opened
5      16/03/15 14:08       opened
1      17/03/15 14:08       opened
4      17/03/15 14:08       opened
6      18/03/15 14:08       opened
1      18/03/15 14:08       opened
6      18/03/15 14:08       other


Output (for event=opened)
date        1time   2times  3times  4times  5times
09/03/15    1       0       0       0       0
10/03/15    1       0       0       0       0
11/03/15    0       1       0       0       0
14/03/15    1       0       0       0       0
15/03/15    0       0       1       0       0
16/03/15    1       0       0       0       0
17/03/15    0       1       0       1       0
18/03/15    1       0       0       0       1

【问题讨论】:

  • 能否提供示例表数据和预期输出?
  • 一如既往地,请提供您的 Postgres 版本。它与最佳解决方案相关。
  • 如果一个用户在他/她的第一天做了两次活动,他(她)是否算作“第一个计时器”“第二个计时器”?

标签: sql postgresql amazon-redshift


【解决方案1】:

对于每个日期,您似乎都想计算点击 1 次、2 次等的用户数。我将其视为row_number(),后跟条件聚合:

select thedate,
       sum(case when seqnum = 1 then 1 else 0 end) as time_1,
       sum(case when seqnum = 2 then 1 else 0 end) as time_2,
       sum(case when seqnum = 3 then 1 else 0 end) as time_3,
       sum(case when seqnum = 4 then 1 else 0 end) as time_4,
       sum(case when seqnum = 5 then 1 else 0 end) as time_5
from (select t.*, date_trunc('day', date) as thedate
             row_number() over (partition by user_id order by date_trunc('day', date)) as seqnum
      from table t
      where event = 'opened'
     ) t
group by thedate
order by thedate;

【讨论】:

  • 巧妙使用casesum 与窗口函数
  • 太棒了!似乎几乎可以工作了。我得到的输出是pastebin.com/UyDYQ2pr。我相信 column2 的正确输出是(column2 - column1)。需要进行一些细微的调整,但无法确定。
  • @Anoop 。 . .我想你只是想要where event = 'opened'
  • 正确。我想到了。接受答案。
【解决方案2】:

聚合FILTER

从 Postgres 9.4 开始使用新的聚合 FILTER 子句:

SELECT event_time::date
     , count(*) FILTER (WHERE rn = 1) AS times_1
     , count(*) FILTER (WHERE rn = 2) AS times_2
     , count(*) FILTER (WHERE rn = 3) AS times_3
    -- etc.
from (
   SELECT *, row_number() OVER (PARTITION BY user_id ORDER BY event_time) AS rn
   FROM   tbl
   ) t
GROUP  BY 1
ORDER  BY 1;

相关:

关于演员event_time::date

交叉表

或者使用实际的交叉表查询(更快)。适用于任何现代 Postgres 版本。 请先阅读:

SELECT * FROM crosstab(
       'SELECT event_time::date, rn, count(*)::int AS ct
        FROM  (
           SELECT *, row_number() OVER (PARTITION BY user_id ORDER BY event_time) AS rn
           FROM   tbl
           ) t
        GROUP  BY 1, 2
        ORDER  BY 1'

      ,$$SELECT * FROM unnest ('{1,2,3}'::int[])$$
   ) AS ct (day date, times_1 int, times_2 int, times_3 int);

【讨论】:

  • 谢谢会尝试。那只是我快速制作的示例数据,实际的字段名称是“event_time”。
  • 我正在使用 Amazon RedShift。我相信不支持交叉表和过滤器。 :(
  • @Anoop:我相信这是你应该提前告诉我们的。 Redshift 不是 Postgres。我也确实要求过版本...
  • 很抱歉。对 RedShift 非常陌生。我相信底层数据库是 postgres,没有任何区别。
猜你喜欢
  • 2021-02-11
  • 2012-08-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-10-29
  • 2019-03-20
  • 1970-01-01
  • 2010-10-30
相关资源
最近更新 更多