เข้าร่วมกับแบบสอบถามย่อย


837

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

ฉันขาดความรู้ทางทฤษฎีที่จะตัดสินด้วยตัวเองหากมีความแตกต่างใด ๆ แบบสอบถามย่อยดีเท่าJOINและดังนั้นจึงไม่มีอะไรต้องกังวลใช่หรือไม่


23
บางครั้งข้อความค้นหาย่อยอาจยอดเยี่ยม พวกเขาดูดประสิทธิภาพใน MySQL อย่าใช้มัน
runrig

8
ฉันมักจะอยู่ภายใต้การแสดงผลที่แบบสอบถามย่อยโดยนัยถูกดำเนินการเป็นตัวเชื่อมที่มีอยู่ในเทคโนโลยีฐานข้อมูล
Kezzer

18
แบบสอบถามย่อยไม่ได้ดูดเสมอเมื่อเข้าร่วมกับตารางขนาดใหญ่สวยวิธีที่ต้องการคือการเลือกย่อยจากตารางขนาดใหญ่นั้น (จำกัด จำนวนแถว) แล้วเข้าร่วม
ovais.tariq

136
"ทุกวันนี้ทุกคนใช้แบบสอบถามย่อย" [อ้างจำเป็น]
Piskvor ออกจากอาคาร

3
อาจมีความเกี่ยวข้อง (แม้ว่าจะเฉพาะเจาะจงมากขึ้น): stackoverflow.com/questions/141278/subqueries-vs-joins/ …
Leigh Brenecki

คำตอบ:


191

นำมาจากคู่มือ MySQL ( 13.2.10.11 การสืบค้นย่อยการสืบค้นใหม่เป็นแบบร่วม ):

LEFT [OUTER] JOIN สามารถเร็วกว่าแบบสอบถามย่อยที่เทียบเท่าได้เนื่องจากเซิร์ฟเวอร์อาจสามารถปรับให้เหมาะสมได้ดีกว่าความจริงที่ไม่เฉพาะเจาะจงกับเซิร์ฟเวอร์ MySQL เพียงอย่างเดียว

ดังนั้นเคียวรีย่อยอาจช้ากว่าLEFT [OUTER] JOINแต่ในความเห็นของฉันความแข็งแกร่งของมันคือการอ่านที่สูงขึ้นเล็กน้อย


45
@ user1735921 IMO มันขึ้นอยู่กับ ... โดยทั่วไปมันเป็นสิ่งสำคัญมากในการอ่านรหัสเพราะมันมีความสำคัญอย่างยิ่งสำหรับการจัดการในภายหลังของมัน ... ขอให้จำคำพูดที่มีชื่อเสียงของ Donald Knuth: "การเพิ่มประสิทธิภาพก่อนวัยอันควรเป็นรากฐานของทั้งหมด ความชั่วร้าย (หรืออย่างน้อยที่สุด) ในการเขียนโปรแกรม " . อย่างไรก็ตามตามธรรมชาติมีพื้นที่ที่มีประสิทธิภาพเป็นสิ่งสำคัญยิ่ง ... นึกคิดโปรแกรมเมื่อหนึ่งความสำเร็จในการปรองดองเป็นหนึ่งเดียวกับ :) อีก
simhumileco

30
ในการสืบค้นที่ซับซ้อนมากขึ้นฉันพบว่าการอ่านง่ายกว่าการสืบค้นย่อย คิวรีย่อยเปลี่ยนเป็นก๋วยเตี๋ยวในหัวของฉัน
Zahra

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

6
@ user1735921 เฉพาะในกรณีที่การเพิ่มประสิทธิภาพนั้นคุ้มค่ากับการเพิ่มเวลาการบำรุงรักษาที่จำเป็นในอนาคต
Joshua Schlichting

3
ความเห็นของฉันJoinและsub queryมีไวยากรณ์ที่แตกต่างกันดังนั้นความสามารถในการอ่านจึงไม่สามารถเปรียบเทียบได้ทั้งสองมีความสามารถในการอ่านที่สูงขึ้นตราบใดที่คุณใช้ไวยากรณ์ SQL ประสิทธิภาพเป็นสิ่งสำคัญมาก
Thavaprakash Swaminathan

840

แบบสอบถามย่อยเป็นวิธีที่ถูกต้องในการแก้ปัญหาของแบบฟอร์ม "รับข้อมูลจาก A, ตามเงื่อนไขบนข้อเท็จจริงจาก B" ในกรณีเช่นนี้มันสมเหตุสมผลมากกว่าที่จะติด B ในคิวรีย่อยแทนที่จะเข้าร่วม นอกจากนี้ยังปลอดภัยกว่าเนื่องจากคุณไม่ต้องระมัดระวังในการรับข้อมูลที่ซ้ำซ้อนจาก A เนื่องจากการแข่งขันหลายนัดกับ B.

อย่างไรก็ตามการพูดจริงแล้วคำตอบมักจะมีประสิทธิภาพลดลง เครื่องมือเพิ่มประสิทธิภาพบางอย่างจะดูดมะนาวเมื่อได้รับการเข้าร่วมกับแบบสอบถามย่อยและบางคนก็ดูดมะนาวด้วยวิธีอื่นและนี่คือเฉพาะเครื่องมือเพิ่มประสิทธิภาพ DBMS รุ่นเฉพาะและแบบสอบถามเฉพาะ

โดยทั่วไปแล้วการเข้าร่วมที่ชัดเจนมักชนะดังนั้นสติปัญญาที่จัดตั้งขึ้นที่รวมดีกว่า แต่ตัวเพิ่มประสิทธิภาพเริ่มดีขึ้นตลอดเวลาและดังนั้นฉันชอบที่จะเขียนข้อความค้นหาเป็นครั้งแรกด้วยวิธีที่สอดคล้องกันอย่างมีเหตุผลและจากนั้นปรับโครงสร้างหากข้อ จำกัด ด้านประสิทธิภาพรับประกัน


105
คำตอบที่ดี ฉันยังเพิ่มว่านักพัฒนา (โดยเฉพาะมือสมัครเล่น) ไม่ได้มีความเชี่ยวชาญใน SQL เสมอไป
ÁlvaroGonzález

