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

Chức năng cửa sổ PostgreSQL:phân vùng theo so sánh

Sử dụng một số chức năng cửa sổ khác nhau và hai truy vấn con, điều này sẽ hoạt động rất nhanh:

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;

Sử dụng ts làm tên cho cột dấu thời gian.
Giả sử ts là duy nhất - và được lập chỉ mục (một ràng buộc duy nhất thực hiện điều đó tự động).

Trong một thử nghiệm với bảng ngoài đời thực có 50k hàng, nó chỉ cần một lần quét chỉ mục . Vì vậy, nên nhanh chóng ngay cả với các bảng lớn. Trong khi đó, truy vấn của bạn với tham gia / phân biệt đã không kết thúc sau một phút (như mong đợi).
Ngay cả phiên bản được tối ưu hóa, xử lý một liên kết chéo tại một thời điểm (liên kết bên trái hầu như không có điều kiện giới hạn cũng là một giới hạn tham gia chéo) đã không kết thúc sau một phút.

Để có hiệu suất tốt nhất với một bảng lớn, hãy điều chỉnh cài đặt bộ nhớ của bạn, đặc biệt cho work_mem (đối với các phép toán sắp xếp lớn). Tạm thời cân nhắc đặt nó (nhiều) cao hơn cho phiên của bạn nếu bạn có thể dự phòng RAM. Đọc thêm tại đây và tại đây.

Làm thế nào?

  1. Trong truy vấn con sub1 nhìn vào sự kiện từ hàng trước đó và chỉ giữ lại sự kiện đó nếu nó đã thay đổi, do đó đánh dấu phần tử đầu tiên của một nhóm mới. Đồng thời, lấy id của hàng trước và hàng tiếp theo (pre_id , post_id ).

  2. Trong truy vấn con sub2 , count() chỉ đếm các giá trị không rỗng. grp kết quả đánh dấu các đồng nghiệp trong các khối các sự kiện giống nhau liên tiếp.

  3. Trong SELECT cuối cùng , lấy pre_id đầu tiên và post_id cuối cùng mỗi nhóm để mỗi hàng đi đến kết quả mong muốn.
    Thực ra, điều này sẽ nhanh hơn trong SELECT bên ngoài :

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

    ... vì thứ tự sắp xếp của cửa sổ đồng ý với cửa sổ cho pre_id , vì vậy chỉ cần một loại duy nhất. Một bài kiểm tra nhanh dường như xác nhận điều đó. Tìm hiểu thêm về định nghĩa khung này.

SQL Fiddle.




  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. PostGIS Đang hoạt động

  2. Cài đặt PL / Java 1.5.2 trong PostgreSQL 11

  3. Làm thế nào để liệt kê các bản ghi có ngày từ 10 ngày qua?

  4. Cách theo dõi Hiệu suất PostgreSQL 12 với OmniDB - Phần 1

  5. Sắp xếp chữ và số với PostgreSQL