Search code examples
sqlpostgresqlstring-function

How to count events in groups before a specific event


Suppose I have a table like the following:

\d events 
    Table "public.events
   Column   |           Type           | Modifiers 
------------+--------------------------+-----------
 my_id      | bigint                   | 
 tstamp     | timestamp with time zone | 
 event_type | text                     | 

With example data:

   my_id     |           tstamp           | event_type 
------------+----------------------------+------------
 1111111111 | 2015-11-14 09:02:46.185+02 | A
 1111111111 | 2015-11-14 17:32:58+02     | B
 1111111111 | 2015-11-28 15:06:30.895+02 | A
 1111111111 | 2015-12-05 15:22:31.582+02 | A
 2222222222 | 2015-11-17 15:06:07.481+02 | A
 2222222222 | 2015-11-17 20:30:03+02     | B
 2222222222 | 2015-12-04 15:36:31.532+02 | A
 3333333333 | 2015-11-20 15:06:01.621+02 | A
 3333333333 | 2015-11-20 19:15:09.908+02 | A
 3333333333 | 2015-11-21 15:06:01.621+02 | A
 3333333333 | 2015-11-26 09:07:45.134+02 | B
 3333333333 | 2015-11-27 14:39:31.657+02 | A
 4444444444 | 2015-12-05 10:21:21.441+02 | A
 4444444444 | 2015-12-05 20:00:40.772+02 | B

I would like to count all events A per my_id before event B.

The expected output would be:

   my_id   | events_before_B 
-----------+-----------------
1111111111 | 1
2222222222 | 2
3333333333 | 3
4444444444 | 1

Postgres version 9.4


Solution

  • SELECT my_id
          ,position('B' IN string_agg(event_type, '')) - 1 events_before_B
    FROM events
    GROUP BY my_id
    ORDER BY my_id
    

    SQLFIDDLE-DEMO


    Explanation:

    string_agg()

    select my_id,string_agg(event_type,'') from events group by my_id yields

    my_id      string_agg 
    ---------- ---------- 
    1111111111 ABAA       
    2222222222 ABA        
    3333333333 AAABA      
    4444444444 AB   
    

    position()

    select position('B' in 'AAABAA') can be used to find out the position of B in string AAABAA

    position 
    -------- 
    4    
    

    So select position('B' in 'AAABAA')-1 yields the position of A just before B

    position 
    -------- 
    3