SQL Server IN เทียบกับ EXISTS Performance


115

ฉันสงสัยว่าข้อใดต่อไปนี้จะมีประสิทธิภาพมากกว่ากัน

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

WHERE EXISTS (SELECT * FROM Base WHERE bx.BoxID = Base.BoxID AND [Rank] = 2)

เมื่อเทียบกับ

WHERE bx.BoxID IN (SELECT BoxID FROM Base WHERE [Rank = 2])

8
วิธีที่ดีที่สุดในการหาคำตอบคือลองใช้และทำบางอย่างให้มั่นใจ
Klaus Byskov Pedersen

10
มีได้จะเป็นรายการที่ซ้ำกัน gazillion สำหรับเรื่องนี้ ......
marc_s

5
@marc_s - อาจจะเป็นเช่นนั้น แต่ในช่วงเวลาหนึ่งมันจะต้องพาฉันไปดูโพสต์ทั้งหมดในเรื่องนี้และพบว่าสิ่งที่เหมาะกับกรณีของฉันฉันมีคำตอบสี่ข้อสำหรับคำถามของฉัน
Randy Minder

7
FYI ถ้าคุณกำลังที่ต้องการมากที่สุดวิธี performant คุณสามารถselect 1 from Base...ในของคุณwhere existsเนื่องจากคุณไม่ดูแลจริงเกี่ยวกับผลเพียงว่าแถวที่มีอยู่จริง
brad

2
@marc_s ที่น่าเศร้าจริงๆเพราะฉันใช้เวลาในการดูโพสต์เพื่อที่จะไม่เพิ่มถังขยะใน stackoverflow ฉันไม่ต้องการคำตอบที่เหมาะสำหรับการทำงานให้เสร็จ นั่นคือความคิดที่เพิ่มรายการที่ซ้ำกันของ Gazillion แทนที่เพียงไม่กี่รายการที่มีคำตอบที่ดี
IvoC

คำตอบ:


140

EXISTS จะเร็วขึ้นเพราะเมื่อเครื่องยนต์พบการชนแล้วก็จะเลิกมองตามเงื่อนไขที่พิสูจน์แล้วว่าเป็นจริง

ด้วยINมันจะรวบรวมผลลัพธ์ทั้งหมดจากแบบสอบถามย่อยก่อนที่จะประมวลผลต่อไป


4
นั่นเป็นจุดที่ดี คำสั่ง IN ต้องการ SQL Server เพื่อสร้างชุดผลลัพธ์ที่สมบูรณ์จากนั้นสร้างคำสั่ง IF ขนาดใหญ่ที่ฉันคิด
Randy Minder

72
สิ่งนี้เคยเป็นจริง แต่ในเวอร์ชันปัจจุบัน (อย่างน้อยปี 2008) เครื่องมือเพิ่มประสิทธิภาพนั้นฉลาดกว่ามาก ... มันปฏิบัติต่อ IN () เช่นเดียวกับ EXISTS ()
Aaron Bertrand

11
@Aaron - ใช่โดยทั่วไปแล้วเครื่องมือเพิ่มประสิทธิภาพจะสร้างแผนการที่ดีขึ้นภายใน อย่างไรก็ตามการใช้ทางลัดภายในอาจเป็นอันตรายในสถานการณ์ที่ซับซ้อนมากขึ้น
Scott Coates

2
นี่เป็นเพียงความผิดพลาด ในปี 2010 และยังคงเป็น
Magnus

2
IN และ EXISTS มีแผนการสืบค้นเดียวกันและ IO ไม่มีเหตุผลที่จะคิดว่าพวกเขามีประสิทธิภาพที่แตกต่างกัน ตรวจสอบสถิติเวลาของคุณและคำนวณตัวเอง
Nelssen

40

คำตอบที่ได้รับการยอมรับคือสายตาสั้นและคำถามค่อนข้างหลวมใน:

