เหตุใด array_agg () จึงช้ากว่าตัวสร้าง ARRAY ที่ไม่ใช่การรวม ()


14

ฉันเพิ่งตรวจสอบโค้ดเก่าที่เขียนขึ้นสำหรับPre-8.4 PostgreSQLและฉันเห็นสิ่งที่ดีจริงๆ ฉันจำได้ว่ามีฟังก์ชั่นที่กำหนดเองทำสิ่งนี้ย้อนกลับไปในวัน แต่ฉันลืมสิ่งที่array_agg()ดูเหมือนก่อน สำหรับการตรวจสอบการรวมกลุ่มที่ทันสมัยเขียนขึ้นเช่นนี้

SELECT array_agg(x ORDER BY x DESC) FROM foobar;

อย่างไรก็ตามกาลครั้งหนึ่งมันถูกเขียนขึ้นเช่นนี้

SELECT ARRAY(SELECT x FROM foobar ORDER BY x DESC);

ดังนั้นฉันลองกับข้อมูลทดสอบบางอย่าง ..

CREATE TEMP TABLE foobar AS
SELECT * FROM generate_series(1,1e7)
  AS t(x);

ผลลัพธ์น่าประหลาดใจวิธี #OldSchoolCool เร็วขึ้นอย่างมาก: เร่งความเร็ว 25% ยิ่งกว่านั้นการทำให้เรียบง่ายขึ้นโดยไม่มีคำสั่งซื้อแสดงความช้าเหมือนกัน

# EXPLAIN ANALYZE SELECT ARRAY(SELECT x FROM foobar);
                                                         QUERY PLAN                                                          
-----------------------------------------------------------------------------------------------------------------------------
 Result  (cost=104425.28..104425.29 rows=1 width=0) (actual time=1665.948..1665.949 rows=1 loops=1)
   InitPlan 1 (returns $0)
     ->  Seq Scan on foobar  (cost=0.00..104425.28 rows=6017728 width=32) (actual time=0.032..716.793 rows=10000000 loops=1)
 Planning time: 0.068 ms
 Execution time: 1671.482 ms
(5 rows)

test=# EXPLAIN ANALYZE SELECT array_agg(x) FROM foobar;
                                                        QUERY PLAN                                                         
---------------------------------------------------------------------------------------------------------------------------
 Aggregate  (cost=119469.60..119469.61 rows=1 width=32) (actual time=2155.154..2155.154 rows=1 loops=1)
   ->  Seq Scan on foobar  (cost=0.00..104425.28 rows=6017728 width=32) (actual time=0.031..717.831 rows=10000000 loops=1)
 Planning time: 0.054 ms
 Execution time: 2174.753 ms
(4 rows)

แล้วเกิดอะไรขึ้นที่นี่ ทำไมarray_aggซึ่งเป็นฟังก์ชั่นภายในจึงช้ากว่าวูดู SQL ของผู้วางแผน

ใช้ " PostgreSQL 9.5.5บน x86_64-pc-linux-gnu รวบรวมโดย gcc (Ubuntu 6.2.0-5ubuntu12) 6.2.0 20161005, 64-bit"

คำตอบ:


17

ไม่มีอะไร "โรงเรียนเก่า" หรือ "ล้าสมัย" เกี่ยวกับตัวสร้าง ARRAY (นั่นคือสิ่งที่ARRAY(SELECT x FROM foobar)เป็น) มันทันสมัยเช่นเคย ใช้สำหรับการรวมอาร์เรย์อย่างง่าย

คู่มือ:

นอกจากนี้ยังเป็นไปได้ที่จะสร้างอาร์เรย์จากผลลัพธ์ของแบบสอบถามย่อย ในฟอร์มนี้ตัวสร้างอาร์เรย์จะถูกเขียนด้วยคำสำคัญARRAYตามด้วยเคียวรี่ย่อยแบบวงเล็บปีกกา

ฟังก์ชันการรวมarray_agg()เป็นมากกว่าที่หลากหลายในการที่จะสามารถบูรณาการในSELECTรายการที่มีคอลัมน์เพิ่มเติมรวมตัวอาจจะมากกว่าในที่เดียวกันและกลุ่มพลสามารถเกิดขึ้นกับSELECT GROUP BYในขณะที่ตัวสร้าง ARRAY สามารถส่งคืนอาร์เรย์เดียวจากการSELECTส่งคืนคอลัมน์เดียวเท่านั้น

ฉันไม่ได้ศึกษาซอร์สโค้ด แต่ดูเหมือนจะชัดเจนว่าเครื่องมืออเนกประสงค์ที่มีราคาแพงกว่า

