Jump SQL gap over specific condition & proper lead() usage

前端 未结 1 494
花落未央
花落未央 2021-01-16 12:30

(PostgreSQL 8.4) Continuing with my previous example, I wish to further my understanding of gaps-and-islands processing with Window-functions. Consider the

1条回答
  •  夕颜
    夕颜 (楼主)
    2021-01-16 13:19

    Query with window functions

    SELECT *
    FROM  (
       SELECT *
             ,lag(val, 1, 0)    OVER (PARTITION BY status ORDER BY id) AS last_val
             ,lag(status, 1, 0) OVER w2 AS last_status
             ,lag(next_id)      OVER w2 AS next_id_of_last_status
       FROM  (
          SELECT *, lead(id) OVER (PARTITION BY status ORDER BY id) AS next_id
          FROM   t1
          ) AS t
       WINDOW w2 AS (PARTITION BY val ORDER BY id)
      ) x
    WHERE (last_val <> val OR last_status <> status)
    AND   (status = 1 
           OR last_status = 1
              AND ((next_id_of_last_status > id) OR next_id_of_last_status IS NULL)
          )
    ORDER  BY id
    

    In addition to what we already had, we need valid OFF switches.

    An OFF switch if valid if the device was switched ON before (last_status = 1) and the next ON operation after that comes after the OFF switch in question (next_id_of_last_status > id).

    We have to provide for the special case that there is was the last ON operation, so we check for NULL in addition (OR next_id_of_last_status IS NULL).

    The next_id_of_last_status comes from the same window that we take last_status from. Therefore I introduced additional syntax for explicit window declaration, so I don't have to repeat myself:

    WINDOW w2 AS (PARTITION BY val ORDER BY id)
    

    And we need to get the next id for the last status in a subquery earlier (subquery t).

    If you've understood all that, you shouldn't have a problem slapping lead() on top of this query to get to your final destination. :)

    PL/pgSQL function

    Once it gets this complex, it's time to switch to procedural processing.

    This comparatively simple plpgsql function nukes the performance of the complex window function query, for the simple reason that it has to scan the whole table only once.

    CREATE OR REPLACE FUNCTION valid_t1 (OUT t t1)  -- row variable of table type
      RETURNS SETOF t1 LANGUAGE plpgsql AS
    $func$
    DECLARE
       _last_on int := -1;  -- init with impossible value
    BEGIN
    
    FOR t IN
       SELECT * FROM t1 ORDER BY id
    LOOP
       IF t.status = 1 THEN
          IF _last_on <> t.val THEN
             RETURN NEXT;
             _last_on := t.val;
          END IF;
       ELSE
          IF _last_on = t.val THEN
             RETURN NEXT;
             _last_on := -1;
          END IF;
       END IF;
    END LOOP;
    
    END
    $func$;
    

    Call:

    SELECT * FROM valid_t1();
    

    0 讨论(0)
提交回复
热议问题