1) ไม่ได้กล่าวถึงอย่างชัดเจนว่ามีดัชนีที่ปิดอยู่ทางซ้ายขวาหรือทั้งสองด้าน

2) ไม่คำนึงถึงขนาดของชุดอินพุตด้านซ้ายและชุดด้านขวาของอินพุต
(คำถามกล่าวถึงชุดผลลัพธ์ขนาดใหญ่โดยรวม)

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

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


3
ไม่รู้ว่าทำไมคำตอบที่ยอดเยี่ยมนี้จึงไม่ได้รับความสนใจอีกต่อไป การทำความเข้าใจดัชนี / โครงสร้างของทั้งสองฝ่ายอาจส่งผลต่อฉันเห็นด้วย พูดได้ดี.
SheldonH

เพิ่มประสิทธิภาพเสมอ จะช่วยให้แผนเดียวกันสำหรับและIN EXISTSลองคิดดูในกรณีที่พวกเขาไม่ได้รับแผนเดียวกัน (แม้ว่าจะใช้ไม่ได้NOT INและNOT EXISTS)
Martin Smith

@MartinSmith ฉันคิดว่าคุณรู้ว่าคุณกำลังพูดถึงอะไร แต่คุณมีหลักฐานว่าแผนการนั้นเหมือนกันเสมอหรือไม่? ถ้าเป็นเช่นนั้นก็จะล้างความขัดแย้งที่ไม่เห็นด้วยมาตลอดทศวรรษที่นี่
MarredCheese

@MarredCheese - ความรับผิดชอบอยู่ที่คนที่อ้างว่าแตกต่างกันในการสร้างตัวอย่างเดียวนี้
Martin Smith

37

ฉันได้ทำการทดสอบบน SQL Server 2005 และ 2008 แล้วและทั้ง EXISTS และ IN กลับมาพร้อมกับแผนการดำเนินการจริงที่เหมือนกันทุกประการตามที่อื่น ๆ ได้ระบุไว้ เครื่องมือเพิ่มประสิทธิภาพเหมาะสมที่สุด :)

สิ่งที่ควรทราบบางครั้ง EXISTS IN และ JOIN อาจส่งคืนผลลัพธ์ที่แตกต่างกันหากคุณไม่ใช้วลีที่ถูกต้อง: http://weblogs.sqlteam.com/mladenp/archive/2007/05/18/60210 ขอบ


5

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

มีคำหลักหลายคำในภาษา SQL (T-) แต่ในท้ายที่สุดสิ่งเดียวที่เกิดขึ้นจริงบนฮาร์ดแวร์คือการดำเนินการตามที่เห็นในแผนการสืบค้นการดำเนินการ

การดำเนินการเชิงสัมพันธ์ (ทฤษฎีคณิตศาสตร์) ที่เราทำเมื่อเราเรียกใช้[NOT] INและ[NOT] EXISTSเป็นการเข้าร่วมกึ่ง (ต่อต้านการเข้าร่วมเมื่อใช้NOT) มันไม่ได้เป็นเรื่องบังเอิญว่าสอดคล้องการดำเนินงาน SQL เซิร์ฟเวอร์มีชื่อเดียวกัน ไม่มีการดำเนินการใด ๆ ที่กล่าวถึงINหรือEXISTSที่ใดก็ได้ - มีเพียงการรวมกึ่ง (ต่อต้าน) เท่านั้น ดังนั้นจึงมีวิธีการที่มีเหตุผลเทียบเท่าไม่มีINเทียบกับEXISTSทางเลือกที่อาจส่งผลกระทบต่อประสิทธิภาพการทำงานเนื่องจากมีหนึ่งและวิธีเดียวที่ (ป้องกัน) กึ่งเข้าร่วมการดำเนินงานการดำเนินการที่จะได้รับผลของพวกเขา

ตัวอย่าง:

แบบสอบถาม 1 ( แผน )

select * from dt where dt.customer in (select c.code from customer c where c.active=0)

