SQL ซ้ายเข้าร่วม vs หลายตารางในบรรทัดจาก?


256

ภาษาถิ่น SQL ส่วนใหญ่ยอมรับทั้งแบบสอบถามต่อไปนี้:

SELECT a.foo, b.foo
FROM a, b
WHERE a.x = b.x

SELECT a.foo, b.foo
FROM a
LEFT JOIN b ON a.x = b.x

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


1
Guffa: คุณค้นพบได้อย่างไร? แม้ว่าคำถามของฉันคือการปฏิบัติที่ดีที่สุดมากกว่า "ฉันจะ"
jmucchiello

เนื่องจากเป็นแนวปฏิบัติที่ดีที่สุดโปรดทำสิ่งนี้เป็น Wiki
Binoj Antony

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

@ahnbizcad ข้อความค้นหาสองข้อความที่ระบุไม่ได้ทำสิ่งเดียวกัน ครั้งแรกที่ผลตอบแทนเช่นเดียวกับการเข้าร่วมภายใน การใช้งานเป็นรุ่นเฉพาะของ DBMS และยังมีการรับประกันเพียงเล็กน้อย แต่การแปลง DBMS ที่เทียบเท่ากับกรณีของเครื่องหมายจุลภาคและเข้าร่วมภายใน / เมื่อเทียบกับข้ามเข้าร่วมซึ่งเป็นเรื่องเล็กน้อย เรียนรู้เกี่ยวกับการปรับให้เหมาะสม / การใช้งานการสืบค้นฐานข้อมูลที่เกี่ยวข้อง
philipxy

ได้รับการแนะนำทรัพยากร? คู่มือขนาดมหึมาที่หนาแน่นเป็นสาเหตุที่ฉันพยายามเรียนรู้จากที่นี่
ahnbizcad

คำตอบ:


319

ไวยากรณ์เก่ามีเพียงรายชื่อตารางและการใช้WHEREข้อเพื่อระบุเกณฑ์การเข้าร่วมจะถูกคัดค้านในฐานข้อมูลที่ทันสมัยที่สุด

มันไม่ได้เป็นเพียงการแสดงไวยากรณ์เก่ามีความเป็นไปได้ที่จะคลุมเครือเมื่อคุณใช้ทั้ง INNER และ OUTER รวมในแบบสอบถามเดียวกัน

ผมขอยกตัวอย่างให้คุณ

สมมติว่าคุณมี 3 ตารางในระบบของคุณ:

Company
Department
Employee

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

ตกลงดังนั้นตอนนี้คุณต้องการทำสิ่งต่อไปนี้:

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

ดังนั้นคุณทำสิ่งนี้:

SELECT * -- for simplicity
FROM Company, Department, Employee
WHERE Company.ID *= Department.CompanyID
  AND Department.ID = Employee.DepartmentID

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

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

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

สาเหตุของข้อนี้คือWHEREข้อใดเป็นตัวกำหนดแถวท้ายแถวในผลลัพธ์สุดท้ายไม่ใช่แต่ละส่วนของแถว

และในกรณีนี้เนื่องจากการเข้าร่วมทางซ้ายคอลัมน์ Department.ID จะเป็น NULL ดังนั้นเมื่อเข้าสู่ INNER JOIN to Employee ไม่มีวิธีใดที่จะบรรลุข้อ จำกัด ดังกล่าวสำหรับแถว Employee และจะไม่เกิดขึ้น ปรากฏ.

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

ดังนั้นไวยากรณ์เก่าจึงไม่ชัดเจน ไม่มีวิธีที่จะระบุสิ่งที่คุณต้องการโดยไม่ต้องจัดการกับคำแนะนำแบบสอบถามและฐานข้อมูลบางอย่างไม่มีวิธีเลย

ป้อนไวยากรณ์ใหม่โดยคุณสามารถเลือกได้

ตัวอย่างเช่นหากคุณต้องการให้ทุก บริษัท ตามคำอธิบายปัญหาที่ระบุไว้นี่คือสิ่งที่คุณจะเขียน:

SELECT *
FROM Company
     LEFT JOIN (
         Department INNER JOIN Employee ON Department.ID = Employee.DepartmentID
     ) ON Company.ID = Department.CompanyID

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

นอกจากนี้สมมติว่าคุณต้องการแผนกที่มีตัวอักษร X ในชื่อของพวกเขาเท่านั้น คุณเสี่ยงต่อการสูญเสีย บริษัท เช่นกันหากไม่มีแผนกใด ๆ ที่มี X ในชื่อ แต่ด้วยไวยากรณ์ใหม่คุณสามารถทำสิ่งนี้ได้:

