sql >> Databasteknik >  >> RDS >> PostgreSQL

Hoppa SQL-gap över specifikt tillstånd och korrekt användning av lead().

Fråga med fönsterfunktioner

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

Förutom det vi redan hade , vi behöver giltiga AV-brytare.

En OFF växla om giltigt om enheten var ON före (last_status = 1 ) och nästa ON operation efter det kommer efter OFF växeln i fråga (next_id_of_last_status > id ).

Vi måste ta hänsyn till det speciella fallet att det var den senaste ON operation, så vi letar efter NULL dessutom (OR next_id_of_last_status IS NULL ).

next_id_of_last_status kommer från samma fönster som vi tar last_status från. Därför introducerade jag ytterligare syntax för explicit fönsterdeklaration, så jag behöver inte upprepa mig:

WINDOW w2 AS (PARTITION BY val ORDER BY id)

Och vi måste få nästa id för den senaste statusen i en underfråga tidigare (underfråga t ).

Om du har förstått allt det , du borde inte ha några problem med att slå lead() ovanpå denna fråga för att komma till din slutdestination. :)

PL/pgSQL-funktion

När det blir så här komplext är det dags att byta till processuell bearbetning.

Denna jämförelsevis enkla plpgsql-funktion kärnar ur prestandan för den komplexa fönsterfunktionsfrågan, av den enkla anledningen att den bara måste skanna hela tabellen en gång.

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$;

Ring:

SELECT * FROM valid_t1();



  1. Hur man får månaden från ett datum i T-SQL

  2. Hur får jag kolumnnamn från en given MySQL-tabell?

  3. Hittar fråga från oracle som blockerar sessionen

  4. Har problem med php och ajax sökfunktion