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

Funkcje okna lub wspólne wyrażenia tabelowe:policz poprzednie wiersze w zakresie

Nie sądzę, że można to zrobić tanio za pomocą zwykłego zapytania, CTE i funkcji okna – ich definicja ramki jest statyczna, ale potrzebujesz ramki dynamicznej (w zależności od wartości kolumn).

Ogólnie rzecz biorąc, musisz ostrożnie zdefiniować dolną i górną granicę swojego okna:Następujące zapytania wyklucz bieżący wiersz i uwzględnij dolna granica.
Nadal istnieje niewielka różnica:funkcja obejmuje poprzednich elementów równorzędnych bieżącego wiersza, podczas gdy skorelowane podzapytanie ich wyklucza...

Przypadek testowy

Korzystanie z ts zamiast słowa zastrzeżonego date jako nazwę kolumny.

CREATE TABLE test (
  id  bigint
, ts  timestamp
);

ROM - zapytanie Romana

Używaj CTE, agreguj znaczniki czasu w tablicę, rozmieszczaj, licz...
Prawdopodobnie wydajność drastycznie spada z więcej niż garścią rzędów. Jest tu kilku zabójców wydajności. Zobacz poniżej.

ARR - liczba elementów tablicy

Wziąłem zapytanie Romana i spróbowałem go nieco usprawnić:

  • Usuń drugie CTE, które nie jest konieczne.
  • Przekształć 1. CTE w podzapytanie, które jest szybsze.
  • Bezpośrednie count() zamiast ponownego agregowania w tablicę i liczenia za pomocą array_length() .

Ale obsługa macierzy jest kosztowna, a wydajność nadal pogarsza się z większą liczbą wierszy.

SELECT id, ts
     , (SELECT count(*)::int - 1
        FROM   unnest(dates) x
        WHERE  x >= sub.ts - interval '1h') AS ct
FROM (
   SELECT id, ts
        , array_agg(ts) OVER(ORDER BY ts) AS dates
   FROM   test
   ) sub;

COR — skorelowane podzapytanie

możesz rozwiąż to za pomocą prostego skorelowanego podzapytania. Dużo szybciej, ale nadal...

SELECT id, ts
     , (SELECT count(*)
        FROM   test t1
        WHERE  t1.ts >= t.ts - interval '1h'
        AND    t1.ts < t.ts) AS ct
FROM   test t
ORDER  BY ts;

FNC — funkcja

Zapętlaj wiersze w kolejności chronologicznej za pomocą row_number() w funkcji plpgsql i połącz to z kursorem w tym samym zapytaniu, obejmującym żądany przedział czasu. Następnie możemy po prostu odjąć numery wierszy:

CREATE OR REPLACE FUNCTION running_window_ct(_intv interval = '1 hour')
  RETURNS TABLE (id bigint, ts timestamp, ct int)
  LANGUAGE plpgsql AS
$func$
DECLARE
   cur   CURSOR FOR
         SELECT t.ts + _intv AS ts1, row_number() OVER (ORDER BY t.ts) AS rn
         FROM   test t ORDER BY t.ts;
   rec   record;
   rn    int;

BEGIN
   OPEN cur;
   FETCH cur INTO rec;
   ct := -1;  -- init

   FOR id, ts, rn IN
      SELECT t.id, t.ts, row_number() OVER (ORDER BY t.ts)
      FROM   test t ORDER BY t.ts
   LOOP
      IF rec.ts1 >= ts THEN
         ct := ct + 1;
      ELSE
         LOOP
            FETCH cur INTO rec;
            EXIT WHEN rec.ts1 >= ts;
         END LOOP;
         ct := rn - rec.rn;
      END IF;

      RETURN NEXT;
   END LOOP;
END
$func$;

Zadzwoń z domyślnym interwałem jednej godziny:

SELECT * FROM running_window_ct();

Lub z dowolną przerwą:

SELECT * FROM running_window_ct('2 hour - 3 second');

db<>graj tutaj
Stary sqlfiddle

Wzorzec

Korzystając z powyższej tabeli, przeprowadziłem szybki test porównawczy na moim starym serwerze testowym:(PostgreSQL 9.1.9 na Debianie).

-- TRUNCATE test;
INSERT INTO test
SELECT g, '2013-08-08'::timestamp
         + g * interval '5 min'
         + random() * 300 * interval '1 min' -- halfway realistic values
FROM   generate_series(1, 10000) g;

CREATE INDEX test_ts_idx ON test (ts);
ANALYZE test;  -- temp table needs manual analyze

Zmieniłem pogrubienie część dla każdego biegu i zdobyła 5 najlepszych dzięki EXPLAIN ANALYZE .

100 wierszy
ROM:27,656 ms
ARR:7,834 ms
COR:5,488 ms
FNC:1115 ms

1000 wierszy
ROM:2116,029 ms
ARR:189,679 ms
COR:65,802 ms
FNC:8,466 ms

5000 wierszy
ROM:51347 ms !!
ARR:3167 ms
COR:333 ms
FNC:42 ms

100000 wierszy
ROM:DNF
ARR:DNF
COR:6760 ms
FNC:828 ms

Funkcja jest wyraźnym zwycięzcą. Jest najszybszy o rząd wielkości i najlepiej skaluje się.
Obsługa macierzy nie może konkurować.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Jaka jest różnica między postgres a postgresql_psycopg2 jako silnikiem bazy danych dla django?

  2. Jak wdrożyć PostgreSQL na DigitalOcean

  3. Napraw „BŁĄD:brak wpisu klauzuli FROM dla tabeli” w PostgreSQL podczas korzystania z UNION, EXCEPT lub INTERSECT

  4. Pobierz rekordy, w których klucz kolumny json ma wartość null

  5. Dodanie nowej wartości do istniejącego typu ENUM