4
+1 กำลังมองหาคำอธิบายเหตุผลบางอย่างสำหรับปัญหานี้มาเป็นเวลานานนี้เป็นคำตอบเดียวที่ดูเหมือนว่าตรรกะให้ฉัน
อาลี Umair

1
@Marcelo Cantos คุณช่วยยกตัวอย่างคำแถลงของคุณว่า "ปลอดภัยกว่าเพราะคุณไม่ต้องระวังในการรับข้อมูลที่ซ้ำซ้อนจาก A เนื่องจากการแข่งขันหลายนัดกับ B. " ฉันพบว่ามันเฉียบแหลม แต่ก็เป็นนามธรรมไปเล็กน้อย ขอบคุณ
Jinghui Niu

6
ลูกค้าที่ซื้อ @JinghuiNiu select custid from cust join bought using (custid) where price > 500รายการค่าใช้จ่าย: หากลูกค้าซื้อสินค้าราคาแพงหลายรายการคุณจะได้รับสองครั้ง select custid from cust where exists (select * from bought where custid = cust.custid and price > 500)เพื่อแก้ไขปัญหานี้ คุณสามารถใช้select distinct …แทนได้ แต่มักจะใช้ได้มากกว่าสำหรับตัวเพิ่มประสิทธิภาพหรือตัวประเมิน
Marcelo Cantos

1
@ MatTheWhale ใช่ฉันใช้คำตอบที่ตรงไปตรงมาเพราะฉันขี้เกียจ ในสถานการณ์จริงคุณจะดึงคอลัมน์มากกว่าเพียงแค่ custid จาก cust
Marcelo Cantos

357

ในกรณีส่วนใหญ่JOINs จะเร็วกว่าคิวรีย่อยและมันหายากมากสำหรับคิวรีย่อยที่จะเร็วกว่า

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

สิ่งที่ดีในแบบสอบถามย่อยคือพวกเขาสามารถอ่านได้มากกว่าJOINs: นั่นเป็นสาเหตุที่คน SQL ใหม่ส่วนใหญ่ชอบพวกเขา มันเป็นวิธีที่ง่าย แต่เมื่อพูดถึงประสิทธิภาพการทำงานเข้าร่วมได้ดีขึ้นในกรณีส่วนใหญ่แม้ว่าพวกเขาจะไม่อ่านยาก


14
ใช่ดังนั้นฐานข้อมูลส่วนใหญ่จึงรวมเป็นขั้นตอนการปรับให้เหมาะสมเพื่อแปลงแบบสอบถามย่อยให้เข้าร่วมเมื่อวิเคราะห์ข้อความค้นหาของคุณ
ภาพยนตร์

16
คำตอบนี้ง่ายเกินไปสำหรับคำถามที่ถาม ตามที่คุณระบุ: เคียวรีย่อยบางอย่างนั้นโอเคและบางอย่างก็ไม่ใช่ คำตอบไม่ได้ช่วยแยกความแตกต่างของทั้งสอง (เช่น 'หายากมาก' ขึ้นอยู่กับข้อมูล / แอปของคุณจริงๆ)
ไม่มีเหตุผล

21
คุณสามารถพิสูจน์คะแนนใด ๆ ของคุณพร้อมเอกสารอ้างอิงหรือผลการทดสอบได้หรือไม่?
UğurGümüşhan

62
ฉันได้รับประสบการณ์ที่ดีมากกับแบบสอบถามย่อยที่มีการอ้างอิงกลับไปยังแบบสอบถามบนโดยเฉพาะอย่างยิ่งเมื่อมันมาถึงการนับแถวมากกว่า 100,000 สิ่งนี้ดูเหมือนจะเป็นการใช้หน่วยความจำและการสลับหน้าไปยังไฟล์ swap การเข้าร่วมจะสร้างข้อมูลจำนวนมากซึ่งอาจไม่พอดีกับหน่วยความจำและต้องได้รับการทำเพจเป็นไฟล์สลับ เมื่อใดก็ตามที่เป็นกรณีนี้เคียวรีย่อยของตัวเลือกย่อยจะselect * from a where a.x = (select b.x form b where b.id = a.id)มีขนาดเล็กมากเมื่อเทียบกับการเข้าร่วม นี่เป็นปัญหาที่เฉพาะเจาะจงมาก แต่ในบางกรณีมันจะนำคุณจากชั่วโมงสู่นาที
zuloo

13
ฉันมีประสบการณ์กับ Oracle และฉันสามารถพูดได้ว่าแบบสอบถามย่อยจะดีกว่ามากในตารางขนาดใหญ่ถ้าคุณไม่มีการกรองหรือเรียงลำดับ
Amir Pashazadeh

130

ใช้อธิบายเพื่อดูว่าฐานข้อมูลของคุณดำเนินการค้นหาข้อมูลของคุณอย่างไร คำตอบนี้มีขนาดใหญ่มาก "ขึ้นอยู่กับ" ...

PostgreSQL สามารถเขียนแบบสอบถามย่อยอีกครั้งเพื่อเข้าร่วมหรือเข้าร่วมแบบสอบถามย่อยเมื่อคิดว่าหนึ่งเร็วกว่าอื่น ๆ ทุกอย่างขึ้นอยู่กับข้อมูลดัชนีความสัมพันธ์จำนวนข้อมูลแบบสอบถาม ฯลฯ


6
ตรงนี้เป็นเหตุผลที่ postgresql ดีและมีประโยชน์เข้าใจว่าเป้าหมายคืออะไรและจะแก้ไขแบบสอบถามตามสิ่งที่คิดว่าดีกว่าและ postgresql ดีมากที่รู้วิธีดูข้อมูล
WojonsTech

heww ฉันเดาว่าไม่จำเป็นต้องเขียนข้อความค้นหามากมายสำหรับฉัน! postgresql สำหรับผู้ชนะ
แดเนียลชิน

77

