sql >> Database teknologi >  >> RDS >> PostgreSQL

PostgreSQL vinduesfunktion:partition ved sammenligning

Ved at bruge flere forskellige vinduesfunktioner og to underforespørgsler burde dette fungere anstændigt hurtigt:

WITH events(id, event, ts) AS (
  VALUES
   (1, 12, '2014-03-19 08:00:00'::timestamp)
  ,(2, 12, '2014-03-19 08:30:00')
  ,(3, 13, '2014-03-19 09:00:00')
  ,(4, 13, '2014-03-19 09:30:00')
  ,(5, 12, '2014-03-19 10:00:00')
   )
SELECT first_value(pre_id)  OVER (PARTITION BY grp ORDER BY ts)      AS pre_id
     , id, ts
     , first_value(post_id) OVER (PARTITION BY grp ORDER BY ts DESC) AS post_id
FROM  (
   SELECT *, count(step) OVER w AS grp
   FROM  (
      SELECT id, ts
           , NULLIF(lag(event) OVER w, event) AS step
           , lag(id)  OVER w AS pre_id
           , lead(id) OVER w AS post_id
      FROM   events
      WINDOW w AS (ORDER BY ts)
      ) sub1
   WINDOW w AS (ORDER BY ts)
   ) sub2
ORDER  BY ts;

Brug af ts som navn for tidsstempelkolonnen.
Under forudsætning af ts at være unik - og indekseret (en unik begrænsning gør det automatisk).

I en test med en virkelig tabel med 50.000 rækker behøvede den kun en enkelt indeksscanning . Så bør være anstændigt hurtig selv med store borde. Til sammenligning blev din forespørgsel med join / distinct ikke afsluttet efter et minut (som forventet).
Selv en optimeret version, der handler om én krydssammenføjning ad gangen (den venstre sammenføjning med næppe en begrænsende betingelse er faktisk en begrænset cross join) blev ikke færdig efter et minut.

For at få den bedste ydeevne med et stort bord skal du justere dine hukommelsesindstillinger, især for work_mem (til store sorteringsoperationer). Overvej at sætte den (meget) højere for din session midlertidigt, hvis du kan spare RAM. Læs mere her og her.

Hvordan?

  1. I underforespørgsel sub1 se på begivenheden fra den forrige række og behold kun den, hvis den har ændret sig, og markerer dermed det første element i en ny gruppe. Få samtidig id af den forrige og den næste række (pre_id , post_id ).

  2. I underforespørgsel sub2 , count() tæller kun ikke-nul værdier. Den resulterende grp markerer jævnaldrende i blokke af på hinanden følgende samme begivenheder.

  3. I den sidste SELECT , tag det første pre_id og den sidste post_id gruppe for hver række for at nå frem til det ønskede resultat.
    Det burde faktisk være endnu hurtigere i den ydre SELECT :

     last_value(post_id) OVER (PARTITION BY grp ORDER BY ts
                               RANGE BETWEEN UNBOUNDED PRECEDING
                                     AND     UNBOUNDED FOLLOWING) AS post_id
    

    ... da sorteringsrækkefølgen af ​​vinduet stemmer overens med vinduet for pre_id , så kun en enkelt sortering er nødvendig. En hurtig test ser ud til at bekræfte det. Mere om denne rammedefinition.

SQL Fiddle.




  1. Kan vi omfordele Oracle tools.jar?

  2. Cloud Vendor Deep-Dive:PostgreSQL på Microsoft Azure

  3. Løsninger til INSERT OR UPDATE på SQL Server

  4. Tjek, om et objekt er en tabel, visning eller lagret procedure i SQL Server ved hjælp af OBJECTPROPERTY()-funktionen