PostgreSQL
 sql >> Base de Dados >  >> RDS >> PostgreSQL

Salte a lacuna do SQL sobre a condição específica e o uso adequado do lead()

Consulta com funções de janela

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

Além do o que já tínhamos , precisamos de chaves OFF válidas.

Um OFF switch se válido se o dispositivo foi ligado ON antes (last_status = 1 ) e o próximo ON operação depois que vem depois do OFF switch em questão (next_id_of_last_status > id ).

Temos que prever o caso especial de que foi o último ON operação, então verificamos se há NULL além disso (OR next_id_of_last_status IS NULL ).

O next_id_of_last_status vem da mesma janela que pegamos last_status a partir de. Portanto, introduzi sintaxe adicional para declaração de janela explícita, para não precisar me repetir:
WINDOW w2 AS (PARTITION BY val ORDER BY id)

E precisamos obter o próximo id para o último status em uma subconsulta anterior (subconsulta t ).

Se você entendeu tudo isso , você não deve ter problemas em dar um tapa em lead() em cima desta consulta para chegar ao seu destino final. :)

Função PL/pgSQL


Uma vez que fica tão complexo, é hora de mudar para o processamento processual.

Esta função plpgsql comparativamente simples elimina o desempenho da consulta de função de janela complexa, pelo simples motivo de que ela precisa varrer toda a tabela apenas uma vez.
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$;

Ligar:
SELECT * FROM valid_t1();