ในปี 2010 ฉันจะได้เข้าร่วมเขียนคำถามนี้และจะได้รับการโหวตอย่างมากJOINแต่ด้วยประสบการณ์ที่มากขึ้น (โดยเฉพาะอย่างยิ่งใน MySQL) ฉันสามารถระบุ: ใช่แบบสอบถามย่อยจะดีกว่า ฉันอ่านคำตอบหลายข้อที่นี่; แบบสอบถามย่อยที่ระบุบางรายการเร็วกว่า แต่ไม่มีคำอธิบายที่ดี ฉันหวังว่าฉันสามารถให้คำตอบที่ล่าช้า (มาก) นี้ได้:

ก่อนอื่นให้ฉันบอกว่าสำคัญที่สุด: มีคิวรีย่อยต่าง ๆ

และข้อความสำคัญที่สอง: เรื่องสำคัญ

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

คิวรีย่อยใน Select-Fields

SELECT moo, (SELECT roger FROM wilco WHERE moo = me) AS bar FROM foo

fooโปรดทราบว่าย่อยแบบสอบถามจะถูกดำเนินการสำหรับแถวที่เกิดจากทุกคน
หลีกเลี่ยงสิ่งนี้ถ้าเป็นไปได้ มันอาจทำให้แบบสอบถามของคุณช้าลงอย่างมากในชุดข้อมูลขนาดใหญ่ อย่างไรก็ตามหากแบบสอบถามย่อยไม่มีการอ้างอิงใด ๆfooสามารถปรับให้เหมาะสมโดย DB-server เป็นเนื้อหาแบบสแตติกและสามารถประเมินได้เพียงครั้งเดียว

แบบสอบถามย่อยในคำสั่งที่ไหน

SELECT moo FROM foo WHERE bar = (SELECT roger FROM wilco WHERE moo = me)

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

แบบสอบถามย่อยในการเข้าร่วมคำสั่ง

SELECT moo, bar 
  FROM foo 
    LEFT JOIN (
      SELECT MIN(bar), me FROM wilco GROUP BY me
    ) ON moo = me

สิ่งนี้น่าสนใจ เรารวมJOINกับแบบสอบถามย่อย และที่นี่เราได้รับความแข็งแกร่งที่แท้จริงของการสืบค้นย่อย ลองนึกภาพชุดข้อมูลที่มีนับล้านแถวในแต่เพียงไม่กี่ที่แตกต่างกันwilco meแทนที่จะเข้าร่วมกับโต๊ะขนาดใหญ่ตอนนี้เรามีตารางชั่วคราวที่เล็กกว่าเพื่อเข้าร่วม ซึ่งอาจส่งผลให้แบบสอบถามเร็วขึ้นมากขึ้นอยู่กับขนาดฐานข้อมูล คุณสามารถมีเอฟเฟกต์แบบเดียวกันกับCREATE TEMPORARY TABLE ...และINSERT INTO ... SELECT ...ซึ่งอาจให้การอ่านที่ดีขึ้นสำหรับเคียวรีที่ซับซ้อนมาก (แต่สามารถล็อกชุดข้อมูลในระดับการแยกการอ่านซ้ำที่ทำซ้ำได้)

แบบสอบถามย่อยย่อยแบบซ้อน

SELECT moo, bar
  FROM (
    SELECT moo, CONCAT(roger, wilco) AS bar
      FROM foo
      GROUP BY moo
      HAVING bar LIKE 'SpaceQ%'
  ) AS temp_foo
  ORDER BY bar

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

ข้อสรุป

ข้อความค้นหาย่อยจะไม่ถูกแทนที่สำหรับ a JOINและคุณไม่ควรใช้คำเหล่านี้ (แม้ว่าจะเป็นไปได้) CREATE TEMPORARY TABLE ...ในความต่ำต้อยของฉันที่ใช้ถูกต้องของแบบสอบถามย่อยคือการใช้แทนอย่างรวดเร็วของ ดีแบบสอบถามย่อยลดชุดข้อมูลในแบบที่คุณไม่สามารถประสบความสำเร็จในคำสั่งของON JOINหากแบบสอบถามย่อยมีหนึ่งในคำหลักGROUP BYหรือDISTINCTและไม่ควรอยู่ในเขตข้อมูลที่เลือกหรือคำสั่ง where ซึ่งก็อาจปรับปรุงประสิทธิภาพได้มาก


3
สำหรับSub-queries in the Join-statement: (1) การสร้างตารางที่ได้รับจากแบบสอบถามย่อยเองอาจใช้เวลานานมาก (2) ตารางที่ได้รับผลลัพธ์ไม่ได้จัดทำดัชนี ทั้งสองอย่างเดียวอาจทำให้ SQL ช้าลงอย่างมาก
jxc

@jxc ฉันสามารถพูดกับ MySQL ได้เท่านั้น (1) มีตารางชั่วคราวคล้ายกับการเข้าร่วม เวลาขึ้นอยู่กับปริมาณข้อมูล หากคุณไม่สามารถลดข้อมูลด้วยแบบสอบถามย่อยให้ใช้การเข้าร่วม (2) ถูกต้องมันขึ้นอยู่กับปัจจัยที่คุณสามารถลดข้อมูลในตารางชั่วคราว ฉันมีกรณีในโลกแห่งความเป็นจริงซึ่งฉันสามารถลดขนาดการรวมได้หลายล้านเป็นสองสามร้อยและลดเวลาการสืบค้นจากหลายวินาที (ด้วยการใช้ดัชนีเต็ม) เป็นหนึ่งในสี่ของวินาทีด้วยแบบสอบถามย่อย
Trendfischer

IMO: (1) ตารางชั่วคราวดังกล่าว (ตารางที่ได้รับ) ไม่ได้เกิดขึ้นดังนั้นทุกครั้งที่คุณเรียกใช้ SQL ตารางชั่วคราวจะต้องถูกสร้างขึ้นใหม่ซึ่งอาจมีค่าใช้จ่ายสูงและคอขวดจริง (เช่นการเรียกใช้กลุ่มโดยล้าน ของเร็กคอร์ด) (2) แม้ว่าคุณสามารถลดขนาดของตาราง temp เป็น10เร็กคอร์ดเนื่องจากไม่มีดัชนีซึ่งยังคงหมายถึงการเคียวรีเรกคอร์ดข้อมูล 9 ครั้งมากกว่า w / o ตารางเทมเพลตเมื่อเข้าร่วมตารางอื่น BTW ฉันมีปัญหานี้มาก่อนกับฐานข้อมูลของฉัน (MySQL) ในกรณีของฉันการใช้แบบสอบถามย่อยSELECT listอาจเร็วขึ้นมาก
jxc

