Làm cách nào để có được tổng hợp của một chức năng cửa sổ trong Postgres?


11

Tôi có một bảng chứa hai cột hoán vị / kết hợp các mảng số nguyên và cột thứ ba chứa một giá trị, như vậy:

CREATE TABLE foo
(
  perm integer[] NOT NULL,
  combo integer[] NOT NULL,
  value numeric NOT NULL DEFAULT 0
);
INSERT INTO foo
VALUES
( '{3,1,2}', '{1,2,3}', '1.1400' ),
( '{3,1,2}', '{1,2,3}', '0' ),
( '{3,1,2}', '{1,2,3}', '1.2680' ),
( '{3,1,2}', '{1,2,3}', '0' ),
( '{3,1,2}', '{1,2,3}', '1.2680' ),
( '{3,1,2}', '{1,2,3}', '0' ),
( '{3,1,2}', '{1,2,3}', '0' ),
( '{3,1,2}', '{1,2,3}', '1.2680' ),
( '{3,1,2}', '{1,2,3}', '0.9280' ),
( '{3,1,2}', '{1,2,3}', '0' ),
( '{3,1,2}', '{1,2,3}', '1.2680' ),
( '{3,1,2}', '{1,2,3}', '0' ),
( '{3,1,2}', '{1,2,3}', '0' ),
( '{3,1,2}', '{1,2,3}', '1.2680' ),
( '{3,1,2}', '{1,2,3}', '0' ),
( '{3,2,1}', '{1,2,3}', '0' ),
( '{3,2,1}', '{1,2,3}', '0.8000' )

Tôi muốn tìm ra độ lệch trung bình và độ lệch chuẩn cho mỗi hoán vị, cũng như cho mỗi kết hợp. Tôi có thể làm điều đó với truy vấn này:

SELECT
  f1.perm,
  f2.combo,
  f1.perm_average_value,
  f2.combo_average_value,
  f1.perm_stddev,
  f2.combo_stddev,
  f1.perm_count,
  f2.combo_count
FROM
(
  SELECT
    perm,
    combo,
    avg( value ) AS perm_average_value,
    stddev_pop( value ) AS perm_stddev,
    count( * ) AS perm_count
  FROM foo
  GROUP BY perm, combo
) AS f1
JOIN
(
  SELECT
    combo,
    avg( value ) AS combo_average_value,
    stddev_pop( value ) AS combo_stddev,
    count( * ) AS combo_count
  FROM foo
  GROUP BY combo
) AS f2 ON ( f1.combo = f2.combo );

Tuy nhiên, truy vấn đó có thể trở nên khá chậm khi tôi có nhiều dữ liệu, bởi vì bảng "foo" (trong thực tế, bao gồm 14 phân vùng, mỗi phân vùng có khoảng 4 triệu hàng) cần được quét hai lần.

Gần đây, tôi được biết rằng Postgres hỗ trợ "Các hàm cửa sổ", về cơ bản giống như một NHÓM THEO cho một cột cụ thể. Tôi đã sửa đổi truy vấn của mình để sử dụng như vậy:

SELECT
  perm,
  combo,
  avg( value ) as perm_average_value,
  avg( avg( value ) ) over w_combo AS combo_average_value,
  stddev_pop( value ) as perm_stddev,
  stddev_pop( avg( value ) ) over w_combo as combo_stddev,
  count( * ) as perm_count,
  sum( count( * ) ) over w_combo AS combo_count
FROM foo
GROUP BY perm, combo
WINDOW w_combo AS ( PARTITION BY combo );

Trong khi điều này hoạt động cho cột "combo_count", các cột "combo_alusive_value" và "combo_stddev" không còn chính xác nữa. Có vẻ như trung bình đang được lấy cho mỗi hoán vị, và sau đó được tính trung bình lần thứ hai cho mỗi kết hợp, điều này là không chính xác.

Làm thế nào tôi có thể sửa lỗi này? Các chức năng cửa sổ thậm chí có thể được sử dụng như một tối ưu hóa ở đây?


Giả sử phiên bản hiện tại Postgres 9.2? Các chức năng của cửa sổ đi kèm với 8.4.
Erwin Brandstetter

Xin lỗi, tôi quên chỉ định. Có, tôi đang sử dụng bản mới nhất, Postgres 9.2.4.
Scott Nhỏ

Câu trả lời:


9

Bạn có thể có các hàm cửa sổ trên kết quả của các hàm tổng hợp trong một cấp truy vấn duy nhất.

Tất cả điều này sẽ hoạt động tốt sau một vài sửa đổi - ngoại trừ việc nó không thành công cho độ lệch chuẩn trên hiệu trưởng toán học . Các tính toán liên quan không phải là tuyến tính, do đó bạn không thể đơn giản kết hợp độ lệch chuẩn của các quần thể phụ.

SELECT perm
      ,combo
      ,avg(value)                 AS perm_average_value
      ,sum(avg(value) * count(*)) OVER w_combo /
       sum(count(*)) OVER w_combo AS combo_average_value
      ,stddev_pop(value)          AS perm_stddev
      ,0                          AS combo_stddev  -- doesn't work!
      ,count(*)                   AS perm_count
      ,sum(count(*)) OVER w_combo AS combo_count
