มีการใช้คำสั่งในลำดับที่เขียนหรือไม่


36

ฉันพยายามเพิ่มประสิทธิภาพการสืบค้นซึ่งมีลักษณะเป็นตารางขนาดใหญ่ (37 ล้านแถว) และมีคำถามเกี่ยวกับลำดับการดำเนินการในแบบสอบถาม

select 1 
from workdays day
where day.date_day >= '2014-10-01' 
    and day.date_day <= '2015-09-30' 
    and day.offer_id in (
        select offer.offer_day 
        from offer  
        inner join province on offer.id_province = province.id_province  
        inner join center cr on cr.id_cr = province.id_cr 
        where upper(offer.code_status) <> 'A' 
            and province.id_region in ('10' ,'15' ,'21' ,'26' ,'31' , ...,'557') 
            and province.id_cr in ('9' ,'14' ,'20' ,'25' ,'30' ,'35' ,'37')
    )

เป็นWHEREคำสั่งสำหรับช่วงวันที่ดำเนินการก่อนแบบสอบถามย่อยหรือไม่ เป็นวิธีที่ดีหรือไม่ที่จะวางอนุประโยคที่เข้มงวดที่สุดเป็นอันดับแรกเพื่อหลีกเลี่ยงการวนซ้ำขนาดใหญ่สำหรับส่วนคำสั่งอื่น ๆ เพื่อให้การประมวลผลเร็วขึ้น?

แบบสอบถามตอนนี้ใช้เวลามากในการดำเนินการ

คำตอบ:


68

หากต้องการรายละเอียดเกี่ยวกับคำตอบของ @ alci:

PostgreSQL ไม่สนใจว่าคุณจะเขียนคำสั่งอะไร

  • PostgreSQL ไม่สนใจเกี่ยวกับลำดับของรายการในWHEREประโยคและเลือกดัชนีและคำสั่งการดำเนินการตามการประมาณการต้นทุนและการเลือกเฉพาะเพียงอย่างเดียว

  • ลำดับที่เข้าร่วมจะเขียนจะถูกละเว้นนอกจากนี้ยังขึ้นอยู่กับการกำหนดค่าjoin_collapse_limit; หากมีการรวมมากกว่านั้นระบบจะดำเนินการตามลำดับที่เขียน

  • แบบสอบถามย่อยสามารถดำเนินการก่อนหรือหลังแบบสอบถามที่มีพวกเขาขึ้นอยู่กับสิ่งที่เร็วที่สุดตราบเท่าที่แบบสอบถามย่อยจะถูกดำเนินการก่อนที่แบบสอบถามด้านนอกต้องการข้อมูลจริง บ่อยครั้งในความเป็นจริงแบบสอบถามย่อยจะถูกดำเนินการแบบตรงกลางหรือแทรกด้วยคิวรีด้านนอก

  • ไม่มีการรับประกัน PostgreSQL จริง ๆ แล้วจะดำเนินการบางส่วนของแบบสอบถามทั้งหมด สามารถเพิ่มประสิทธิภาพได้อย่างสมบูรณ์ นี่เป็นสิ่งสำคัญหากคุณเรียกใช้ฟังก์ชันที่มีผลข้างเคียง

PostgreSQL จะเปลี่ยนการสืบค้นของคุณ

PostgreSQL จะทำการแปลงข้อความค้นหาอย่างหนักในขณะที่ยังคงรักษาเอฟเฟ็กต์ที่เหมือนกันเพื่อให้มันทำงานได้เร็วขึ้นในขณะที่ไม่เปลี่ยนผลลัพธ์

  • คำที่อยู่นอกเคียวรีย่อยสามารถถูกส่งลงในเคียวรีย่อยเพื่อให้รันเป็นส่วนหนึ่งของเคียวรีย่อยที่คุณไม่ได้เขียนไว้ในเคียวรีภายนอก

  • คำศัพท์ในแบบสอบถามย่อยสามารถดึงขึ้นไปยังแบบสอบถามด้านนอกเพื่อให้การดำเนินการของพวกเขาเป็นส่วนหนึ่งของแบบสอบถามด้านนอกไม่ใช่ที่ที่คุณเขียนพวกเขาในแบบสอบถามย่อย

  • แบบสอบถามย่อยสามารถและมักจะแบนเข้าร่วมในตารางด้านนอก เช่นเดียวกับสิ่งที่ต้องการEXISTSและNOT EXISTSข้อความค้นหา

  • จำนวนการดูจะถูกปรับเป็นคิวรีที่ใช้มุมมอง

  • ฟังก์ชั่น SQL มักจะได้รับการแทรกเข้าไปในแบบสอบถามการโทร

  • ... และมีการแปลงอื่น ๆ อีกมากมายที่ทำกับเคียวรีเช่นการแสดงออกก่อนการประเมินค่าคงที่การยกเลิกการเชื่อมโยงของเคียวรีย่อยบางรายการและเทคนิคการวางแผน / การเพิ่มประสิทธิภาพอื่น ๆ

