多表查詢是業務開發高頻痛點。本文以「學生-班級-關系表」經典模型,通過可視化圖解,演示如何用 內連接 與 子查詢 高效解決「查詢 1 班學生名單」問題,並深度解析主鍵/外鍵約束的完整性語義。

| 表名 | 核心列 | 業務含義 |
|---|---|---|
i_student | id (PK), name | 學生實體,3 條記錄:a、b、c |
i_class | id (PK) | 班級實體,2 條記錄:1、2 |
i_class_student | class_id (FK), student_id (FK) | 多對多關系表:1班→a,b;2班→c |

id NOT NULL + UNIQUE,保證每行唯一標識。class_id → i_class.id、student_id → i_student.id,防止孤兒記錄,級聯動作由應用/觸發器控制。
SELECT student_id FROM i_class_student WHERE class_id = 1;

返回:1, 2
SELECT id, name FROM i_student;

SELECT t1.name
FROM i_student AS t1
INNER JOIN (
SELECT student_id FROM i_class_student WHERE class_id = 1
) AS t2 ON t1.id = t2.student_id;

結果:a, b ✅
| 寫法 | SQL | 優缺點 |
|---|---|---|
| 內連接 + 子查詢 | 上文方案 | 語義清晰,優化器易轉換為 Semi-Join |
| 直接多表 JOIN | SELECT s.name FROM i_student s JOIN i_class_student cs ON s.id=cs.student_id WHERE cs.class_id=1; | 最簡潔,推薦生產環境 |
| EXISTS 子查詢 | SELECT name FROM i_student s WHERE EXISTS (SELECT 1 FROM i_class_student cs WHERE cs.student_id=s.id AND cs.class_id=1); | 大表驅動小表時性能優,語義「存在即可」 |
| IN 子查詢 | SELECT name FROM i_student WHERE id IN (SELECT student_id FROM i_class_student WHERE class_id=1); | MySQL 5.6+ 優化為 Semi-Join,性能同 EXISTS |
i_class_student(class_id, student_id) 複合索引覆蓋查詢。NOT IN 子查詢含 NULL 會返回空集,改用 NOT EXISTS。核心心法:多表查詢本質是「集合運算」。先在關系表按條件縮小集合,再回主表取屬性,配合索引覆蓋,就是最高效的執行路徑。