@jxc ฉันไม่สงสัยเลยว่ามีตัวอย่างมากมายที่ใช้แบบสอบถามย่อยจะดีที่สุดน้อย ตามแนวทางปฏิบัติที่ดีคุณควรใช้EXPLAINแบบสอบถามก่อนเพิ่มประสิทธิภาพ ด้วยความเก่าที่set profiling=1คุณสามารถมองเห็นได้ง่ายหากตารางชั่วคราวเป็นคอขวด และแม้กระทั่งดัชนีที่ต้องใช้เวลาในการประมวลผล B-Trees จะปรับการสืบค้นสำหรับเรกคอร์ดให้เหมาะสม แต่ตาราง 10 เรคคอร์ดสามารถเร็วกว่าดัชนีสำหรับเรคคอร์ดนับล้าน แต่มันก็ขึ้นอยู่กับปัจจัยหลายอย่างเช่นขนาดและประเภทของฟิลด์
Trendfischer

1
ฉันชอบคำอธิบายของคุณจริงๆ ขอบคุณ.
unpairestgood

43

ก่อนอื่นเพื่อเปรียบเทียบสองข้อแรกคุณควรแยกเคียวรีด้วยเคียวรีย่อยกับ:

  1. คลาสของเคียวรีย่อยที่มีเคียวรีที่เทียบเท่าซึ่งเขียนด้วยการรวมเสมอ
  2. คลาสของแบบสอบถามย่อยที่ไม่สามารถเขียนใหม่ได้โดยใช้การรวม

สำหรับการสืบค้นระดับแรก RDBMS ที่ดีจะเห็นการรวมและการสืบค้นย่อยเท่ากันและจะสร้างแผนการสืบค้นที่เหมือนกัน

ทุกวันนี้แม้แต่ mysql ก็ทำเช่นนั้น

