PostgreSQL
 sql >> Baza danych >  >> RDS >> PostgreSQL

Funkcja okna PostgreSQL:partycja przez porównanie

Używając kilku różnych funkcji okna i dwóch podzapytań, powinno to działać całkiem szybko:

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;

Korzystanie z ts jako nazwę kolumny sygnatury czasowej.
Zakładając ts być unikalnym - i indeksowanym (unikalne ograniczenie robi to automatycznie).

W teście z rzeczywistą tabelą zawierającą 50 tys. wierszy wystarczyło pojedyncze skanowanie indeksu . Więc powinno być przyzwoicie szybkie nawet przy dużych stołach. Dla porównania, twoje zapytanie z join / different nie zakończyło się po minucie (zgodnie z oczekiwaniami).
Nawet zoptymalizowana wersja, obsługująca jedno sprzężenie krzyżowe na raz (lewe sprzężenie prawie nie ograniczające jest skutecznie ograniczone połączenie krzyżowe) nie zakończyło się po minucie.

Aby uzyskać najlepszą wydajność przy dużym stole, dostosuj ustawienia pamięci, w szczególności dla work_mem (dla dużych operacji sortowania). Rozważ tymczasowe ustawienie jej (znacznie) wyższej dla swojej sesji, jeśli możesz oszczędzić pamięć RAM. Przeczytaj więcej tutaj i tutaj.

Jak?

  1. W podzapytaniu sub1 spójrz na wydarzenie z poprzedniego wiersza i zachowaj je tylko wtedy, gdy się zmieniło, zaznaczając w ten sposób pierwszy element nowej grupy. W tym samym czasie uzyskaj id poprzedniego i następnego wiersza (pre_id , post_id ).

  2. W podzapytaniu sub2 , count() zlicza tylko wartości inne niż null. Wynikowy grp zaznacza rówieśników w blokach następujących po sobie tych samych wydarzeń.

  3. W końcowym SELECT , weź pierwszy pre_id i ostatni post_id na grupę dla każdego wiersza, aby uzyskać pożądany wynik.
    Właściwie powinno to być jeszcze szybsze w zewnętrznym SELECT :

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

    ... ponieważ porządek sortowania okna zgadza się z oknem dla pre_id , więc potrzebny jest tylko jeden rodzaj. Szybki test zdaje się to potwierdzać. Więcej o tej definicji ramki.

Skrzypce SQL.




  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Jak używać funkcji podciągów w PostgreSQL i Redshift

  2. USUŃ KASKADOWE tylko raz

  3. Zmodyfikuj wartość początkową AutoField Django

  4. Statystyki zasięgu kodu

  5. Jak ustawić automatyczny przyrost klucza podstawowego w PostgreSQL?