นี่เป็นคำถามที่ดีมากฉันจึงตัดสินใจเขียนบทความโดยละเอียดเกี่ยวกับหัวข้อนี้ในบล็อกของฉัน
โมเดลตารางฐานข้อมูล
สมมติว่าเรามีตารางสองตารางต่อไปนี้ในฐานข้อมูลของเราซึ่งสร้างความสัมพันธ์แบบหนึ่งต่อหลายกลุ่ม
student
ตารางผู้ปกครองและstudent_grade
เป็นตารางเด็กเพราะมันมี student_id คอลัมน์สำคัญต่างประเทศอ้างอิง ID คอลัมน์คีย์หลักในตารางนักเรียน
student table
มีดังต่อไปนี้สองระเบียน:
| id | first_name | last_name | admission_score |
|----|------------|-----------|-----------------|
| 1 | Alice | Smith | 8.95 |
| 2 | Bob | Johnson | 8.75 |
และstudent_grade
ตารางเก็บคะแนนที่นักเรียนได้รับ:
| id | class_name | grade | student_id |
|----|------------|-------|------------|
| 1 | Math | 10 | 1 |
| 2 | Math | 9.5 | 1 |
| 3 | Math | 9.75 | 1 |
| 4 | Science | 9.5 | 1 |
| 5 | Science | 9 | 1 |
| 6 | Science | 9.25 | 1 |
| 7 | Math | 8.5 | 2 |
| 8 | Math | 9.5 | 2 |
| 9 | Math | 9 | 2 |
| 10 | Science | 10 | 2 |
| 11 | Science | 9.4 | 2 |
SQL ที่มีอยู่
สมมติว่าเราต้องการให้นักเรียนทุกคนที่ได้รับเกรด 10 ในวิชาคณิตศาสตร์
หากเราสนใจเฉพาะตัวระบุนักเรียนเราสามารถเรียกใช้คิวรีแบบนี้ได้:
SELECT
student_grade.student_id
FROM
student_grade
WHERE
student_grade.grade = 10 AND
student_grade.class_name = 'Math'
ORDER BY
student_grade.student_id
แต่แอปพลิเคชันมีความสนใจในการแสดงชื่อเต็มของstudent
ไม่ใช่เพียงตัวระบุดังนั้นเราต้องการข้อมูลจากstudent
ตารางเช่นกัน
ในการกรองstudent
เรคคอร์ดที่มีเกรด 10 ในวิชาคณิตศาสตร์เราสามารถใช้ตัวดำเนินการ EXISTS SQL ดังนี้:
SELECT
id, first_name, last_name
FROM
student
WHERE EXISTS (
SELECT 1
FROM
student_grade
WHERE
student_grade.student_id = student.id AND
student_grade.grade = 10 AND
student_grade.class_name = 'Math'
)
ORDER BY id
เมื่อเรียกใช้คิวรีด้านบนเราจะเห็นได้ว่าเฉพาะแถวอลิซเท่านั้นที่เลือก:
| id | first_name | last_name |
|----|------------|-----------|
| 1 | Alice | Smith |
แบบสอบถามด้านนอกเลือก student
คอลัมน์แถวที่เราสนใจกลับไปที่ลูกค้า อย่างไรก็ตามส่วนคำสั่ง WHERE กำลังใช้ตัวดำเนินการ EXISTS กับแบบสอบถามย่อยภายในที่เกี่ยวข้อง
ผู้ประกอบการ EXISTS จะส่งกลับค่าจริงถ้าแบบสอบถามย่อยส่งกลับอย่างน้อยหนึ่งระเบียนและเท็จถ้าไม่มีแถวถูกเลือก เอ็นจินฐานข้อมูลไม่จำเป็นต้องรันเคียวรีย่อยทั้งหมด หากมีการจับคู่ระเบียนเดียวตัวดำเนินการ EXISTS จะส่งกลับค่าจริงและเลือกแถวแบบสอบถามอื่น ๆ ที่เกี่ยวข้อง
แบบสอบถามย่อยภายในมีความสัมพันธ์เนื่องจากคอลัมน์ student_id ของstudent_grade
ตารางตรงกับคอลัมน์ id ของตารางนักเรียนด้านนอก
SQL ไม่ใช่ EXISTS
ลองพิจารณาว่าเราต้องการที่จะเลือกนักเรียนทุกคนที่ไม่มีเกรดต่ำกว่า 9 สำหรับเรื่องนี้เราสามารถใช้ NOT EXISTS ซึ่งขัดแย้งกับตรรกะของผู้ดำเนินการ EXISTS
ดังนั้นตัวดำเนินการ NOT EXISTS จะส่งกลับค่าจริงหากเคียวรีย่อยอ้างอิงไม่มีการบันทึก อย่างไรก็ตามหากมีการจับคู่บันทึกเดียวโดยแบบสอบถามย่อยภายในตัวดำเนินการ NOT EXISTS จะส่งกลับค่าเท็จและการดำเนินการแบบสอบถามย่อยจะหยุดลง
เพื่อให้ตรงกับบันทึกของนักเรียนทั้งหมดที่ไม่มี student_grade ที่เกี่ยวข้องกับค่าต่ำกว่า 9 เราสามารถเรียกใช้แบบสอบถาม SQL ต่อไปนี้:
SELECT
id, first_name, last_name
FROM
student
WHERE NOT EXISTS (
SELECT 1
FROM
student_grade
WHERE
student_grade.student_id = student.id AND
student_grade.grade < 9
)
ORDER BY id
เมื่อเรียกใช้แบบสอบถามข้างต้นเราจะเห็นว่ามีเพียงระเบียนของอลิซเท่านั้นที่ตรงกัน:
| id | first_name | last_name |
|----|------------|-----------|
| 1 | Alice | Smith |
ดังนั้นข้อดีของการใช้ตัวดำเนินการ SQL EXISTS และ NOT EXISTS ก็คือการดำเนินการแบบสอบถามย่อยภายในสามารถหยุดได้ตราบเท่าที่พบระเบียนที่ตรงกัน