PostgreSQL
 sql >> Database >  >> RDS >> PostgreSQL

Salta il divario SQL su condizioni specifiche e utilizzo corretto di lead()

Query con le funzioni della finestra

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

Oltre a quello che avevamo già , abbiamo bisogno di interruttori OFF validi.

Un OFF switch se valido se il dispositivo è stato acceso ON prima (last_status = 1 ) e il successivo ON l'operazione segue dopo il OFF interruttore in questione (next_id_of_last_status > id ).

Dobbiamo prevedere per il caso speciale che c'è stato l'ultimo ON operazione, quindi controlliamo NULL inoltre (OR next_id_of_last_status IS NULL ).

Il next_id_of_last_status viene dalla stessa finestra che prendiamo last_status da. Pertanto ho introdotto una sintassi aggiuntiva per la dichiarazione esplicita della finestra, quindi non devo ripetermi:

WINDOW w2 AS (PARTITION BY val ORDER BY id)

E dobbiamo ottenere l'ID successivo per l'ultimo stato in una sottoquery in precedenza (subquery t ).

Se hai capito tutto quello , non dovresti avere problemi a schiaffeggiare lead() in cima a questa query per raggiungere la tua destinazione finale. :)

Funzione PL/pgSQL

Una volta che diventa così complesso, è tempo di passare all'elaborazione procedurale.

Questa funzione plpgsql relativamente semplice annulla le prestazioni della complessa query della funzione finestra, per il semplice motivo che deve scansionare l'intera tabella solo una volta.

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

Chiama:

SELECT * FROM valid_t1();