โดยทั่วไป PostgreSQL สามารถแปลงและเขียนข้อความค้นหาของคุณได้อย่างหนาแน่นจนถึงจุดที่แต่ละแบบสอบถามเหล่านี้:

select my_table.*
from my_table
left join other_table on (my_table.id = other_table.my_table_id)
where other_table.id is null;

select *
from my_table
where not exists (
  select 1
  from other_table
  where other_table.my_table_id = my_table.id
);

select *
from my_table
where my_table.id not in (
  select my_table_id
  from other_table
  where my_table_id is not null
);

โดยปกติจะสร้างแผนแบบสอบถามเดียวกันทั้งหมด (สมมติว่าฉันไม่ได้ทำผิดพลาดโง่ ๆ ในข้างต้น)

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

ข้อ จำกัด

เครื่องมือวางแผน / เพิ่มประสิทธิภาพอยู่ไกลจาก omnicient และถูก จำกัด ด้วยความต้องการอย่างแน่นอนว่าไม่สามารถเปลี่ยนผลกระทบของแบบสอบถามข้อมูลที่มีอยู่เพื่อการตัดสินใจกฎที่ใช้และเวลา CPU มันสามารถที่จะใช้จ่ายไตร่ตรองการเพิ่มประสิทธิภาพ ตัวอย่างเช่น:

  • วางแผนการอาศัยอยู่กับสถิติเก็บไว้โดยANALYZE(มักจะผ่าน autovacuum) หากสิ่งเหล่านี้ล้าสมัยตัวเลือกแผนอาจไม่ดี

  • สถิติเป็นเพียงตัวอย่างเท่านั้นดังนั้นพวกเขาจึงอาจทำให้เข้าใจผิดเนื่องจากผลการสุ่มตัวอย่างโดยเฉพาะอย่างยิ่งหากมีตัวอย่างน้อยเกินไป ตัวเลือกแผนไม่ถูกต้องอาจส่งผลให้

  • สถิติไม่ได้ติดตามข้อมูลบางประเภทเกี่ยวกับตารางเช่นความสัมพันธ์ระหว่างคอลัมน์ สิ่งนี้สามารถนำไปสู่การวางแผนในการตัดสินใจที่ไม่ดีเมื่อมันคิดว่าสิ่งต่าง ๆ เป็นอิสระเมื่อพวกเขาไม่

  • ผู้วางแผนจะอาศัยพารามิเตอร์ราคาที่ต้องการrandom_page_costบอกความเร็วของการดำเนินการต่างๆในระบบเฉพาะที่ติดตั้งไว้ คำแนะนำเหล่านี้เป็นเพียง หากพวกเขาผิดอย่างรุนแรงพวกเขาสามารถนำไปสู่การเลือกแผนไม่ดี

  • แบบสอบถามย่อยใด ๆ ที่มีLIMITหรือOFFSETไม่สามารถถูกแบนหรืออาจมีการดึง / กด นี้ไม่ได้หมายความว่ามันจะดำเนินการก่อนที่จะทุกส่วนของแบบสอบถามนอกแม้ว่าหรือแม้กระทั่งว่ามันจะดำเนินการในทุก

  • คำศัพท์ CTE (ส่วนคำในWITHแบบสอบถาม) จะถูกดำเนินการอย่างครบถ้วนหากพวกเขาดำเนินการเลย พวกเขาไม่สามารถถูกแบนและคำศัพท์ไม่สามารถถูกผลักหรือดึงข้ามอุปสรรค CTE ได้ คำศัพท์ CTE จะถูกดำเนินการเสมอก่อนที่จะค้นหาครั้งสุดท้าย นี่เป็นพฤติกรรมที่ไม่ได้มาตรฐานของ SQLแต่มีการบันทึกไว้ว่า PostgreSQL ทำงานอย่างไร

  • PostgreSQL มีความสามารถที่ จำกัด ในการปรับให้เหมาะสมกับการสืบค้นในตารางต่างประเทศsecurity_barrierมุมมองและความสัมพันธ์ชนิดพิเศษอื่น ๆ

  • PostgreSQL จะไม่อินไลน์ฟังก์ชั่นที่เขียนด้วยอะไรยกเว้น SQL ธรรมดาและจะไม่ทำการดึง / กดระหว่างมันกับเคียวรีด้านนอก

  • เครื่องมือวางแผน / เพิ่มประสิทธิภาพนั้นโง่มากเกี่ยวกับการเลือกดัชนีนิพจน์และความแตกต่างของชนิดข้อมูลเล็กน้อยระหว่างดัชนีและนิพจน์

