4

I'd like to detect changes in column values in this (example) db

    WITH events(id, row,event) AS (
    VALUES
    (1,1, 0 )
   ,(1,2, 0 ) 
   ,(1,3, 1 )
   ,(1,4, 0 )
   ,(1,5, 1 )
   ,(2,1, 0 )
   ,(2,2, 1 )
   ,(3,1, 0 )
   ,(3,2, 0 )
   )
   select * from events

What I am looking for is code for a new column 'code' which switches to 1 AFTER de event column shows a 1. Within the same id the code stays 1. For this example this new column wil look like this

    WITH events2(id, row,event, code) AS (
    VALUES
    (1,1, 0, 0 )
   ,(1,2, 0, 0 ) 
   ,(1,3, 1, 0 )
   ,(1,4, 0, 1 ) -- notice the switch here
   ,(1,5, 1, 1 ) -- 
   ,(2,1, 0, 0 )
   ,(2,2, 1, 0 )
   ,(3,1, 0, 0 )
   ,(3,2, 0, 0 )
   )
   select * from events2

I have a hunch that the answer will be related to the answer on this question : PostgreSQL window function: partition by comparison

Somehow I cannot figure this out myself..

Peter

Community
  • 1
  • 1
Ptr
  • 41
  • 3

1 Answers1

0

COALESCE over a scalar subquery:

WITH events(id, zrow, zevent) AS (
    VALUES
    (1,1, 0 ) ,(1,2, 0 ) ,(1,3, 1 ) ,(1,4, 0 ) ,(1,5, 1 )
   ,(2,1, 0 ) ,(2,2, 1 )
   ,(3,1, 0 ) ,(3,2, 0 )
   )
SELECT id, zrow, zevent
        , COALESCE((SELECT 1 FROM events ex WHERE ex.id = ev.id AND ex.zrow < ev.zrow AND ex.zevent> 0),0) AS oevent
 FROM events ev
        ;

Or, avoid the COALESCE() by typecasting the boolean EXISTS() to INTEGER:

WITH events(id, zrow,event) AS (
    VALUES
    (1,1, 0 ) ,(1,2, 0 ) ,(1,3, 1 ) ,(1,4, 0 ) ,(1,5, 1 )
   ,(2,1, 0 ) ,(2,2, 1 )
   ,(3,1, 0 ) ,(3,2, 0 )
   )
SELECT id, zrow, event
        , EXISTS(SELECT 1 FROM events ex WHERE ex.id = ev.id AND ex.zrow < ev.zrow AND ex.event> 0)::integer AS oevent
FROM events ev
        ;

Find the MAX() value over the previous records within the same group (frame):

WITH events(id, zrow,event) AS (
    VALUES
    (1,1, 0 ) ,(1,2, 0 ) ,(1,3, 1 ) ,(1,4, 0 ) ,(1,5, 1 )
   ,(2,1, 0 ) ,(2,2, 1 )
   ,(3,1, 0 ) ,(3,2, 0 )
   )
, drag AS (
        SELECT id, zrow, event, MAX(event)
                OVER (PARTITION BY id
                        ORDER BY zrow
                        ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING
                        ) AS lagged
        FROM events ev
        )
SELECT id, zrow, event
        , COALESCE(lagged,0) AS oevent
        FROM drag dr
        ;

The same without the extra CTE:

WITH events(id, zrow,event) AS (
    VALUES
    (1,1, 0 ) ,(1,2, 0 ) ,(1,3, 1 ) ,(1,4, 0 ) ,(1,5, 1 )
   ,(2,1, 0 ) ,(2,2, 1 )
   ,(3,1, 0 ) ,(3,2, 0 )
   )
SELECT id, zrow, event, COALESCE(MAX(event) OVER (PARTITION BY id
                                                ORDER BY zrow
                                                ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING
                                ),0) AS lagged
        FROM events ev
        ;

Another way to perform the self-join would be to use a recursive query.

joop
  • 4,330
  • 1
  • 15
  • 26