FROM   foo
GROUP  BY perm, combo
WINDOW w_combo  AS (PARTITION BY combo);

Đối với combo_average_valuebạn sẽ cần biểu thức này

sum(avg(value) * count(*)) OVER w_combo / sum(count(*)) OVER w_combo

Vì bạn cần một trung bình có trọng số . (Trung bình của một nhóm có 10 thành viên nặng hơn trung bình của một nhóm chỉ có 2 thành viên!)

Điều này hoạt động :

SELECT DISTINCT ON (perm, combo)
       perm
      ,combo
      ,avg(value)        OVER wpc AS perm_average_value
      ,avg(value)        OVER wc  AS combo_average_value
      ,stddev_pop(value) OVER wpc AS perm_stddev
      ,stddev_pop(value) OVER wc  AS combo_stddev
      ,count(*)          OVER wpc AS perm_count
      ,count(*)          OVER wc  AS combo_count
FROM   foo
WINDOW wc  AS (PARTITION BY combo)
      ,wpc AS (PARTITION BY perm, combo);

Tôi đang sử dụng hai cửa sổ khác nhau ở đây và giảm các hàng DISTINCTđược áp dụng ngay cả sau các chức năng của cửa sổ.

Nhưng tôi thực sự nghi ngờ nó sẽ nhanh hơn truy vấn ban đầu của bạn. Tôi khá chắc chắn rằng nó không phải là.

Hiệu suất tốt hơn với cách bố trí bảng thay đổi

Mảng có tổng phí là 24 byte (các biến thể nhẹ tùy theo loại). Ngoài ra, bạn dường như có khá nhiều mục trên mỗi mảng và nhiều lần lặp lại. Đối với một bảng lớn như của bạn, nó sẽ trả tiền để bình thường hóa lược đồ. Bố cục ví dụ:

CREATE TABLE combo ( 
  combo_id serial PRIMARY KEY
 ,combo    int[] NOT NULL
);

CREATE TABLE perm ( 
  perm_id  serial PRIMARY KEY
 ,perm     int[] NOT NULL
);

CREATE TABLE value (
  perm_id  int REFERENCES perm(perm_id)
 ,combo_id int REFERENCES combo(combo_id)
 ,value numeric NOT NULL DEFAULT 0
);

Nếu bạn không cần tính toàn vẹn tham chiếu, bạn có thể bỏ qua các ràng buộc khóa ngoại.

Kết nối đến combo_idcũng có thể được đặt trong bảng perm, nhưng trong kịch bản này tôi sẽ lưu trữ nó (hơi không chuẩn hóa) valueđể có hiệu suất tốt hơn.

Điều này sẽ dẫn đến kích thước hàng là 32 byte (tiêu đề tuple + phần đệm: 24 byte, 2 x int (8 byte), không có phần đệm), cộng với kích thước không xác định của numericcột. (Nếu bạn không cần độ chính xác cực cao, một double precisionhoặc thậm chí một realcột cũng có thể làm được.)

Thông tin thêm về lưu trữ vật lý trong câu trả lời liên quan này trên SO hoặc tại đây: Định
cấu hình PostgreSQL để đọc hiệu suất

Dù sao, đó chỉ là một phần nhỏ của những gì bạn có bây giờ và sẽ làm cho truy vấn của bạn nhanh hơn rất nhiều chỉ bằng kích thước. Nhóm và sắp xếp trên các số nguyên đơn giản cũng nhanh hơn rất nhiều.

Trước tiên, bạn sẽ tổng hợp trong một truy vấn con và sau đó tham gia permcombođể có hiệu suất tốt nhất.


Cảm ơn bạn đã trả lời rõ ràng và súc tích. Bạn đã đúng, dường như không có cách nào có được độ lệch chuẩn của dân số tập hợp con theo cách này. Điều đó đang được nói, tôi thích sự đơn giản của giải pháp của bạn. Loại bỏ NHÓM THEO làm cho truy vấn kết quả dễ đọc hơn nhiều. Thật không may khi bạn nghi ngờ hiệu suất là phụ. Tôi đã phải giết truy vấn sau khi chạy hơn 30 phút.
Scott Nhỏ

@ScottSmall: Bạn có thể làm gì đó cho hiệu suất ... xem cập nhật để trả lời.
Erwin Brandstetter

Để đơn giản hóa câu hỏi của tôi, tôi đã xóa các cột khỏi foobảng không liên quan. Trong thực tế, có một số cột khác không được sử dụng bởi truy vấn này, vì vậy tôi không tin rằng việc bình thường hóa các hoán vị và kết hợp sẽ giúp tăng tốc đáng kể, trong trường hợp sử dụng cụ thể này.
Scott Nhỏ

Ngoài ra, các giá trị nguyên tạo thành mỗi hoán vị và kết hợp đến từ một bảng khác trong DB. Tạo trước dữ liệu này là tính toán tốn kém. Độ dài tối đa của perm / combo là 5, tuy nhiên 5Pn và 5Cn tăng khá lớn đối với các giá trị lớn của n (hiện tại khoảng 1000, nhưng đang tăng lên hàng ngày) ... dù sao, tối ưu hóa đó là câu hỏi của một ngày khác. Cảm ơn một lần nữa vì tất cả sự giúp đỡ của bạn Erwin.
Scott Nhỏ
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.