มากขึ้นเช่นกัน

คำค้นหาของคุณ

ในกรณีที่มีการสอบถามของคุณ:

select 1 
from workdays day
where day.date_day >= '2014-10-01' 
    and day.date_day <= '2015-09-30' 
    and day.offer_id in (
        select offer.offer_day 
        from offer  
        inner join province on offer.id_province = province.id_province  
        inner join center cr on cr.id_cr = province.id_cr 
        where upper(offer.code_status) <> 'A' 
            and province.id_region in ('10' ,'15' ,'21' ,'26' ,'31' , ...,'557') 
            and province.id_cr in ('9' ,'14' ,'20' ,'25' ,'30' ,'35' ,'37')
    )

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

มันอาจจะกลายเป็นสิ่งที่ต้องการ (ยังไม่ทดลอง):

select 1 
from workdays day
inner join offer on day.offer_id = offer.offer_day
inner join province on offer.id_province = province.id_province  
inner join center cr on cr.id_cr = province.id_cr 
where upper(offer.code_status) <> 'A' 
   and province.id_region in ('10' ,'15' ,'21' ,'26' ,'31' , ...,'557') 
   and province.id_cr in ('9' ,'14' ,'20' ,'25' ,'30' ,'35' ,'37')
   and day.date_day >= '2014-10-01' 
   and day.date_day <= '2015-09-30';

PostgreSQL จะปรับลำดับการเข้าร่วมและวิธีการเข้าร่วมให้เหมาะสมโดยพิจารณาจากการเลือกแถวและการประมาณจำนวนแถวและดัชนีที่มี หากสิ่งเหล่านี้สะท้อนความเป็นจริงอย่างสมเหตุสมผลแล้วมันจะทำการเชื่อมต่อและเรียกใช้ส่วนคำสั่งที่เหมาะสมที่สุด - มักผสมเข้าด้วยกันดังนั้นมันจะทำสิ่งนี้เล็กน้อยจากนั้นสักเล็กน้อยจากนั้นกลับมาที่ส่วนแรก ฯลฯ

วิธีดูว่าเครื่องมือเพิ่มประสิทธิภาพทำอะไร

คุณไม่เห็น SQL ที่ PostgreSQL ปรับคิวรีของคุณให้เหมาะสมเพราะจะแปลง SQL ให้เป็นตัวแทนแผนผังคิวรีภายในจากนั้นปรับเปลี่ยนนั้น คุณสามารถดัมพ์แผนเคียวรีและเปรียบเทียบกับเคียวรีอื่น

ไม่มีวิธีที่จะ "ลดระดับ" แผนการสืบค้นหรือแผนผังแผนภายในกลับเป็น SQL

http://explain.depesz.com/มีตัวช่วยแผนคิวรีที่ดี หากคุณยังใหม่ทั้งหมดในการสอบถามแผน ฯลฯ (ซึ่งในกรณีนี้ฉันประหลาดใจที่คุณทำสิ่งนี้ผ่านการโพสต์นี้) PgAdmin มีตัวแสดงแผนแบบสอบถามแบบกราฟิกที่ให้ข้อมูลน้อยกว่า แต่ง่ายกว่า

การอ่านที่เกี่ยวข้อง:

ขยายลง / pullup และความสามารถแฟบดำเนินการปรับปรุงในแต่ละรุ่น PostgreSQL เป็นมักจะถูกต้องเกี่ยวกับการดึงขึ้น / ผลักดันลง / แฟบตัดสินใจ แต่ไม่เสมอไปดังนั้นบางครั้งคุณต้อง (AB) ใช้ CTE หรือOFFSET 0สับ หากคุณพบกรณีดังกล่าวให้รายงานข้อผิดพลาดของเครื่องมือวางแผนแบบสอบถาม


หากคุณเป็นคนที่กระตือรือร้นจริง ๆ คุณสามารถใช้debug_print_plansตัวเลือกเพื่อดูแผนการสืบค้นข้อมูลดิบ แต่ฉันสัญญาว่าคุณไม่ต้องการอ่าน จริงๆ.


