PostgreSQL
 sql >> Cơ Sở Dữ Liệu >  >> RDS >> PostgreSQL

Chức năng cửa sổ hoặc Biểu thức bảng thông thường:đếm các hàng trước đó trong phạm vi

Tôi không nghĩ rằng bạn có thể làm điều này một cách rẻ tiền với một truy vấn đơn giản, CTE và các hàm cửa sổ - định nghĩa khung của chúng là tĩnh, nhưng bạn cần khung động (tùy thuộc vào giá trị cột).

Nói chung, bạn sẽ phải xác định cận dưới và giới hạn trên của cửa sổ một cách cẩn thận:Các truy vấn sau đây loại trừ hàng hiện tại và bao gồm đường viền dưới.
Vẫn có một sự khác biệt nhỏ:hàm bao gồm các hàm tương tự trước đó của hàng hiện tại, trong khi truy vấn con tương quan loại trừ chúng ...

Trường hợp thử nghiệm

Sử dụng ts thay vì từ dành riêng date dưới dạng tên cột.

CREATE TABLE test (
  id  bigint
, ts  timestamp
);

ROM - Truy vấn của Roman

Sử dụng CTE, tổng hợp dấu thời gian thành một mảng, không tốt nhất, đếm ...
Mặc dù đúng, hiệu suất giảm mạnh với hơn một bàn tay đầy hàng. Có một vài sát thủ hiệu suất ở đây. Xem bên dưới.

ARR - đếm phần tử mảng

Tôi lấy truy vấn của Roman và cố gắng sắp xếp hợp lý một chút:

  • Xóa CTE thứ 2 không cần thiết.
  • Chuyển đổi CTE đầu tiên thành truy vấn con, nhanh hơn.
  • count() trực tiếp thay vì tổng hợp lại thành một mảng và đếm với array_length() .

Tuy nhiên, việc xử lý mảng rất tốn kém và hiệu suất vẫn giảm sút nghiêm trọng với nhiều hàng hơn.

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 - truy vấn con tương quan

Bạn có thể giải quyết nó bằng một truy vấn con tương quan đơn giản. Nhanh hơn rất nhiều, nhưng vẫn ...

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 - Chức năng

Lặp lại các hàng theo thứ tự thời gian với row_number() trong hàm plpgsql và kết hợp con trỏ đó với con trỏ trên cùng một truy vấn, trải dài trong khung thời gian mong muốn. Sau đó, chúng ta chỉ có thể trừ số hàng:

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

Gọi với khoảng thời gian mặc định là một giờ:

SELECT * FROM running_window_ct();

Hoặc với bất kỳ khoảng thời gian nào:

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

db <> fiddle here
Sqlfiddle cũ

Điểm chuẩn

Với bảng từ trên, tôi đã chạy một điểm chuẩn nhanh trên máy chủ thử nghiệm cũ của mình:(PostgreSQL 9.1.9 trên 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

Tôi đã thay đổi in đậm phần cho mỗi lần chạy và chiếm phần tốt nhất trong số 5 phần với EXPLAIN ANALYZE .

100 hàng
ROM:27,656 ms
ARR:7,834 ms
COR:5,488 ms
FNC: 1,115 ms

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

5000 hàng
ROM:51347 ms !!
ARR:3167 ms
COR:333 ms
FNC: 42 ms

100000 hàng
ROM:DNF
ARR:DNF
COR:6760 ms
FNC: 828 ms

Chức năng là người chiến thắng rõ ràng. Nó là nhanh nhất theo thứ tự độ lớn và tỷ lệ tốt nhất.
Xử lý mảng không thể cạnh tranh.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. PostgreSQL:Tạm thời vô hiệu hóa các kết nối

  2. Cấu hình PostgreSQL để có khả năng quan sát

  3. gem install pg --with-pg-config hoạt động, gói không thành công

  4. Xuất Bảng AWS Postgres RDS sang AWS S3

  5. Truy vấn PostgreSQL chạy nhanh hơn với quét chỉ mục, nhưng công cụ chọn tham gia băm