แบบสอบถาม 2 ( แผน )

select * from dt where exists (select 1 from customer c where c.code=dt.customer and c.active=0)

คุณได้ทดสอบหรือไม่? ถ้าเป็นเช่นนั้นคุณสามารถแบ่งปัน SQL และผลลัพธ์ของคุณได้หรือไม่
UnhandledExcepSean

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

5

ฉันจะไปกับ EXISTS มากกว่า IN ดูลิงค์ด้านล่าง:

SQL Server: JOIN vs IN เทียบกับ EXISTS - ความแตกต่างทางตรรกะ

มีความเข้าใจผิดทั่วไปว่า IN มีพฤติกรรมเท่าเทียมกันกับ EXISTS หรือ JOIN ในแง่ของผลลัพธ์ที่ส่งกลับ นี้เป็นเพียงไม่เป็นความจริง.

IN:ส่งคืนจริงหากค่าที่ระบุตรงกับค่าใด ๆ ในแบบสอบถามย่อยหรือรายการ

มีอยู่:ส่งกลับค่าจริงหากคิวรีย่อยมีแถวใด ๆ

เข้าร่วม:เข้าร่วม 2 ชุดผลลัพธ์ในคอลัมน์การเข้าร่วม

เครดิตบล็อก: https://stackoverflow.com/users/31345/mladen-prajdic


ว้าวขอบคุณสำหรับบล็อกและคำอธิบายของคุณ
Christian Müller

3

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


3

ดังนั้น IN จึงไม่เหมือนกับ EXISTS และจะสร้างแผนการดำเนินการเดียวกัน

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

โดยปกติแล้ว IN จะถูกใช้โดยไม่มีความสัมพันธ์ของแบบสอบถามภายในกับแบบสอบถามภายนอกและสามารถแก้ไขได้ในขั้นตอนเดียวเท่านั้น (ในกรณีที่ดีที่สุด)

พิจารณาสิ่งนี้:

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

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

  3. ทั้งหมดข้างต้นจะสังเกตเห็นเมื่อคุณมีแถวจำนวนพอสมควรในแต่ละตาราง (โดยธรรมฉันหมายถึงสิ่งที่เกินการประมวลผล CPU ของคุณและ / หรือขีด จำกัด แรมสำหรับการแคช)

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

เคล็ดลับคือการ จำกัด จำนวนแถวที่จะสแกน

ความนับถือ,

MarianoC


1

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

ดังนั้น:

WHERE EXISTS (SELECT TOP 1 1 FROM Base WHERE bx.BoxID = Base.BoxID AND [Rank] = 2)

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


ฉันมักจะระมัดระวังอย่างมากในการใช้การเข้ารหัส LEFT JOIN + NULL เพราะมันง่ายมากที่จะพลาดหรือผลลัพธ์ที่บิดเบี้ยวหากคุณไม่ระมัดระวังในการจัดการ NULL ของคุณ ฉันไม่ค่อยพบสถานการณ์ที่ EXISTS หรือ CTE (สำหรับการค้นหาการทำซ้ำหรือการแทรกข้อมูลสังเคราะห์สำหรับข้อมูลที่ขาดหายไป) ทั้งคู่ไม่ตรงตามข้อกำหนดเดียวกันและมีประสิทธิภาพดีกว่า LEFT JOIN + NULL
Josh Lewis

3
อันดับ 1 ควรเป็นข้อมูลที่ไม่เกี่ยวข้อง (หรือเหตุการณ์ซ้ำซ้อน) เมื่อใช้กับ EXISTS EXISTS จะส่งคืนทันทีที่พบแถวที่ตรงกัน
Karl Kieninger

ฉันไม่เห็นประโยชน์ด้านประสิทธิภาพใด ๆ กับแนวทางนี้ โปรดแสดงภาพหน้าจอของแผนการดำเนินการ
DaFi4

-1

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

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