ว้าว ... คำตอบที่ค่อนข้างสมบูรณ์ :-) หนึ่งในกรณีที่ฉันมีแผนช้ากับ Postgresql (เช่นเดียวกับเครื่องมือ DB อื่น ๆ ที่รู้จักกันดีเช่น Oracle) มีความสัมพันธ์ระหว่างคอลัมน์หรือการรวมที่สัมพันธ์กันหลายรายการ มันมักจะจบลงด้วยการทำลูปซ้อนกันคิดว่ามีเพียงไม่กี่แถวที่จุดนี้ของแผนเมื่อในความเป็นจริงมีหลายพัน วิธีหนึ่งในการเพิ่มประสิทธิภาพข้อความค้นหาประเภทนี้คือ 'set enable_nestloop = off;' ตลอดระยะเวลาของการสืบค้น
alci

ฉันวิ่งเข้าไปในสถานการณ์ที่ v9.5.5 กำลังพยายามใช้ TO_DATE ก่อนการตรวจสอบว่าสามารถใช้ได้หรือไม่ใน 7 แบบง่ายที่แบบสอบถามข้อ คำสั่งสำคัญ
user1133275

@ user1133275 ในกรณีนั้นจะทำงานให้คุณโดยบังเอิญเท่านั้นเนื่องจากการประมาณการต้นทุนการคำนวณเหมือนกัน PostgreSQL อาจยังคงตัดสินใจที่จะเรียกใช้to_dateก่อนที่จะตรวจสอบในบางรุ่นในภายหลังหรือเนื่องจากบางสถิติการเพิ่มประสิทธิภาพการเปลี่ยนแปลง หากต้องการเรียกใช้การตรวจสอบอย่างน่าเชื่อถือก่อนฟังก์ชั่นที่ควรทำงานหลังจากการตรวจสอบให้ใช้CASEคำสั่ง
Craig Ringer

หนึ่งในคำตอบที่ยิ่งใหญ่ที่สุดที่ฉันเคยเห็นบน SO! ยกนิ้วให้ผู้ชาย!
62mkv

ฉันได้ประสบการณ์สถานการณ์ที่สำหรับแบบสอบถามง่ายๆเพิ่มทำดำเนินการแบบสอบถามเร็วกว่าหากไม่มีorder by order byนั่นเป็นหนึ่งในเหตุผลที่ฉันเขียนข้อความค้นหาด้วยการเข้าร่วมในลักษณะเช่นถ้าฉันต้องการให้มีการดำเนินการ - เป็นเรื่องดีที่มีเครื่องมือเพิ่มประสิทธิภาพที่ดี แต่ฉันคิดว่าไม่ควรเชื่อถือชะตากรรมของคุณโดยสิ้นเชิงกับผลลัพธ์ของมัน มันmay beดำเนินการ ... คำตอบที่ดี !!
Greg0ry

17

SQL เป็นภาษาที่ประกาศ: คุณบอกสิ่งที่คุณต้องการไม่ใช่วิธีการ RDBMS จะเลือกวิธีดำเนินการแบบสอบถามเรียกว่าแผนการดำเนินการ

กาลครั้งหนึ่งนานมานี้ (5-10 ปีที่ผ่านมา) วิธีการเขียนแบบสอบถามมีผลกระทบโดยตรงต่อแผนการดำเนินการ แต่ทุกวันนี้เครื่องมือฐานข้อมูล SQL ส่วนใหญ่ใช้ Cost Based Optimizer สำหรับการวางแผน นั่นคือมันจะประเมินกลยุทธ์ที่แตกต่างเพื่อดำเนินการค้นหาตามสถิติของวัตถุฐานข้อมูลและเลือกสิ่งที่ดีที่สุด

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


ควรสังเกตว่าในการสอบถาม RDBMS บางคำสั่งยังคงมีความสำคัญ แต่สำหรับคำสั่งที่สูงกว่านั้นทุกสิ่งที่คุณพูดนั้นเป็นจริงในทางปฏิบัติเช่นเดียวกับทฤษฎี เมื่อผู้วางแผนคิวรีเลือกตัวเลือกการดำเนินการที่ไม่ดีมักจะมีคำแนะนำแบบสอบถามเพื่อผลักดันมันไปในทิศทางที่มีประสิทธิภาพมากขึ้น (เช่นWITH(INDEX(<index>))ใน MSSQL เพื่อบังคับให้เลือกดัชนีสำหรับการเข้าร่วมโดยเฉพาะ)
David Spillett

คำถามคือถ้ามีดัชนีdate_dayอยู่จริง หากไม่มีเลยตัวเพิ่มประสิทธิภาพจะไม่มีแผนมากมายที่จะเปรียบเทียบ
jkavalik
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.