ถึงกระนั้นบางครั้งก็ไม่ได้ แต่นี่ไม่ได้หมายความว่าการเข้าร่วมจะชนะเสมอ - ฉันมีกรณีเมื่อใช้แบบสอบถามย่อยในการปรับปรุงประสิทธิภาพ mysql (ตัวอย่างเช่นหากมีบางสิ่งบางอย่างที่ป้องกัน mysql planner เพื่อประเมินค่าใช้จ่ายได้อย่างถูกต้องและหากผู้วางแผนไม่เห็นตัวแปรย่อยและตัวแปรย่อยเหมือนกัน

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

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


1
คุณสามารถให้ตัวอย่างของแบบสอบถามที่เขียนโดยใช้แบบสอบถามย่อยที่ไม่สามารถแปลงเป็นรวม (ชั้นที่สองในขณะที่คุณเรียกว่า)?
Zahra

24

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

(แม้ว่า Marcelo Cantos จะพูดถึงมัน)

ฉันจะยกตัวอย่างจากหลักสูตร Lagunita ของ Stanford ใน SQL

โต๊ะนักเรียน

+------+--------+------+--------+
| sID  | sName  | GPA  | sizeHS |
+------+--------+------+--------+
|  123 | Amy    |  3.9 |   1000 |
|  234 | Bob    |  3.6 |   1500 |
|  345 | Craig  |  3.5 |    500 |
|  456 | Doris  |  3.9 |   1000 |
|  567 | Edward |  2.9 |   2000 |
|  678 | Fay    |  3.8 |    200 |
|  789 | Gary   |  3.4 |    800 |
|  987 | Helen  |  3.7 |    800 |
|  876 | Irene  |  3.9 |    400 |
|  765 | Jay    |  2.9 |   1500 |
|  654 | Amy    |  3.9 |   1000 |
|  543 | Craig  |  3.4 |   2000 |
+------+--------+------+--------+

ใช้ตาราง

(การสมัครสำหรับมหาวิทยาลัยและสาขาวิชาเฉพาะ)

+------+----------+----------------+----------+
| sID  | cName    | major          | decision |
+------+----------+----------------+----------+
|  123 | Stanford | CS             | Y        |
|  123 | Stanford | EE             | N        |
|  123 | Berkeley | CS             | Y        |
|  123 | Cornell  | EE             | Y        |
|  234 | Berkeley | biology        | N        |
|  345 | MIT      | bioengineering | Y        |
|  345 | Cornell  | bioengineering | N        |
|  345 | Cornell  | CS             | Y        |
|  345 | Cornell  | EE             | N        |
|  678 | Stanford | history        | Y        |
|  987 | Stanford | CS             | Y        |
|  987 | Berkeley | CS             | Y        |
|  876 | Stanford | CS             | N        |
|  876 | MIT      | biology        | Y        |
|  876 | MIT      | marine biology | N        |
|  765 | Stanford | history        | Y        |
|  765 | Cornell  | history        | N        |
|  765 | Cornell  | psychology     | Y        |
|  543 | MIT      | CS             | N        |
+------+----------+----------------+----------+

ลองค้นหาคะแนน GPA สำหรับนักเรียนที่สมัครเข้าเรียนCSวิชาเอก (โดยไม่คำนึงถึงมหาวิทยาลัย)

ใช้แบบสอบถามย่อย:

select GPA from Student where sID in (select sID from Apply where major = 'CS');

+------+
| GPA  |
+------+
|  3.9 |
|  3.5 |
|  3.7 |
|  3.9 |
|  3.4 |
+------+

ค่าเฉลี่ยสำหรับชุดผลลัพธ์นี้คือ:

select avg(GPA) from Student where sID in (select sID from Apply where major = 'CS');

+--------------------+
| avg(GPA)           |
+--------------------+
| 3.6800000000000006 |
+--------------------+

ใช้การเข้าร่วม:

select GPA from Student, Apply where Student.sID = Apply.sID and Apply.major = 'CS';

+------+
| GPA  |
+------+
|  3.9 |
|  3.9 |
|  3.5 |
|  3.7 |
|  3.7 |
|  3.9 |
|  3.4 |
+------+

ค่าเฉลี่ยสำหรับชุดผลลัพธ์นี้:

select avg(GPA) from Student, Apply where Student.sID = Apply.sID and Apply.major = 'CS';

+-------------------+
| avg(GPA)          |
+-------------------+
| 3.714285714285714 |
+-------------------+

เห็นได้ชัดว่าความพยายามครั้งที่สองให้ผลลัพธ์ที่ทำให้เข้าใจผิดในกรณีที่เราใช้งานเนื่องจากมันนับซ้ำสำหรับการคำนวณค่าเฉลี่ย นอกจากนี้ยังเห็นได้ชัดว่าการใช้งานdistinctด้วยคำสั่งการเข้าร่วมจะไม่ขจัดปัญหาเนื่องจากจะทำให้เกิดการผิดพลาดหนึ่งในสามของ3.9คะแนน กรณีที่ถูกต้องคือการบัญชีสำหรับสอง (2)การเกิดขึ้นของ3.9คะแนนที่กำหนดว่าเราจะมีสอง (2)นักเรียนที่มีคะแนนที่สอดคล้องกับเกณฑ์แบบสอบถามของเรา

ดูเหมือนว่าในบางกรณีแบบสอบถามย่อยเป็นวิธีที่ปลอดภัยที่สุดนอกเหนือไปจากปัญหาด้านประสิทธิภาพ


ฉันคิดว่าคุณไม่สามารถใช้แบบสอบถามย่อยได้ที่นี่ นี่ไม่ใช่กรณีที่คุณสามารถใช้เหตุผลอย่างใดอย่างหนึ่ง แต่ให้คำตอบที่ผิดเพราะการใช้งานทางเทคนิคของมัน นี่เป็นกรณีที่คุณไม่สามารถใช้คิวรีย่อยได้เนื่องจากนักเรียนที่ไม่ได้เป็นสมาชิกของ CS สามารถให้คะแนน 3.9 ซึ่งอยู่ในรายการคะแนน IN บริบทของ CS หายไปเมื่อมีการดำเนินการค้นหาย่อยซึ่งไม่ใช่สิ่งที่เราต้องการในเชิงตรรกะ ดังนั้นนี่ไม่ใช่ตัวอย่างที่ดีที่สามารถใช้ได้ การใช้คิวรีย่อยเป็นแนวคิด / ผิดเหตุผลสำหรับกรณีใช้งานนี้แม้ว่าโชคดีที่มันให้ผลลัพธ์ที่ถูกต้องสำหรับชุดข้อมูลอื่น
Saurabh Patil

22

เอกสาร MSDN สำหรับ SQL Server กล่าว

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

ดังนั้นถ้าคุณต้องการอะไร

select * from t1 where exists select * from t2 where t2.parent=t1.id

ลองใช้การเข้าร่วมแทน ในกรณีอื่น ๆ มันไม่ทำให้เกิดความแตกต่าง

ฉันพูดว่า: การสร้างฟังก์ชั่นสำหรับเคียวรีย่อยช่วยขจัดปัญหาของ cluttter และอนุญาตให้คุณใช้ตรรกะเพิ่มเติมกับเคียวรี่ย่อย ดังนั้นฉันขอแนะนำให้สร้างฟังก์ชั่นสำหรับเคียวรีย่อยเมื่อทำได้

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


9
การแทนที่เคียวรีย่อยด้วยฟังก์ชั่นเป็นความคิดที่ไม่ดีมากในการทำงานของ RDBMS (เช่น Oracle) ดังนั้นฉันจึงแนะนำตรงกันข้าม: ใช้เคียวรีย่อย / รวมเข้าด้วยกันแทนฟังก์ชั่นที่เป็นไปได้
Frank Schmitt

3
@ FrankSchmitt โปรดสนับสนุนการโต้แย้งของคุณด้วยการอ้างอิง
UğurGümüşhan

2
นอกจากนี้ยังมีกรณีที่คุณควรใช้แบบสอบถามย่อยแทนเข้าร่วมแม้ว่าคุณจะตรวจสอบสำหรับการดำรงอยู่: NOT EXISTSถ้าคุณตรวจสอบ การNOT EXISTSชนะมากกว่า a LEFT OUTER JOIN ด้วยเหตุผลต่าง ๆ : preformance, ความล้มเหลว - ปลอดภัย (ในกรณีของคอลัมน์ nulable) และความสามารถในการอ่าน sqlperformance.com/2012/12/12/t-sql-queries/left-anti-semi-join
Tim Schmelter

16

ทำงานบนฐานข้อมูลขนาดใหญ่มากจาก Mambo CMS เก่า:

SELECT id, alias
FROM
  mos_categories
WHERE
  id IN (
    SELECT
      DISTINCT catid
    FROM mos_content
  );

0 วินาที

SELECT
  DISTINCT mos_content.catid,
  mos_categories.alias
FROM
  mos_content, mos_categories
WHERE
  mos_content.catid = mos_categories.id;

ประมาณ 3 วินาที

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

และ...

SELECT
  DISTINCT mos_categories.id,
  mos_categories.alias
FROM
  mos_content, mos_categories
WHERE
  mos_content.catid = mos_categories.id;

0 วินาที

ตรวจสอบผลลัพธ์เดียวกันอีกครั้งตรวจสอบจำนวนแถวเดียวกัน ฉันเดาว่า DISTINCT mos_content.catid ใช้เวลานานกว่าจะคิดออกมากกว่า DISTINCT mos_categories.id


1
ฉันต้องการทราบเพิ่มเติมเกี่ยวกับสิ่งที่คุณพยายามชี้ให้เห็นในบรรทัดสุดท้าย "เดาของฉันคือ DISTINCT mos_content.catid DISTINCT ใช้เวลานานกว่าจะคิดออกมากกว่า mos_categories.id DISTINCT" . คุณกำลังบอกว่าควรจะตั้งชื่อ id เท่านั้นidและไม่ได้ตั้งชื่อบางอย่างเช่นcatid? พยายามเพิ่มประสิทธิภาพการเข้าถึงฐานข้อมูลของฉันและการเรียนรู้ของคุณอาจช่วยได้
bool.dev

2
การใช้ SQL IN ในกรณีนั้นเป็นการปฏิบัติที่ไม่ดีและไม่ได้พิสูจน์อะไรเลย
UğurGümüşhan

15

จากการสังเกตของฉันเช่นสองกรณีหากตารางมีน้อยกว่า 100,000 บันทึกการเข้าร่วมจะทำงานได้อย่างรวดเร็ว

แต่ในกรณีที่ตารางมีระเบียนมากกว่า 100,000 รายการแบบสอบถามย่อยจะได้ผลลัพธ์ที่ดีที่สุด

ฉันมีตารางหนึ่งตารางที่มี 500,000 ระเบียนที่ฉันสร้างไว้ด้านล่างข้อความค้นหาและเวลาที่ผลลัพธ์เป็นเช่นนั้น

SELECT * 
FROM crv.workorder_details wd 
inner join  crv.workorder wr on wr.workorder_id = wd.workorder_id;

ผลลัพธ์: 13.3 วินาที

select * 
from crv.workorder_details 
where workorder_id in (select workorder_id from crv.workorder)

ผลลัพธ์: 1.65 วินาที


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

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

ฉันเห็นด้วยกับ Ajay Gajera ฉันได้เห็นสิ่งนี้ด้วยตัวเองแล้ว
user1735921

14
การเปรียบเทียบประสิทธิภาพของการค้นหาสองรายการที่ให้ผลลัพธ์ที่แตกต่างกันอย่างไร
Paul Spiegel

ใช่มันเป็นคำค้นหาที่แตกต่างกัน แต่กลับผลลัพธ์เหมือนเดิม
king neo

12

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

   SELECT title FROM books WHERE author_id = (SELECT id FROM authors WHERE last_name = 'Bar' AND first_name = 'Foo');

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

   SELECT title FROM books WHERE author_id IN (SELECT id FROM authors WHERE last_name ~ '^[A-E]');

สิ่งนี้แตกต่างอย่างชัดเจนจากการพูดว่า LEFT-JOIN ที่คุณต้องการเข้าร่วมจากตาราง A และ B แม้ว่าเงื่อนไขการเข้าร่วมจะไม่พบระเบียนใด ๆ ที่ตรงกันในตาราง B เป็นต้น

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


11

รุ่น MySQL: 5.5.28-0ubuntu0.12.04.2-log

ฉันยังรู้สึกว่าเข้าร่วมได้ดีกว่าแบบสอบถามย่อยใน MySQL เสมอ แต่อธิบายเป็นวิธีที่ดีกว่าในการตัดสิน นี่คือตัวอย่างที่แบบสอบถามย่อยทำงานได้ดีกว่า JOIN

นี่คือแบบสอบถามของฉันกับ 3 แบบสอบถามย่อย:

EXPLAIN SELECT vrl.list_id,vrl.ontology_id,vrl.position,l.name AS list_name, vrlih.position AS previous_position, vrl.moved_date 
FROM `vote-ranked-listory` vrl 
INNER JOIN lists l ON l.list_id = vrl.list_id 
INNER JOIN `vote-ranked-list-item-history` vrlih ON vrl.list_id = vrlih.list_id AND vrl.ontology_id=vrlih.ontology_id AND vrlih.type='PREVIOUS_POSITION' 
INNER JOIN list_burial_state lbs ON lbs.list_id = vrl.list_id AND lbs.burial_score < 0.5 
WHERE vrl.position <= 15 AND l.status='ACTIVE' AND l.is_public=1 AND vrl.ontology_id < 1000000000 
 AND (SELECT list_id FROM list_tag WHERE list_id=l.list_id AND tag_id=43) IS NULL 
 AND (SELECT list_id FROM list_tag WHERE list_id=l.list_id AND tag_id=55) IS NULL 
 AND (SELECT list_id FROM list_tag WHERE list_id=l.list_id AND tag_id=246403) IS NOT NULL 
ORDER BY vrl.moved_date DESC LIMIT 200;

อธิบายแสดง:

+----+--------------------+----------+--------+-----------------------------------------------------+--------------+---------+-------------------------------------------------+------+--------------------------+
| id | select_type        | table    | type   | possible_keys                                       | key          | key_len | ref                                             | rows | Extra                    |
+----+--------------------+----------+--------+-----------------------------------------------------+--------------+---------+-------------------------------------------------+------+--------------------------+
|  1 | PRIMARY            | vrl      | index  | PRIMARY                                             | moved_date   | 8       | NULL                                            |  200 | Using where              |
|  1 | PRIMARY            | l        | eq_ref | PRIMARY,status,ispublic,idx_lookup,is_public_status | PRIMARY      | 4       | ranker.vrl.list_id                              |    1 | Using where              |
|  1 | PRIMARY            | vrlih    | eq_ref | PRIMARY                                             | PRIMARY      | 9       | ranker.vrl.list_id,ranker.vrl.ontology_id,const |    1 | Using where              |
|  1 | PRIMARY            | lbs      | eq_ref | PRIMARY,idx_list_burial_state,burial_score          | PRIMARY      | 4       | ranker.vrl.list_id                              |    1 | Using where              |
|  4 | DEPENDENT SUBQUERY | list_tag | ref    | list_tag_key,list_id,tag_id                         | list_tag_key | 9       | ranker.l.list_id,const                          |    1 | Using where; Using index |
|  3 | DEPENDENT SUBQUERY | list_tag | ref    | list_tag_key,list_id,tag_id                         | list_tag_key | 9       | ranker.l.list_id,const                          |    1 | Using where; Using index |
|  2 | DEPENDENT SUBQUERY | list_tag | ref    | list_tag_key,list_id,tag_id                         | list_tag_key | 9       | ranker.l.list_id,const                          |    1 | Using where; Using index |
+----+--------------------+----------+--------+-----------------------------------------------------+--------------+---------+-------------------------------------------------+------+--------------------------+

แบบสอบถามเดียวกันกับ JOIN คือ:

EXPLAIN SELECT vrl.list_id,vrl.ontology_id,vrl.position,l.name AS list_name, vrlih.position AS previous_position, vrl.moved_date 
FROM `vote-ranked-listory` vrl 
INNER JOIN lists l ON l.list_id = vrl.list_id 
INNER JOIN `vote-ranked-list-item-history` vrlih ON vrl.list_id = vrlih.list_id AND vrl.ontology_id=vrlih.ontology_id AND vrlih.type='PREVIOUS_POSITION' 
INNER JOIN list_burial_state lbs ON lbs.list_id = vrl.list_id AND lbs.burial_score < 0.5 
LEFT JOIN list_tag lt1 ON lt1.list_id = vrl.list_id AND lt1.tag_id = 43 
LEFT JOIN list_tag lt2 ON lt2.list_id = vrl.list_id AND lt2.tag_id = 55 
INNER JOIN list_tag lt3 ON lt3.list_id = vrl.list_id AND lt3.tag_id = 246403 
WHERE vrl.position <= 15 AND l.status='ACTIVE' AND l.is_public=1 AND vrl.ontology_id < 1000000000 
AND lt1.list_id IS NULL AND lt2.tag_id IS NULL 
ORDER BY vrl.moved_date DESC LIMIT 200;

และผลลัพธ์คือ:

+----+-------------+-------+--------+-----------------------------------------------------+--------------+---------+---------------------------------------------+------+----------------------------------------------+
| id | select_type | table | type   | possible_keys                                       | key          | key_len | ref                                         | rows | Extra                                        |
+----+-------------+-------+--------+-----------------------------------------------------+--------------+---------+---------------------------------------------+------+----------------------------------------------+
|  1 | SIMPLE      | lt3   | ref    | list_tag_key,list_id,tag_id                         | tag_id       | 5       | const                                       | 2386 | Using where; Using temporary; Using filesort |
|  1 | SIMPLE      | l     | eq_ref | PRIMARY,status,ispublic,idx_lookup,is_public_status | PRIMARY      | 4       | ranker.lt3.list_id                          |    1 | Using where                                  |
|  1 | SIMPLE      | vrlih | ref    | PRIMARY                                             | PRIMARY      | 4       | ranker.lt3.list_id                          |  103 | Using where                                  |
|  1 | SIMPLE      | vrl   | ref    | PRIMARY                                             | PRIMARY      | 8       | ranker.lt3.list_id,ranker.vrlih.ontology_id |   65 | Using where                                  |
|  1 | SIMPLE      | lt1   | ref    | list_tag_key,list_id,tag_id                         | list_tag_key | 9       | ranker.lt3.list_id,const                    |    1 | Using where; Using index; Not exists         |
|  1 | SIMPLE      | lbs   | eq_ref | PRIMARY,idx_list_burial_state,burial_score          | PRIMARY      | 4       | ranker.vrl.list_id                          |    1 | Using where                                  |
|  1 | SIMPLE      | lt2   | ref    | list_tag_key,list_id,tag_id                         | list_tag_key | 9       | ranker.lt3.list_id,const                    |    1 | Using where; Using index                     |
+----+-------------+-------+--------+-----------------------------------------------------+--------------+---------+---------------------------------------------+------+----------------------------------------------+

การเปรียบเทียบคอลัมน์บอกความแตกต่างและการสืบค้นโดยร่วมใช้rowsUsing temporary; Using filesort

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

หากฉันไม่มี INNER JOIN บนlist_tagโต๊ะเช่นถ้าฉันลบ

AND (SELECT list_id FROM list_tag WHERE list_id=l.list_id AND tag_id=246403) IS NOT NULL  

จากแบบสอบถามแรกและสอดคล้องกัน:

INNER JOIN list_tag lt3 ON lt3.list_id = vrl.list_id AND lt3.tag_id = 246403

จากแบบสอบถามที่สองจากนั้น EXPLAIN จะส่งกลับจำนวนแถวเท่ากันสำหรับทั้งแบบสอบถามและแบบสอบถามทั้งสองนี้ทำงานอย่างรวดเร็วเท่าเทียมกัน


ฉันมีสถานการณ์ที่คล้ายกัน แต่มีการเข้าร่วมมากกว่าคุณจะพยายามอธิบายอีกครั้ง
pahnin

ใน Oracle หรือ PostgreSQL ฉันจะลองแล้ว: และไม่ใช่ EXISTS (เลือก 1 จาก list_tag WHERE list_id = l.list_id และ tag_id ใน (43, 55, 246403))
David Aldridge

11

แบบสอบถามย่อยมีความสามารถในการคำนวณฟังก์ชั่นการรวมตัวได้ทันที เช่นหาราคาหนังสือน้อยที่สุดและรับหนังสือทั้งหมดที่ขายในราคานี้ 1) การใช้แบบสอบถามย่อย:

