Zapytanie z funkcjami okna
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
Oprócz co już mieliśmy , potrzebujemy prawidłowych wyłączników OFF.
OFF
przełącznik, jeśli jest ważny, jeśli urządzenie zostało włączone ON
przed (last_status = 1
) i następny ON
operacja po tym następuje po OFF
przełącznik, o którym mowa (next_id_of_last_status > id
).
Musimy uwzględnić szczególny przypadek, w którym wystąpił ostatni ON
operacja, więc sprawdzamy, czy NULL
dodatkowo (OR next_id_of_last_status IS NULL
).
next_id_of_last_status
pochodzi z tego samego okna, co last_status
z. Dlatego wprowadziłem dodatkową składnię dla jawnej deklaracji okna, więc nie muszę się powtarzać:
WINDOW w2 AS (PARTITION BY val ORDER BY id)
I musimy uzyskać następny identyfikator dla ostatniego statusu w podzapytaniu wcześniej (podzapytanie t
).
Jeśli wszystko zrozumiałeś, to , nie powinieneś mieć problemu z uderzeniem w lead()
na górze tego zapytania, aby dostać się do miejsca docelowego. :)
Funkcja PL/pgSQL
Kiedy robi się tak złożona, nadszedł czas, aby przejść na przetwarzanie proceduralne.
Ta stosunkowo prosta funkcja plpgsql nukuje wydajność złożonego zapytania funkcji okna z tego prostego powodu, że musi przeskanować całą tabelę tylko raz.
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$;
Zadzwoń:
SELECT * FROM valid_t1();