Forespørgsel med vinduesfunktioner
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
Ud over hvad vi allerede havde , vi har brug for gyldige OFF-kontakter.
En OFF
skift, hvis gyldigt, hvis enheden blev tændt ON
før (last_status = 1
) og den næste ON
operation efter det kommer efter OFF
den pågældende switch (next_id_of_last_status > id
).
Vi er nødt til at sørge for det specielle tilfælde, at der var den sidste ON
operation, så vi tjekker efter NULL
derudover (OR next_id_of_last_status IS NULL
).
next_id_of_last_status
kommer fra det samme vindue, som vi tager last_status
fra. Derfor introducerede jeg yderligere syntaks for eksplicit vindueserklæring, så jeg ikke behøver at gentage mig selv:
WINDOW w2 AS (PARTITION BY val ORDER BY id)
Og vi skal have det næste id for den sidste status i en underforespørgsel tidligere (underforespørgsel t
).
Hvis du har forstået alt det , burde du ikke have problemer med at slå lead()
oven på denne forespørgsel for at komme til din endelige destination. :)
PL/pgSQL-funktion
Når det bliver så komplekst, er det tid til at skifte til proceduremæssig behandling.
Denne forholdsvis simple plpgsql-funktion Nukes ydeevnen af den komplekse vinduesfunktionsforespørgsel, af den simple grund, at den kun skal scanne hele tabellen én gang.
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$;
Ring til:
SELECT * FROM valid_t1();