SELECT titles, price
FROM Books, Orders
WHERE price = 
(SELECT MIN(price)
 FROM Orders) AND (Books.ID=Orders.ID);

2) ใช้ JOIN

SELECT MIN(price)
     FROM Orders;
-----------------
2.99

SELECT titles, price
FROM Books b
INNER JOIN  Orders o
ON b.ID = o.ID
WHERE o.price = 2.99;

อีกกรณีหนึ่ง: หลายรายการGROUP BYที่มีตารางแตกต่างกัน: stackoverflow.com/questions/11415284/…คำถามย่อยดูเหมือนจะเข้มงวดกว่าทั่วไป ดูเพิ่มเติมที่ MySQL man: dev.mysql.com/doc/refman/5.7/en/optimizing-subqueries.html | dev.mysql.com/doc/refman/5.7/en/rewriting-subqueries.html
Ciro Santilli 冠状病毒审查审查六四事件法轮功

6
-1 สิ่งนี้ทำให้เข้าใจผิดในขณะที่คุณใช้แบบสอบถามย่อยและเข้าร่วมทั้งสองตัวอย่าง ว่าคุณได้ดึงแบบสอบถามย่อยออกเป็นแบบสอบถามที่สองเพื่อตรวจสอบว่าราคาสั่งซื้อต่ำสุดไม่มีผลเนื่องจากฐานข้อมูลจะทำสิ่งเดียวกัน นอกจากนี้คุณไม่ได้เขียนการเข้าร่วมใหม่โดยใช้แบบสอบถามย่อย แบบสอบถามทั้งสองใช้การเข้าร่วม คุณมีความถูกต้องที่ subqueries อนุญาตให้ฟังก์ชันการรวม แต่ตัวอย่างนี้ไม่ได้แสดงให้เห็นถึงความจริงที่ว่า
David Harkness