SELECT *
FROM Company
     LEFT JOIN (
         Department INNER JOIN Employee ON Department.ID = Employee.DepartmentID
     ) ON Company.ID = Department.CompanyID AND Department.Name LIKE '%X%'

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

นี่คือเหตุผลว่าทำไมในหมู่ผู้ขายรายอื่น Microsoft ได้คัดค้านไวยากรณ์การรวมภายนอกด้านนอกเก่า แต่ไม่ใช่ไวยากรณ์การเข้าร่วมด้านในเก่าตั้งแต่ SQL Server 2005 ขึ้นไป วิธีเดียวที่จะพูดคุยกับฐานข้อมูลที่ทำงานบน Microsoft SQL Server 2005 หรือ 2008 โดยใช้ไวยากรณ์การรวมภายนอกแบบเก่าคือการตั้งค่าฐานข้อมูลนั้นในโหมดความเข้ากันได้ 8.0 (aka SQL Server 2000)

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

ดังนั้นคุณมีมัน

ซ้ายและเข้าร่วมเป็นคลื่นแห่งอนาคต


28
"กำลังเลิกใช้ในฐานข้อมูลที่ทันสมัยที่สุด" --- แค่อยากรู้อยากเห็นคนไหน?
zerkms

10
ยกโทษให้ฉันไม่คุ้นเคยกับผู้ดำเนินการ * = มันทำอะไร? ขอบคุณ!
ultrajohn

9
Star = และ = Star อยู่ด้านขวาและซ้ายด้านนอกเข้าร่วมหรือว่าซ้ายและขวา? ถูกเลิกใช้มานานแล้วฉันไม่ได้ใช้ตั้งแต่ SQL Server 6
Tony Hopkinson

3
เครื่องหมายจุลภาคไม่ได้ถูกคัดค้าน OUTER JOINไวยากรณ์ไม่ได้มาตรฐาน*=/ =*/ *=*เลิกใช้แล้ว
philipxy

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

17

ไวยากรณ์เข้าร่วมรักษาสภาพใกล้กับตารางที่พวกเขานำไปใช้ สิ่งนี้มีประโยชน์อย่างยิ่งเมื่อคุณเข้าร่วมตารางจำนวนมาก

อย่างไรก็ตามคุณสามารถทำการรวมภายนอกกับไวยากรณ์แรกด้วย:

WHERE a.x = b.x(+)

หรือ

WHERE a.x *= b.x

หรือ

WHERE a.x = b.x or a.x not in (select x from b)

2
ไวยากรณ์ * = เลิกใช้แล้วใน MS SQLServer และด้วยเหตุผลที่ดี: ไม่เพียง แต่จะทำให้อ่านได้ยากขึ้นเท่านั้น แต่ไม่ได้ทำในสิ่งที่คนคิดว่าทำ ฉันไม่คุ้นเคยกับไวยากรณ์ (+); การใช้ SQL แบบใด
Euro Micelli

2
อย่างน้อย Oracle จะใช้ไวยากรณ์อื่น ๆ
Lasse V. Karlsen

4
อย่าใช้ไวยากรณ์ของ SQL Server * = มันจะไม่ให้ผลลัพธ์ที่สอดคล้องกันเนื่องจากบางครั้งมันจะตีความว่าเป็นการเข้าร่วมไขว้ไม่ใช่การเข้าร่วมซ้าย สิ่งนี้เป็นจริงแม้กระทั่งย้อนกลับไปถึง SQL Server 2000 หากคุณมีรหัสใด ๆ ที่ใช้สิ่งนี้คุณต้องแก้ไข
HLGEM

12

วิธีแรกคือมาตรฐานที่เก่ากว่า วิธีที่สองได้รับการแนะนำใน SQL-92, http://en.wikipedia.org/wiki/SQL มาตรฐานที่สมบูรณ์สามารถดูได้ที่http://www.contrib.andrew.cmu.edu/~shadow/sql/sql1992.txt

ใช้เวลาหลายปีก่อนที่ บริษัท ฐานข้อมูลจะใช้มาตรฐาน SQL-92

ดังนั้นเหตุผลที่ต้องการวิธีที่สองจึงเป็นมาตรฐาน SQL ตามมาตรฐาน ANSI และ ISO


,ยังคงเป็นมาตรฐาน onจำเป็นต้องได้รับการแนะนำสำหรับการเลือกย่อยouter joinครั้งเดียวเท่านั้น
philipxy

12

โดยทั่วไปเมื่อคำสั่ง FROM ของคุณแสดงรายการตารางดังนี้:

SELECT * FROM
  tableA, tableB, tableC

