Abfrage mit Fensterfunktionen
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
Zusätzlich zu was wir bereits hatten , brauchen wir gültige AUS-Schalter.
Ein OFF
Schalter falls gültig, wenn das Gerät ON
war davor (last_status = 1
) und das nächste ON
Betrieb danach kommt nach dem OFF
betreffenden Schalter (next_id_of_last_status > id
).
Wir müssen für den Sonderfall sorgen, dass der letzte ON
war Operation, also prüfen wir auf NULL
zusätzlich (OR next_id_of_last_status IS NULL
).
Der next_id_of_last_status
kommt aus demselben Fenster, aus dem wir last_status
nehmen aus. Deshalb habe ich eine zusätzliche Syntax für die explizite Fensterdeklaration eingeführt, damit ich mich nicht wiederholen muss:
WINDOW w2 AS (PARTITION BY val ORDER BY id)
Und wir müssen die nächste ID für den letzten Status in einer früheren Unterabfrage erhalten (Unterabfrage t
).
Wenn Sie das alles verstanden haben , sollten Sie kein Problem damit haben, lead()
zu schlagen zusätzlich zu dieser Abfrage, um zu Ihrem endgültigen Ziel zu gelangen. :)
PL/pgSQL-Funktion
Sobald es so komplex wird, ist es an der Zeit, zur prozeduralen Verarbeitung überzugehen.
Diese vergleichsweise einfache plpgsql-Funktion verringert die Leistung der komplexen Fensterfunktion query, aus dem einfachen Grund, dass sie die gesamte Tabelle nur einmal scannen muss.
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$;
Aufruf:
SELECT * FROM valid_t1();