ข้อแตกต่างที่น่าสังเกตหนึ่ง: ตัวสร้าง ARRAY ส่งคืนอาร์เรย์ว่าง ( {}) หากไม่มีแถวที่มีคุณสมบัติ array_agg()ผลตอบแทนNULLสำหรับเดียวกัน


6

ฉันเชื่อว่าคำตอบที่ได้รับการยอมรับจาก Erwin สามารถเพิ่มได้ดังต่อไปนี้

โดยปกติแล้วเรากำลังทำงานกับตารางปกติที่มีดัชนีแทนที่จะเป็นตารางชั่วคราว (ไม่มีดัชนี) เช่นเดียวกับคำถามเดิม มันมีประโยชน์ที่จะทราบว่ารวมตัวเช่นARRAY_AGGไม่สามารถยกระดับดัชนีที่มีอยู่เมื่อเรียงลำดับจะทำในระหว่างการรวม

ตัวอย่างเช่นสมมติว่าแบบสอบถามต่อไปนี้:

SELECT ARRAY(SELECT c FROM t ORDER BY id)

ถ้าเรามีดัชนีในt(id, ...)ดัชนีสามารถนำมาใช้ในความโปรดปรานของการสแกนตามลำดับในตามด้วยการเรียงลำดับt t.idนอกจากนี้หากคอลัมน์เอาต์พุตที่อยู่ในอาร์เรย์ (ที่นี่c) เป็นส่วนหนึ่งของดัชนี (เช่นดัชนีบนt(id, c)หรือดัชนีรวมบนt(id) include(c)) สิ่งนี้อาจเป็นการสแกนเฉพาะดัชนีเท่านั้น

ตอนนี้เรามาเขียนคำค้นหาใหม่อีกครั้งดังนี้:

SELECT ARRAY_AGG(c ORDER BY id) FROM t

ตอนนี้การรวมจะไม่ใช้ดัชนีและจะต้องเรียงแถวในหน่วยความจำ (หรือแย่กว่านั้นสำหรับชุดข้อมูลขนาดใหญ่บนดิสก์) นี้จะเป็นสแกนลำดับบนtตามด้วยการรวมตัว + การจัดเรียง

เท่าที่ฉันรู้นี้ไม่ได้มีการบันทึกไว้ในเอกสารอย่างเป็นทางการ แต่สามารถได้มาจากแหล่งที่มา นี่ควรเป็นกรณีสำหรับเวอร์ชันปัจจุบันทั้งหมดรวม v11 ด้วย


2
จุดดี. แต่ในความเป็นธรรมทั้งหมดแบบสอบถามหรือฟังก์ชั่นรวมที่คล้ายกันยังสามารถยกระดับดัชนีที่มีแบบสอบถามย่อยเช่น:array_agg() ประโยคSELECT ARRAY_AGG(c) FROM (SELECT c FROM t ORDER BY id) subต่อการรวมORDER BYคือสิ่งที่ห้ามการใช้ดัชนีในตัวอย่างของคุณ ตัวสร้างอาร์เรย์จะเร็วกว่าarray_agg()เมื่อใดสามารถใช้ดัชนีเดียวกัน (หรือไม่) มันไม่ได้อเนกประสงค์ ดู: dba.stackexchange.com/a/213724/3684
Erwin Brandstetter

1
ใช่มันเป็นความแตกต่างที่สำคัญ ฉันเปลี่ยนคำตอบเล็กน้อยเพื่อให้ชัดเจนว่าคำพูดนี้เก็บไว้เมื่อฟังก์ชันการรวมต้องเรียงลำดับเท่านั้น คุณสามารถยังคงได้กำไรจากดัชนีในกรณีง่าย ๆ เนื่องจาก PostgreSQL ดูเหมือนว่าจะรับประกันได้ว่าการรวมจะเกิดขึ้นในลำดับเดียวกันกับที่กำหนดไว้ในแบบสอบถามย่อยตามที่อธิบายไว้ในลิงก์ มันค่อนข้างเท่ห์ ฉันสงสัยว่าถ้าสิ่งนี้ยังคงอยู่ในกรณีของตารางที่แบ่งพาร์ติชันและ / หรือตาราง FDW และ / หรือคนทำงานแบบขนาน - และถ้า PostgreSQL สามารถรักษาสัญญานี้ไว้ในอนาคตได้
pbillen

สำหรับบันทึกฉันไม่เคยมีความตั้งใจที่จะสงสัยในคำตอบที่ยอมรับ ฉันแค่คิดว่ามันเป็นการดีนอกเหนือจากเหตุผลเกี่ยวกับการมีอยู่และการใช้ดัชนีร่วมกับการรวม
pbillen

1
มันเป็นยังดี
Erwin Brandstetter
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.