เหตุใดแบบสอบถามนี้จึงใช้งานได้


37

ฉันมีสองตาราง table_a (id, ชื่อ) และ table_b (id) สมมุติว่าบน Oracle 12c

เหตุใดแบบสอบถามนี้จึงไม่ส่งคืนข้อยกเว้น

select * from table_a where name in (select name from table_b);

จากสิ่งที่ฉันเข้าใจ Oracle เห็นว่าเป็น

select * from table_a where name = name;

แต่สิ่งที่ฉันไม่ได้คือทำไม

คำตอบ:


61

แบบสอบถามถูกต้องตามหลักไวยากรณ์ SQL แม้ว่าtable_bจะไม่มีnameคอลัมน์ เหตุผลก็คือการแก้ไขขอบเขต

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

ในที่สุดแบบสอบถามจะถูกดำเนินการเป็น:

select a.* 
from table_a  a
where a.name in (select a.name 
                 from table_b  b
                );

สำหรับผลลัพธ์ที่แบบสอบถามจะให้สำหรับทุกแถวของtable_aแบบสอบถามย่อย(select name from table_b)- หรือ(select a.name from table_b b)- คือตารางที่มีคอลัมน์เดียวที่มีa.nameค่าเดียวกันและมีจำนวนแถวเท่าtable_bกัน ดังนั้นหากtable_bมี 1 แถวขึ้นไปเคียวรีจะทำงานเป็น:

select a.* 
from table_a  a
where a.name in (a.name, a.name, ..., a.name) ;

หรือ:

select a.* 
from table_a  a
where a.name = a.name ;

หรือ:

select a.* 
from table_a  a
where a.name is not null ;

ถ้าtable_bว่างเปล่าแบบสอบถามจะไม่ส่งคืนแถวใด (ขอบคุณถึง @ughai สำหรับการชี้ความเป็นไปได้นั้น)


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

select a.* from table_a where a.name in (select b.name from table_b); 

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

อ่านเพิ่มเติมในOracle docs: การแก้ปัญหาชื่อในงบ SQL แบบคงที่ตัวอย่างที่คล้ายกัน B-6 ในการดักจับด้านในและคำแนะนำในการหลีกเลี่ยง Inner Capture ในย่อหน้าSELECT และ DML Statement :

รับรองการอ้างอิงแต่ละคอลัมน์ในคำสั่งด้วยนามแฝงตารางที่เหมาะสม


คุณแยกงานภายในของเครื่องยนต์ SQL ออกมาอย่างแม่นยำได้อย่างไร?
RinkyPinku

8

เพราะ

Oracle ดำเนินการแบบสอบถามย่อยที่มีความสัมพันธ์กันเมื่อแบบสอบถามย่อยแบบซ้อนอ้างอิงคอลัมน์จากตารางที่อ้างถึงคำสั่งหลักหนึ่งระดับเหนือแบบสอบถามย่อย http://docs.oracle.com/cd/E11882_01/server.112/e41084/queries007.htm#SQLRF52357

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


4

ไม่มีคือnameในสนามเพื่อให้ออราเคิลจะใช้เวลาหนึ่งจากtable_b table_aฉันพยายามแต่ตอนนี้ให้ฉันเพียงคนเดียวที่มีEXPLAIN PLAN TABLE ACCESS FULLฉันคิดว่าสิ่งนี้จะสร้างผลิตภัณฑ์คาร์ทีเซียนบางประเภทระหว่างทั้งสองตารางที่ส่งผลให้รายการของชื่อทั้งหมดที่table_aถูกส่งกลับโดยแบบสอบถามย่อย


5
"ไม่มีฟิลด์ชื่อใน table_b ดังนั้น Oracle จึงเลือกใช้จาก table_a" แก้ไข. "ฉันคิดว่าสิ่งนี้จะสร้างผลิตภัณฑ์คาร์ทีเซียนบางประเภท" ไม่ถูกต้อง. from table_a where ...แบบสอบถามมี มันจะส่งคืนแถวทั้งหมดtable_aยกเว้นแถวที่nameเป็นโมฆะ
ypercubeᵀᴹ

1
TABLE ACCESS FULLเป็นเพียงวิธีของ Oracle ในการบอกคุณว่ากำลังทำการสแกนตามลำดับ
Joishi Bodio

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