PostgreSQL
 sql >> Teknologi Basis Data >  >> RDS >> PostgreSQL

Fungsi Jendela atau Ekspresi Tabel Umum:hitung baris sebelumnya dalam jangkauan

Saya tidak berpikir Anda dapat melakukan ini dengan murah dengan kueri biasa, CTE, dan fungsi jendela - definisi bingkainya statis, tetapi Anda memerlukan bingkai dinamis (tergantung pada nilai kolom).

Umumnya, Anda harus menentukan batas bawah dan atas jendela dengan hati-hati:Kueri berikut kecualikan baris saat ini dan sertakan batas bawah.
Masih ada sedikit perbedaan:fungsi menyertakan rekan sebelumnya dari baris saat ini, sedangkan subkueri terkait mengecualikannya ...

Kasus uji

Menggunakan ts alih-alih kata date yang dicadangkan sebagai nama kolom.

CREATE TABLE test (
  id  bigint
, ts  timestamp
);

ROM - permintaan Roman

Gunakan CTE, gabungkan stempel waktu ke dalam larik, hapus sarang, hitung ...
Meskipun benar, kinerja menurun secara drastis dengan lebih dari satu tangan penuh baris. Ada beberapa pembunuh kinerja di sini. Lihat di bawah.

ARR - menghitung elemen larik

Saya mengambil kueri Roman dan mencoba menyederhanakannya sedikit:

  • Hapus CTE ke-2 yang tidak perlu.
  • Ubah CTE ke-1 menjadi subkueri, yang lebih cepat.
  • Langsung count() alih-alih menggabungkan kembali ke dalam array dan menghitung dengan array_length() .

Namun penanganan array itu mahal, dan kinerjanya masih memburuk dengan lebih banyak baris.

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 - subkueri terkait

Anda bisa menyelesaikannya dengan subquery berkorelasi sederhana. Jauh lebih cepat, tapi tetap saja ...

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 - Fungsi

Ulangi baris dalam urutan kronologis dengan row_number() di fungsi plpgsql dan gabungkan itu dengan kursor atas kueri yang sama, mencakup kerangka waktu yang diinginkan. Kemudian kita cukup mengurangi angka baris:

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

Panggilan dengan interval default satu jam:

SELECT * FROM running_window_ct();

Atau dengan interval apa pun:

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

db<>main biola di sini
sqlfiddle lama

Tolok ukur

Dengan tabel di atas, saya menjalankan benchmark cepat di server pengujian lama saya:(PostgreSQL 9.1.9 di Debian).

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

Saya memvariasikan tebal bagian untuk setiap lari dan ambil yang terbaik dari 5 dengan EXPLAIN ANALYZE .

100 baris
ROM:27.656 ms
ARR:7.834 ms
COR:5.488 ms
FNC:1.115 ms

1000 baris
ROM:2116.029 ms
ARR:189.679 ms
COR:65.802 ms
FNC:8.466 ms

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

100000 baris
ROM:DNF
ARR:DNF
COR:6760 ms
FNC:828 md

Fungsinya adalah pemenang yang jelas. Ini tercepat menurut urutan besarnya dan skala terbaik.
Penanganan array tidak dapat bersaing.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Bagaimana cara menambahkan kunci utama peningkatan otomatis ke tabel yang ada, di PostgreSQL?

  2. Penyediaan Sendiri Akun Pengguna di PostgreSQL melalui Akses Anonim Tanpa Hak

  3. Bagaimana cara menggunakan ANY alih-alih IN dalam klausa WHERE dengan Rails?

  4. Gambaran Umum Kolom yang Dihasilkan untuk PostgreSQL

  5. pindahkan data dari satu tabel ke tabel lainnya, edisi postgresql