Dotaz s funkcemi 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
Kromě toho, co jsme již měli , potřebujeme platné vypínače.
OFF
přepínač, pokud je platný, pokud bylo zařízení zapnuto ON
před (last_status = 1
) a další ON
operace poté následuje po OFF
dotyčný přepínač (next_id_of_last_status > id
).
Musíme zajistit speciální případ, kdy byl poslední ON
operace, takže zkontrolujeme NULL
navíc (OR next_id_of_last_status IS NULL
).
next_id_of_last_status
pochází ze stejného okna, ve kterém bereme last_status
z. Proto jsem zavedl další syntaxi pro explicitní deklaraci okna, takže se nemusím opakovat:
WINDOW w2 AS (PARTITION BY val ORDER BY id)
A potřebujeme získat další ID pro poslední stav v poddotazu dříve (poddotaz t
).
Pokud jste tomu všemu rozuměli , neměli byste mít problém plácnout lead()
nad tímto dotazem, abyste se dostali do svého konečného cíle. :)
Funkce PL/pgSQL
Jakmile se to stane takto komplexní, je čas přejít na procedurální zpracování.
Tato poměrně jednoduchá funkce plpgsql nukleuje výkon komplexního dotazu na funkci okna, a to z jednoduchého důvodu, že musí prohledat celou tabulku pouze jednou.
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$;
Volejte:
SELECT * FROM valid_t1();