Requête avec les fonctions de fenêtre
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
En plus de ce que nous avions déjà , nous avons besoin d'interrupteurs OFF valides.
Un OFF
basculer si valide si l'appareil a été allumé ON
avant (last_status = 1
) et le prochain ON
opération après qui vient après le OFF
switch en question (next_id_of_last_status > id
).
Nous devons prévoir le cas particulier où il y a eu le dernier ON
opération, nous vérifions donc NULL
en plus (OR next_id_of_last_status IS NULL
).
Le next_id_of_last_status
vient de la même fenêtre que nous prenons last_status
de. Par conséquent, j'ai introduit une syntaxe supplémentaire pour la déclaration de fenêtre explicite, afin de ne pas avoir à me répéter :
WINDOW w2 AS (PARTITION BY val ORDER BY id)
Et nous devons obtenir le prochain identifiant pour le dernier statut dans une sous-requête plus tôt (sous-requête t
).
Si vous avez tout compris ça , vous ne devriez pas avoir de problème pour taper lead()
en plus de cette requête pour arriver à votre destination finale. :)
Fonction PL/pgSQL
Une fois que cela devient aussi complexe, il est temps de passer au traitement procédural.
Cette fonction plpgsql relativement simple réduit les performances de la requête de fonction de fenêtre complexe, pour la simple raison qu'elle n'a à parcourir l'ensemble de la table qu'une seule fois.
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$;
Appel :
SELECT * FROM valid_t1();