Query met vensterfuncties
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
Naast wat we al hadden , we hebben geldige UIT-schakelaars nodig.
Een OFF
schakelaar indien geldig als het apparaat ON
was geschakeld voor (last_status = 1
) en de volgende ON
bewerking daarna komt na de OFF
schakelaar in kwestie (next_id_of_last_status > id
).
We moeten zorgen voor het speciale geval dat er de laatste ON
. was bewerking, dus we controleren op NULL
daarnaast (OR next_id_of_last_status IS NULL
).
De next_id_of_last_status
komt uit hetzelfde venster dat we nemen last_status
van. Daarom heb ik extra syntaxis geïntroduceerd voor expliciete vensterdeclaratie, zodat ik mezelf niet hoef te herhalen:
WINDOW w2 AS (PARTITION BY val ORDER BY id)
En we moeten de volgende id voor de laatste status in een eerdere subquery ophalen (subquery t
).
Als je alles dat hebt begrepen , zou je geen probleem moeten hebben om lead()
te slaan bovenop deze vraag om uw eindbestemming te bereiken. :)
PL/pgSQL-functie
Zodra het zo complex wordt, is het tijd om over te schakelen naar procedurele verwerking.
Deze relatief eenvoudige plpgsql-functie vernietigt de prestaties van de complexe vensterfunctiequery, om de eenvoudige reden dat het de hele tabel maar één keer hoeft te scannen.
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$;
Bel:
SELECT * FROM valid_t1();