sql >> Database >  >> RDS >> PostgreSQL

PostgreSQL-vensterfunctie:partitie in vergelijking

Door verschillende vensterfuncties en twee subquery's te gebruiken, zou dit behoorlijk snel moeten werken:

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;

Met behulp van ts als naam voor de tijdstempelkolom.
Ervan uitgaande dat ts uniek zijn - en geïndexeerd (een unieke beperking doet dat automatisch).

In een test met een echte tabel met 50k rijen was er slechts een enkele indexscan nodig . Dus zou behoorlijk snel moeten zijn, zelfs met grote tafels. Ter vergelijking:uw zoekopdracht met join / distinct is niet na een minuut voltooid (zoals verwacht).
Zelfs een geoptimaliseerde versie, die met één cross join tegelijk te maken heeft (de linker join met nauwelijks een beperkende voorwaarde is in feite een beperkte cross join) was na een minuut niet klaar.

Voor de beste prestaties met een grote tafel, stem je je geheugeninstellingen af, in het bijzonder voor work_mem (voor grote sorteerbewerkingen). Overweeg om deze tijdelijk (veel) hoger voor uw sessie in te stellen als u het RAM-geheugen kunt sparen. Lees hier en hier meer.

Hoe?

  1. In subquery sub1 kijk naar de gebeurtenis uit de vorige rij en bewaar die alleen als deze is gewijzigd, en markeer zo het eerste element van een nieuwe groep. Haal tegelijkertijd de id . op van de vorige en de volgende rij (pre_id , post_id ).

  2. In subquery sub2 , count() telt alleen niet-null-waarden. De resulterende grp markeert peers in blokken van opeenvolgende dezelfde gebeurtenissen.

  3. In de laatste SELECT , neem de eerste pre_id en de laatste post_id per groep voor elke rij om tot het gewenste resultaat te komen.
    Eigenlijk zou dit nog sneller moeten zijn in de buitenste SELECT :

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

    ... aangezien de sorteervolgorde van het venster overeenkomt met het venster voor pre_id , dus er is maar één sortering nodig. Een snelle test lijkt het te bevestigen. Meer over deze framedefinitie.

SQL Fiddle.




  1. sqlite geretourneerd:foutcode =1, msg =geen dergelijke kolom:keuken1

  2. Hoe alle verbonden subgrafieken van een ongerichte grafiek te vinden?

  3. MySQL zoeken en tekst in een veld vervangen

  4. Hoe importeer ik bestaande *.sql-bestanden in PostgreSQL 8.4?