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