曾為取得所需資訊而反覆執行多次查詢?子查詢 (Subquery/Subselect) 讓您在單一陳述式中完成資料關聯、過濾與彙總比較。本文透過實務範例詳細說明四大核心用法。
子查詢本質是「查詢中的查詢」——內層查詢產生的結果集成為外層查詢的資源。這將資料比對邏輯下推至資料庫引擎,避免應用層搬運資料比對。
情境:找出 Metallica 所有歌名中包含 "justice" 的歌曲。
SELECT song_name FROM Album
WHERE band_name = 'Metallica'
AND song_name IN (
SELECT song_name FROM Lyric
WHERE song_lyric LIKE '%justice%'
);
解析:
IN 將內層結果集作為外層過濾條件關鍵點:子查詢為獨立單元,無需使用完整欄位名 (如 Album.song_name);若需同時輸出歌詞,請改用 JOIN。
情境:找出《And Justice for All》專輯中歌詞「不含」 justice 的歌曲。
SELECT song_name FROM Album
WHERE album_name = 'And Justice for All'
AND band_name = 'Metallica'
AND song_name NOT IN (
SELECT song_name FROM Lyric
WHERE song_lyric LIKE '%justice%'
);
邏輯為:專輯歌曲集合 減去 含 justice 歌曲集合 = 目標結果。單一陳述式取代應用層陣列比對。
情境:同時從 Album 表 (原唱) 與 Cover 表 (翻唱團體 Damage, Inc.) 找出同名歌曲。
SELECT Album.song_name FROM Album
WHERE Album.band_name = 'Metallica'
AND EXISTS (
SELECT Cover.song_name FROM Cover
WHERE Cover.band_name = 'Damage, Inc.'
AND Cover.song_name = Album.song_name
);
差異於 IN:此處直接比對兩表欄位 (Cover.song_name = Album.song_name),故需使用完整欄位名稱。EXISTS 僅檢查「是否存在至少一筆」,不回傳實際資料,效能通常優於 IN。部分資料庫支援 NOT EXISTS。
情境:驗證 Album 表中每張專輯的歌曲數是否與 AlbumInfo 表記錄的 album_tracks 一致,並找出缺漏的專輯。
SELECT album_name FROM AlbumInfo a
WHERE album_tracks <> (
SELECT COUNT(*) FROM Album
WHERE Album.album_name = a.album_name
);
子查詢動態計算實際歌曲數,外層與預期值比對。此模式常用於資料品質檢核、對帳報表。
| 場景 | 建議 |
|---|---|
| 子查詢結果集極大 | 改用 JOIN 或 EXISTS,避免物質化大量中間結果 |
| 相關子查詢逐列執行 | 確認有適當索引支援連接欄位 |
| NOT IN 含 NULL 值 | 小心!NOT IN (..., NULL) 永遠回傳 FALSE,改用 NOT EXISTS |
| 重複使用相同子查詢 | 考慮 CTE (WITH 子句) 或暫存表 |
子查詢是 SQL 表達複雜業務邏輯的利器:IN 適合集合包含檢查,NOT IN 用於排除 (注意 NULL 陷阱),EXISTS 專為存在性驗證優化,彙總函數則能在單一查詢中完成跨層級數值比對。善用它們,能大幅簡化應用程式碼並提升效能。