IN 操作符用於判斷某值是否在指定集合中,集合來源可為子查詢結果集或顯式值列表。
-- 學生表
s(sno, sname, sex, age, dept)
-- 課程表
c(cno, cname, tname)
-- 選課表
sc(sno, cno, grade)
需求:將「物理」課程不及格(<60分)的學生成績設為 0。
UPDATE sc
SET grade = 0
WHERE sc.cno IN (SELECT cno FROM c WHERE c.cname = '物理')
AND sc.grade < 60;
解析:
SELECT cno FROM c WHERE c.cname = '物理' 返回課號集合(如 'C01', 'C02')。WHERE sc.cno IN (...) 匹配這些課號的選課記錄。sc.grade < 60 過濾不及格記錄並更新。優勢:集合隨資料動態變化,無需硬編碼值。
需求:查詢姓名為 'aaa'、'bbb'、'ccc' 的學生資料。
SELECT * FROM s WHERE sname IN ('aaa', 'bbb', 'ccc');
值列表不限於字串,數值、日期皆可:
SELECT * FROM sc WHERE grade IN (60, 70, 80, 90, 100);
SELECT * FROM s WHERE age IN (18, 19, 20);
| 場景 | 建議 |
|---|---|
| 值列表較小(< 100)且固定 | 直接用 IN (val1, val2...),MySQL 內部優化為二分搜尋。 |
| 值列表大或頻變 | 用臨時表或子查詢,避免 SQL 過長解析開銷。 |
| 子查詢返回大量行 | 考慮改用 EXISTS 或 JOIN,避免 IN 子查詢在舊版本(<5.6)效能差。 |
| 欄位有索引 | IN 可走索引範圍掃描(range),效能佳。 |
-- 錯誤:混合型別(數字與字串)
SELECT * FROM t WHERE id IN (1, 2, 'a');
-- 正確:統一型別
SELECT * FROM t WHERE id IN ('1', '2', 'a');
MySQL 會嘗試將所有值轉換為相同型別比較,混合型別導致隱式轉換結果難以預期。
NOT IN 若子查詢結果含 NULL,整個判斷永遠為 NULL(非 TRUE),導致無資料返回。解決:
-- 避免 NULL
SELECT * FROM s WHERE sno NOT IN (SELECT sno FROM sc WHERE grade > 80 AND sno IS NOT NULL);
-- 或改用 NOT EXISTS(推薦)
SELECT * FROM s WHERE NOT EXISTS (SELECT 1 FROM sc WHERE sc.sno = s.sno AND grade > 80);