sql >> Database >  >> RDS >> PostgreSQL

Sprong SQL-kloof over specifieke voorwaarde en correct gebruik van lead()

Query met vensterfuncties

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

Naast wat we al hadden , we hebben geldige UIT-schakelaars nodig.

Een OFF schakelaar indien geldig als het apparaat ON was geschakeld voor (last_status = 1 ) en de volgende ON bewerking daarna komt na de OFF schakelaar in kwestie (next_id_of_last_status > id ).

We moeten zorgen voor het speciale geval dat er de laatste ON . was bewerking, dus we controleren op NULL daarnaast (OR next_id_of_last_status IS NULL ).

De next_id_of_last_status komt uit hetzelfde venster dat we nemen last_status van. Daarom heb ik extra syntaxis geïntroduceerd voor expliciete vensterdeclaratie, zodat ik mezelf niet hoef te herhalen:

WINDOW w2 AS (PARTITION BY val ORDER BY id)

En we moeten de volgende id voor de laatste status in een eerdere subquery ophalen (subquery t ).

Als je alles dat hebt begrepen , zou je geen probleem moeten hebben om lead() te slaan bovenop deze vraag om uw eindbestemming te bereiken. :)

PL/pgSQL-functie

Zodra het zo complex wordt, is het tijd om over te schakelen naar procedurele verwerking.

Deze relatief eenvoudige plpgsql-functie vernietigt de prestaties van de complexe vensterfunctiequery, om de eenvoudige reden dat het de hele tabel maar één keer hoeft te scannen.

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

Bel:

SELECT * FROM valid_t1();



  1. Hoe importeer ik MySql Connector in Unity Project?

  2. Wat is een vergelijkingsoperator?

  3. OPENQUERY gebruiken met een Oracle-database om een ​​datumbereik te doorzoeken

  4. Wijzig het scheidingsteken in een komma in SQLite-queryresultaten