简体   繁体   中英

Grouping of PostgreSQL data

I have a postgresql table which has events recorded by date/time. The table has the columns id , event and timestamp .

My output has to be something like this:

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

1st timers are all ids that have done the event for the first time. 2nd timers are all ids that have done the event for the second time. etc. etc.

Is this possible using a single SQL query?

edit: Sample data and output as per request

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

For each date, you seem to want to count the number of users that hit 1 time, 2 times, and so on. I see this as a row_number() followed by conditional aggregation:

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;

Aggregate FILTER

Starting with Postgres 9.4 use the new aggregate FILTER clause:

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;

Related:

About the cast event_time::date :

Crosstab

Or use an actual crosstab query (faster). Available for any modern Postgres version. Read this first:

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);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM