sql >> Databáze >  >> RDS >> PostgreSQL

Přeskočte mezeru SQL přes konkrétní podmínky a správné použití lead().

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();



  1. Existuje / neexistuje:'vybrat 1' vs 'vybrat pole'

  2. Optimalizace při sloučení z datalinku Oracle

  3. Dapper.NET a uložený proc s více sadami výsledků

  4. Problém s vytvořením cizího klíče v Oracle