ฉันเห็นด้วยกับเดวิดและคุณสามารถใช้กลุ่มเพื่อรับราคาต่ำสุด
user1735921

9
  • กฎทั่วไปคือการเข้าร่วมจะเร็วขึ้นในกรณีส่วนใหญ่ (99%)
  • ยิ่งมีตารางข้อมูลมากเท่าใดแบบสอบถามย่อยก็จะช้าลง
  • น้อยกว่าที่ตารางข้อมูลจะมีsubqueriesมีความเร็วเทียบเท่าเป็นร่วม
  • subqueriesมีความเรียบง่ายเข้าใจง่ายและง่ายต่อการอ่าน
  • ที่สุดของเว็บและ app กรอบและ "ออม" ของพวกเขาและ "บันทึกที่ใช้งาน" s สร้างแบบสอบถามsubqueriesเพราะมีsubqueriesจะง่ายต่อการรับผิดชอบแยกรักษารหัส ฯลฯ
  • สำหรับเว็บไซต์ขนาดเล็กหรือแอพพลิเคsubqueriesจะ OK แต่สำหรับเว็บไซต์ที่มีขนาดใหญ่และแอปที่คุณมักจะต้องเขียนคำสั่งสร้างขึ้นเพื่อเข้าร่วมแบบสอบถามโดยเฉพาะถ้าแบบสอบถามใช้หลายsubqueriesในแบบสอบถาม

