ฉันจะรับผลรวมของฟังก์ชันหน้าต่างใน Postgres ได้อย่างไร


11

ฉันมีตารางที่มีสองคอลัมน์ของพีชคณิต / การรวมกันของอาร์เรย์จำนวนเต็มและคอลัมน์ที่สามที่มีค่าเช่น:

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

ฉันต้องการหาค่าเฉลี่ยและส่วนเบี่ยงเบนมาตรฐานสำหรับการเปลี่ยนแปลงแต่ละครั้งรวมถึงแต่ละชุดค่าผสม ฉันสามารถทำได้ด้วยแบบสอบถามนี้:

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

อย่างไรก็ตามการสืบค้นนั้นช้าลงมากเมื่อฉันมีข้อมูลจำนวนมากเนื่องจากตาราง "foo" (ซึ่งในความเป็นจริงประกอบด้วย 14 พาร์ติชั่นแต่ละอันมีประมาณ 4 ล้านแถว) ต้องสแกนสองครั้ง

เมื่อเร็ว ๆ นี้ฉันได้เรียนรู้ว่า Postgres รองรับ "ฟังก์ชั่นของหน้าต่าง" ซึ่งโดยทั่วไปแล้วจะเหมือนกับ GROUP BY สำหรับคอลัมน์ใดคอลัมน์หนึ่ง ฉันแก้ไขข้อความค้นหาเพื่อใช้สิ่งเหล่านี้:

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

แม้ว่าสิ่งนี้จะใช้ได้กับคอลัมน์ "combo_count" แต่คอลัมน์ "combo_average_value" และ "combo_stddev" นั้นไม่ถูกต้องอีกต่อไป ปรากฏว่ามีการใช้ค่าเฉลี่ยสำหรับการเปลี่ยนแปลงแต่ละครั้งและจากนั้นเฉลี่ยเป็นครั้งที่สองสำหรับแต่ละชุดค่าซึ่งไม่ถูกต้อง

ฉันจะแก้ไขสิ่งนี้ได้อย่างไร สามารถใช้ฟังก์ชั่นหน้าต่างเพื่อเพิ่มประสิทธิภาพได้ที่นี่หรือไม่


สมมติว่ารุ่นปัจจุบัน Postgres 9.2? ฟังก์ชั่นหน้าต่างมาพร้อมกับ 8.4
Erwin Brandstetter

ขออภัยฉันลืมระบุ ใช่ฉันใช้ Postgres รุ่นล่าสุด 9.2.4
Scott Small

คำตอบ:


9

คุณสามารถมีฟังก์ชั่นหน้าต่างจากผลของฟังก์ชั่นรวมในระดับแบบสอบถามเดียว

นี้จะทำงานทุกอย่างหลังจากที่ปรับเปลี่ยนไม่กี่ - ยกเว้นว่ามันล้มเหลวสำหรับค่าเบี่ยงเบนมาตรฐานของเงินต้นทางคณิตศาสตร์ การคำนวณที่เกี่ยวข้องไม่ใช่เชิงเส้นดังนั้นคุณจึงไม่สามารถรวมค่าเบี่ยงเบนมาตรฐานของประชากรย่อยได้

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

สำหรับcombo_average_valueคุณจะต้องมีการแสดงออกนี้

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

เนื่องจากคุณต้องการค่าเฉลี่ยถ่วงน้ำหนัก (ค่าเฉลี่ยของกลุ่มที่มีสมาชิก 10 คนมีน้ำหนักมากกว่าค่าเฉลี่ยของกลุ่มที่มีสมาชิกเพียง 2 คน!)

งานนี้ :

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

ฉันใช้สองหน้าต่างที่แตกต่างกันที่นี่และลดแถวDISTINCTที่ใช้แม้หลังจากฟังก์ชันของหน้าต่าง

แต่ฉันสงสัยอย่างจริงจังว่าจะเร็วกว่าการสืบค้นดั้งเดิม ฉันค่อนข้างแน่ใจว่ามันไม่ใช่

ประสิทธิภาพที่ดีขึ้นด้วยการจัดวางตารางที่เปลี่ยนแปลง

อาร์เรย์มีค่าใช้จ่าย 24 ไบต์ (การเปลี่ยนแปลงเล็กน้อยขึ้นอยู่กับประเภท) นอกจากนี้คุณดูเหมือนจะมีรายการไม่กี่รายการต่ออาเรย์และการทำซ้ำหลายรายการ สำหรับตารางขนาดใหญ่เช่นคุณมันจะจ่ายเงินเพื่อทำให้ปกติสคีมา เค้าโครงตัวอย่าง:

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