ผลที่ได้คือผลคูณของทุกแถวในตาราง A, B, C จากนั้นคุณใช้ข้อ จำกัดWHERE tableA.id = tableB.a_idที่จะทิ้งจำนวนมากของแถวแล้วต่อไป ... AND tableB.id = tableC.b_idและคุณควรได้รับเฉพาะแถวที่คุณสนใจจริงๆ ใน.

DBMSs รู้วิธีเพิ่มประสิทธิภาพ SQL นี้เพื่อให้ความแตกต่างของประสิทธิภาพในการเขียนโดยใช้ JOIN นั้นเล็กน้อย (ถ้ามี) การใช้รูปแบบ JOIN ทำให้คำสั่ง SQL อ่านได้ง่ายขึ้น (IMHO ไม่ใช้การรวมจะเปลี่ยนคำสั่งให้เป็นระเบียบ) เมื่อใช้ผลิตภัณฑ์ข้ามคุณจะต้องระบุเกณฑ์การเข้าร่วมในส่วนคำสั่ง WHERE และนั่นเป็นปัญหาของรูปแบบ คุณกำลังเบียดข้อของคุณกับสิ่งที่ชอบ

    tableA.id = tableB.a_id 
AND tableB.id = tableC.b_id 

ซึ่งใช้เพื่อ จำกัด ผลิตภัณฑ์ไขว้เท่านั้น ข้อใดควรมีข้อ จำกัด เฉพาะกับ resultset หากคุณผสมผสานเกณฑ์การเข้าร่วมตารางกับข้อ จำกัด ของชุดผลลัพธ์คุณ (และคนอื่น ๆ ) จะค้นหาข้อความค้นหาของคุณได้ยากขึ้น คุณควรใช้การเข้าร่วมอย่างแน่นอนและเก็บ FROM clause เป็น clause clause และ WHERE clause a WHERE clause


10

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

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

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


6

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

ในคำสั่ง SELECT ที่ซับซ้อนจริงๆมันกลายเป็นเรื่องง่ายสำหรับผู้อ่านที่จะเข้าใจสิ่งที่เกิดขึ้น


5

SELECT * FROM table1, table2, ...ไวยากรณ์ก็โอเคสำหรับคู่ของตาราง แต่มันจะกลายเป็นทวีคูณ ( ไม่จำเป็นต้องเป็นคำสั่งที่ถูกต้องทางคณิตศาสตร์ ) หนักและยากที่จะอ่านเป็นจำนวนของตารางที่เพิ่มขึ้น

ไวยากรณ์ JOIN นั้นยากต่อการเขียน (ตอนต้น) แต่มันทำให้ชัดเจนว่าเกณฑ์ใดมีผลต่อตารางใด สิ่งนี้ทำให้การทำผิดพลาดยากขึ้นมาก

นอกจากนี้หากการรวมทั้งหมดเป็น INNER ดังนั้นทั้งสองเวอร์ชันจะเทียบเท่ากัน อย่างไรก็ตามช่วงเวลาที่คุณมีการเข้าร่วมด้านนอกที่ใดก็ได้ในแถลงการณ์สิ่งต่าง ๆ มีความซับซ้อนและรับรองได้ว่าสิ่งที่คุณเขียนจะไม่สอบถามสิ่งที่คุณคิด


2

เมื่อคุณต้องการเข้าร่วม outer ไม่จำเป็นต้องใช้ไวยากรณ์ที่สองเสมอไป:

ออราเคิล:

SELECT a.foo, b.foo
  FROM a, b
 WHERE a.x = b.x(+)

MSSQLServer (แม้ว่าจะถูกเลิกใช้ในรุ่น 2000) / Sybase:

SELECT a.foo, b.foo
  FROM a, b
 WHERE a.x *= b.x

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


เซิร์ฟเวอร์ SQL เลิกใช้งานแล้วและยังอยู่ใน SQL Server 2000 แต่จะไม่ให้ผลลัพธ์ที่ถูกต้อง (บางครั้งมันก็เป็นการเข้าร่วมไขว้แทนที่จะเข้าร่วมซ้าย) และไม่ควรใช้ใน SQL Server
HLGEM

@HLGEM: ขอบคุณสำหรับข้อมูล ฉันจะอัปเดตโพสต์ของฉันเพื่อสะท้อนสิ่งที่คุณพูด
Pablo Santa Cruz

0

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


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

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

0

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


0

แบบสอบถามที่หนึ่งและที่สองอาจให้ผลลัพธ์ที่แตกต่างกันเนื่องจาก LEFT JOIN รวมระเบียนทั้งหมดจากตารางแรกแม้ว่าจะไม่มีระเบียนที่เกี่ยวข้องในตารางด้านขวา

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