บางคนบอกว่า "บาง RDBMS สามารถเขียนแบบสอบถามย่อยไปสู่การเข้าร่วมหรือเข้าร่วมกับแบบสอบถามย่อยเมื่อมันคิดว่าหนึ่งจะเร็วกว่าที่อื่น ๆ ." แต่คำสั่งนี้นำไปใช้กับกรณีที่เรียบง่ายแน่นอนไม่ได้สำหรับคำสั่งที่ซับซ้อนกับsubqueriesซึ่งอันที่จริงสาเหตุ ปัญหาในการปฏิบัติงาน


> แต่ข้อความนี้ใช้กับกรณีง่าย ๆ ฉันเข้าใจว่าเป็นกรณีง่าย ๆ ที่สามารถเขียนใหม่เป็น "JOIN" โดย RDBMS หรือเป็นกรณีที่ซับซ้อนเช่นกรณีย่อยที่เหมาะสมที่นี่ :-) จุดดีใน ORMs ฉันคิดว่านี่มีผลกระทบมากที่สุด
pilat

4

ความแตกต่างจะเห็นก็ต่อเมื่อตารางการเข้าร่วมที่สองมีข้อมูลมากกว่าตารางหลักอย่างมาก ฉันมีประสบการณ์ด้านล่าง ...

เรามีตารางผู้ใช้หนึ่งแสนรายการและข้อมูลสมาชิก (มิตรภาพ) ของพวกเขาประมาณ 3 แสนรายการ มันเป็นคำสั่งเข้าร่วมเพื่อนำเพื่อนและข้อมูลของพวกเขา แต่มีความล่าช้ามาก แต่มันก็ทำงานได้ดีที่มีข้อมูลเพียงเล็กน้อยในตารางสมาชิก เมื่อเราเปลี่ยนให้ใช้แบบสอบถามย่อยมันทำงานได้ดี

แต่ในขณะเดียวกันคิวรีแบบร่วมจะทำงานกับตารางอื่นที่มีรายการน้อยกว่าตารางหลัก

ดังนั้นฉันคิดว่าคำสั่งการเข้าร่วมและการสืบค้นย่อยทำงานได้ดีและขึ้นอยู่กับข้อมูลและสถานการณ์


3

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


1

ฉันแค่คิดเกี่ยวกับปัญหาเดียวกัน แต่ฉันกำลังใช้แบบสอบถามย่อยในส่วนจาก ฉันต้องการเชื่อมต่อและสืบค้นจากตารางขนาดใหญ่ตาราง "slave" มี 28 ล้านเร็กคอร์ด แต่ผลลัพธ์นั้นมีเพียง 128 ผลลัพธ์ที่ทำให้มีข้อมูลขนาดใหญ่น้อยมาก! ฉันใช้ฟังก์ชัน MAX () กับมัน

ครั้งแรกที่ฉันใช้ LEFT JOIN เพราะฉันคิดว่านั่นเป็นวิธีที่ถูกต้อง mysql สามารถปรับให้เหมาะสมเป็นต้นเป็นครั้งที่สองสำหรับการทดสอบฉันเขียนซ้ำเพื่อเลือกย่อยกับ JOIN

รันไทม์ LEFT JOIN: 1.12s รันไทม์ SUB-SELECT: 0.06 วินาที

การเลือกย่อยเร็วกว่าการเข้าร่วม 18 เท่า! เพียงแค่ใน chokito adv การเลือกย่อยดูแย่มาก แต่ผลลัพธ์ ...


-1

หากคุณต้องการเพิ่มความเร็วการสืบค้นโดยใช้การเข้าร่วม:

สำหรับ "Inner join / join" อย่าใช้เงื่อนไขใดแทนใช้ในเงื่อนไข "ON" เช่น:

     select id,name from table1 a  
   join table2 b on a.name=b.name
   where id='123'

 Try,

    select id,name from table1 a  
   join table2 b on a.name=b.name and a.id='123'

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


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