หากคุณไม่ต้องการความสมบูรณ์ของการอ้างอิงคุณสามารถละเว้นข้อ จำกัด คีย์ต่างประเทศ

การเชื่อมต่อไปcombo_idยังสามารถวางไว้ในตารางpermได้ แต่ในสถานการณ์นี้ฉันจะเก็บไว้ (ลดขนาดปกติเล็กน้อย) valueเพื่อประสิทธิภาพที่ดีขึ้น

ซึ่งจะส่งผลให้มีขนาดแถว 32 ไบต์ (ส่วนหัว tuple + การขยาย: 24 ไบต์, 2 x int (8 ไบต์), ไม่มีการขยาย) รวมถึงขนาดที่ไม่รู้จักของnumericคอลัมน์ของคุณ (หากคุณไม่ต้องการความแม่นยำสูงอาจdouble precisionมีrealคอลัมน์หรือแม้แต่คอลัมน์ก็ได้)

ข้อมูลเพิ่มเติมเกี่ยวกับหน่วยเก็บข้อมูลจริงในคำตอบที่เกี่ยวข้องกับ SOหรือที่นี่: การ
กำหนดค่า PostgreSQL สำหรับประสิทธิภาพการอ่าน

อย่างไรก็ตามนั่นเป็นเพียงเศษเสี้ยวของสิ่งที่คุณมีในตอนนี้และจะทำให้ข้อความค้นหาของคุณเร็วขึ้นตามขนาดเพียงอย่างเดียว การจัดกลุ่มและการเรียงลำดับตามจำนวนเต็มง่ายก็เร็วขึ้นเช่นกัน

คุณจะเป็นครั้งแรกรวมในแบบสอบถามย่อยและจากนั้นเข้าร่วมpermและcomboเพื่อประสิทธิภาพการทำงานที่ดีที่สุด


ขอบคุณสำหรับคำตอบที่ชัดเจนและรัดกุม คุณถูกต้องดูเหมือนว่าไม่มีทางที่จะได้ค่าเบี่ยงเบนมาตรฐานของประชากรชุดย่อยด้วยวิธีนี้ ที่ถูกกล่าวว่าฉันชอบความเรียบง่ายของการแก้ปัญหาของคุณ การกำจัด GROUP BY ทำให้แบบสอบถามที่เป็นผลลัพธ์นั้นสามารถอ่านได้มากขึ้น น่าเสียดายที่คุณสงสัยว่าประสิทธิภาพการทำงานย่อย ฉันต้องฆ่าแบบสอบถามหลังจากทำงานมานานกว่า 30 นาที
Scott Small

@ScottSmall: คุณสามารถทำอะไรบางอย่างเพื่อประสิทธิภาพ ... ดูการอัปเดตเพื่อตอบ
Erwin Brandstetter

เพื่อให้คำถามของฉันง่ายขึ้นฉันได้ลบคอลัมน์ออกจากfooตารางที่ไม่เกี่ยวข้อง ในความเป็นจริงมีคอลัมน์อีกหลายคอลัมน์ที่ไม่ได้ใช้โดยการค้นหานี้ดังนั้นฉันไม่มั่นใจว่าการทำให้การเรียงสับเปลี่ยนและการรวมกันเป็นปกติจะช่วยเพิ่มความเร็วที่สำคัญสำหรับกรณีการใช้งานนี้โดยเฉพาะ
Scott Small

นอกจากนี้ค่าจำนวนเต็มซึ่งประกอบด้วยการเรียงสับเปลี่ยนและการรวมกันนั้นมาจากอีกตารางหนึ่งในฐานข้อมูล การสร้างข้อมูลนี้ล่วงหน้ามีราคาแพง ความยาวสูงสุดของระดับการอนุญาต / คำสั่งผสมคือ 5 อย่างไรก็ตาม 5Pn และ 5Cn เติบโตได้ค่อนข้างมากสำหรับค่าขนาดใหญ่ของ n (ปัจจุบันประมาณ 1,000 แต่เพิ่มขึ้นทุกวัน) ... อย่างไรก็ตามการเพิ่มประสิทธิภาพนั้นเป็นคำถามของอีกวัน ขอขอบคุณอีกครั้งสำหรับความช่วยเหลือของคุณเออร์วิน
Scott Small
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.