SELECT ... FROM ... WHEREcolumnIN (SELECTcolumnFROM ... WHERE ...);
IN 運算子(最基本、最常考)
語意
v IN V
比較一個值 v 是否屬於一個集合(或 multiset)V
若 v 是集合中任一元素 → 回傳 TRUE
特性
子查詢回傳「一個欄位(單一 column)」
可搭配 OR、AND 使用
範例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
SELECTDISTINCT Pnumber FROM PROJECT WHERE Pnumber IN ( SELECT Pnumber FROM PROJECT, DEPARTMENT, EMPLOYEE WHERE Dnum = Dnumber AND Mgr_ssn = Ssn AND Lname ='Smith' ) OR Pnumber IN ( SELECT Pno FROM WORKS_ON, EMPLOYEE WHERE Essn = Ssn AND Lname ='Smith' );
找出 Smith 當經理的專案
或 Smith 有實際參與的專案
使用 Tuple(多欄位)做比較
語法
可以把多個欄位包成一個 tuple
放在比較條件中
1
(column1, column2) IN (subquery)
Example
1 2 3 4 5 6 7
SELECTDISTINCT Essn FROM WORKS_ON WHERE (Pno, Hours) IN ( SELECT Pno, Hours FROM WORKS_ON WHERE Essn ='123456789' );
找出「工作內容(專案 + 時數)
和員工 123456789 完全一樣」的其他員工
IN 右邊可以直接寫常數集合
不一定要子查詢
1 2 3
SELECTDISTINCT mId FROM Browse WHERE pNo IN ('b30999', 'b10234', 'd11222');
IN (集合) ≈ = 某個集合中的一個
子查詢可以參考外層查詢
概念
子查詢可以使用「外層查詢的變數」
這會導致:子查詢為外層的每一筆資料重新執行
EXAMPLE
1 2 3 4 5 6 7 8 9 10
SELECTDISTINCT M.mId, B.pNo FROM Browse AS B, MemberAS M WHERE name ='王小明' AND B.mId = M.mId AND pNo IN ( SELECT pNo FROM Record AS R, Transaction AS T WHERE transMid = M.mId AND T.tNo = R.tNo );
先找出「王小明」的所有 mId
對 每一筆瀏覽紀錄
檢查該商品是否出現在「他實際購買的商品清單」中
集合比較運算子
IN
ANY / SOME
ALL
ANY(或 SOME)運算子
語法
1
value=ANY (subquery)
或
1
value=SOME (subquery)
說明
若 value 等於子查詢結果中的 任一個值,則回傳 TRUE
等價於 IN
1 2 3
valueIN (subquery) ≡ value=ANY (subquery)
ALL 運算子(非常重要)
語法
1
value>ALL (subquery)
說明
value 必須 大於子查詢回傳的所有值
常用於「最大 / 最小」或「比全部都大(小)」的語意
範例 1:薪資比較
1 2 3 4 5 6 7
SELECT Lname, Fname FROM EMPLOYEE WHERE Salary >ALL ( SELECT Salary FROM EMPLOYEE WHERE Dno =5 );
👉 意義:找出薪水 高於部門 5 所有員工 的人
範例 2:商品價格
1 2 3 4 5 6 7
SELECT pNo, pName FROM Product WHERE unitPrice >ALL ( SELECT unitPrice FROM Product WHERE category ='Book' );
👉 意義:找出價格 比所有書籍都貴 的商品
比較運算子可搭配 ANY / ALL
可搭配的比較運算子包含:
>、>=
<、<=
=、<>
1 2
value>ANY (subquery) value<=ALL (subquery)
使用 Alias 避免模糊錯誤
為什麼需要 Alias?
不同資料表可能有 相同欄位名稱
若不使用 alias,SQL 會產生 語意不明或錯誤
範例(Query 16)
1 2 3 4 5 6 7 8
SELECT E.Fname, E.Lname FROM EMPLOYEE AS E WHERE E.Ssn IN ( SELECT Essn FROM DEPENDENT AS D WHERE E.Fname = D.Dependent_name AND E.Sex = D.Sex );
👉 意義:
找出「有家屬」
且家屬 名字與性別都與員工相同 的員工
Correlated Nested Queries(相關巢狀查詢)
定義
子查詢 會參考外層查詢的欄位
子查詢會對外層的 每一筆資料重新執行一次
特性
表達能力強
但效能可能較差(若 DBMS 未最佳化)
改寫為單一查詢(JOIN)
1 2 3 4 5
SELECT E.Fname, E.Lname FROM EMPLOYEE AS E, DEPENDENT AS D WHERE E.Ssn = D.Essn AND E.Sex = D.Sex AND E.Fname = D.Dependent_name;
👉 與前一頁巢狀查詢 語意完全相同
👉 DBMS 內部通常會做這類最佳化
EXISTS 函數
語法
1
EXISTS (subquery)
說明
檢查子查詢 是否回傳至少一筆資料
回傳值為 TRUE / FALSE
常與 correlated nested query 搭配使用
範例:找出有購買指定商品的會員
1 2 3 4 5 6 7 8 9 10
SELECT mId, name FROMMember WHEREEXISTS ( SELECT* FROM Product, Record, Transaction WHERE pName ='系統分析理論與實務' AND Product.pNo = Record.pNo AND Record.tNo = Transaction.tNo AND mId = transMid );
👉 子查詢中的 mId 來自外層 Member(相關子查詢)
EXISTS + EXISTS(複合條件)
範例:找出「至少有一位家屬的主管」
1 2 3 4 5 6 7 8 9 10 11 12
SELECT Fname, Lname FROM Employee WHEREEXISTS ( SELECT* FROM DEPENDENT WHERE Ssn = Essn ) ANDEXISTS ( SELECT* FROM Department WHERE Ssn = Mgr_ssn );
👉 同時滿足:
是主管
且至少有一位家屬
NOT EXISTS
語法
1
NOTEXISTS (subquery)
說明
子查詢 完全沒有回傳資料 時為 TRUE
範例:找出不是由購物車產生的交易
1 2 3 4 5 6 7
SELECT tNo, transMid FROM Transaction AS T WHERENOTEXISTS ( SELECT* FROM Cart WHERE tNo = T.tNo );
👉 意義:找出 非由購物車而來的交易紀錄
重點整理
IN ≡ = ANY
> ALL:比所有值都大
ANY / ALL 可搭配各種比較運算子
EXISTS 回傳 TRUE / FALSE(不看內容,只看有沒有資料)
Correlated nested query:對外層每筆資料執行一次
EXISTS / NOT EXISTS 非常適合表示「存在 / 不存在」
JOINed Tables
核心觀念:JOIN 是在 FROM 先做、WHERE 再過濾
SQL 的邏輯執行順序(觀念上):
FROM(含 JOIN / ON)先形成一張「中間結果表」
WHERE 再把不符合條件的列刪掉
SELECT 決定輸出欄位
DISTINCT / GROUP BY / HAVING / ORDER BY …
✅ 這會直接影響 OUTER JOIN:
ON:決定「怎麼配對」以及「配不到要不要補 NULL」
WHERE:會把結果再刪掉,可能把補 NULL 的列刪光 → OUTER JOIN 失效
Joined table(在 FROM 直接指定 JOIN 出來的表)
定義
Joined table:讓你在 FROM 子句直接寫出 join 的結果表。
範例
1 2 3
SELECT Fname, Lname, Address FROM (EMPLOYEE JOIN DEPARTMENT ON Dno = Dnumber) WHERE Dname ='Research';
SELECT Pnumber, Dnum, Lname, Address, Bdate FROM ( (PROJECT JOIN DEPARTMENT ON Dnum = Dnumber) JOIN EMPLOYEE ON Mgr_ssn = Ssn ) WHERE Plocation ='Stafford';
📌 解讀流程:
PROJECT 先 JOIN DEPARTMENT(專案屬於哪個部門)
再把上一步結果 JOIN EMPLOYEE(該部門的經理是誰)
最後 WHERE 篩選專案地點 Stafford
OUTER JOIN 的「超級陷阱」:條件放 WHERE 會讓 OUTER JOIN 失效
題目需求
「列出每一位會員的會員編號、姓名,以及 2005 年所瀏覽的商品編號(如果有的話)」
也就是:
每個會員都要列出(所以要 LEFT OUTER JOIN)
但瀏覽紀錄只要 2005 年的
錯誤寫法(WRONG)
1 2 3 4
SELECT M.mId, name, pNo FROMMemberAS M LEFTOUTERJOIN Browse AS B ON M.mId = B.mId WHERE to_char(browseTime, 'yyyy') ='2005';
❌ 為什麼錯?
LEFT OUTER JOIN 先把「沒有瀏覽紀錄」的會員補了一列(B.* = NULL)
但 WHERE to_char(browseTime, 'yyyy') = '2005'
對於 browseTime = NULL 的列,條件結果會是 UNKNOWN → 被 WHERE 過濾掉
結果:沒有 2005 瀏覽紀錄的會員整個不見
➡️ LEFT OUTER JOIN 的「保留所有會員」效果被破壞
正確寫法(把條件放 ON)
1 2 3 4 5 6
SELECT M.mId, name, pNo FROMMemberAS M LEFTOUTERJOIN Browse AS B ON ( M.mId = B.mId AND to_char(browseTime, 'yyyy') ='2005' );
✅ 為什麼這樣對?
ON 的條件是「配對規則」:只把 2005 的瀏覽紀錄拿來配
沒有 2005 瀏覽紀錄的會員:仍會保留(右表補 NULL)
完全符合題意:「如果有的話」
什麼時候放 ON?什麼時候放 WHERE?
✅ 放 ON(配對條件):
你要保留 outer join 的「配不到也要留」效果
右表的篩選條件(例如年份、狀態)又不能把左表刪掉
✅ 放 WHERE(結果過濾):
你就是要把不符合條件的列刪掉(不管 outer join)
或你在 INNER JOIN 情境下過濾
JOIN整理
JOIN 預設是 INNER JOIN
NATURAL JOIN:用同名欄位自動等值連接(不寫 ON)
LEFT OUTER JOIN:左表全保留;右表配不到補 NULL
RIGHT OUTER JOIN:右表全保留;左表配不到補 NULL
多表 JOIN:可以巢狀寫 (... JOIN ...) JOIN ...
OUTER JOIN 篩選條件:
放 WHERE 可能讓 OUTER JOIN 變成 INNER JOIN
放 ON 才能保留「配不到也要留」
Aggregate Functions & GROUP BY / HAVING
Aggregate Functions(彙總函數)
用來把多筆 tuple 的資訊彙總成「單筆結果」或「每組一筆結果」。
常見內建函數:
COUNT, SUM, MAX, MIN, AVG
NULL 與 aggregate
對欄位做 SUM/MAX/MIN/AVG 時:NULL 會被忽略(discarded)
COUNT(*):算列數(包含該列某些欄位為 NULL 也會算)
COUNT(col):只算 col不是 NULL 的列
Aggregation 結果改欄位名、取別名(AS)
1 2
SELECTSUM(Salary), MAX(Salary), MIN(Salary), AVG(Salary) FROM EMPLOYEE;
1 2 3 4 5 6
SELECT SUM(Salary) AS Total_Sal, MAX(Salary) AS Highest_Sal, MIN(Salary) AS Lowest_Sal, AVG(Salary) AS Average_Sal FROM EMPLOYEE;
Aggregation + WHERE(先篩選,再彙總)
範例
1 2 3
SELECTSUM(Salary), MAX(Salary), MIN(Salary), AVG(Salary) FROM (EMPLOYEE JOIN DEPARTMENT ON Dno = Dnumber) WHERE Dname ='Research';
COUNT 範例
1 2 3 4 5 6 7 8
-- 全公司員工數 SELECTCOUNT(*) FROM EMPLOYEE;
-- Research 部門員工數 SELECTCOUNT(*) FROM EMPLOYEE, DEPARTMENT WHERE Dno = Dnumber AND Dname ='Research';
SOME / ALL 也可當「布林集合函數」
把一串布林值(TRUE/FALSE/UNKNOWN)當集合看:
SOME:只要至少一個 TRUE → TRUE(像 OR)
ALL:必須全部 TRUE → TRUE(像 AND)
這裡是「函數」語意,不是前面比較運算子的 = ANY, > ALL 那種用法。
GROUP BY(分組)
把資料依某些欄位分成多組,然後 每組各自做 aggregate。
規則(必背)
SELECT 中若同時出現「一般欄位」與「aggregate」:
一般欄位必須出現在 GROUP BY
範例(Q24:每個部門的員工數與平均薪資)
1 2 3
SELECT Dno, COUNT(*), AVG(Salary) FROM EMPLOYEE GROUPBY Dno;
NULL 分組
分組欄位若可能為 NULL → NULL 會形成獨立的一組
GROUP BY 套在 JOIN 結果上
範例(Q25:每個專案有多少員工參與)
1 2 3 4
SELECT Pnumber, Pname, COUNT(*) FROM PROJECT, WORKS_ON WHERE Pnumber = Pno GROUPBY Pnumber, Pname;
HAVING(篩選「整組」)
WHERE:逐列(tuple by tuple)篩選
HAVING:逐組(group)篩選(常搭配 aggregate 條件)
範例(Q26:參與人數 > 2 的專案)
1 2 3 4 5
SELECT Pnumber, Pname, COUNT(*) FROM PROJECT, WORKS_ON WHERE Pnumber = Pno GROUPBY Pnumber, Pname HAVINGCOUNT(*) >2;
WHERE + HAVING 的常見錯誤與正確寫法
需求:
各部門中「薪資 > 40000 的員工數」,但只統計「部門總員工數 > 5」的部門。
錯誤(會把部門總人數也限制成薪資 > 40000 的人數)
1 2 3 4 5
SELECT Dno, COUNT(*) FROM EMPLOYEE WHERE Salary >40000 GROUPBY Dno HAVINGCOUNT(*) >5;
正確(先選出「總員工數 > 5」的部門,再統計該部門的高薪員工數)
1 2 3 4 5 6 7 8 9 10
SELECT Dno, COUNT(*) FROM EMPLOYEE WHERE Salary >40000 AND Dno IN ( SELECT Dno FROM EMPLOYEE GROUPBY Dno HAVINGCOUNT(*) >5 ) GROUPBY Dno;
快速對照
WHERE:篩「列」
GROUP BY:分「組」
HAVING:篩「組」
COUNT(*):算列數
COUNT(col):算 col 非 NULL 的列數
WITH Clause(CTE
定義只在單一查詢中使用的暫時表(temporary view)
用來提升可讀性,把複雜查詢拆成步驟
功能等同子查詢,但更好讀
WITH 範例(改寫子查詢
1 2 3 4 5 6 7 8 9 10
WITH BIGDEPTS(Dno) AS ( SELECT Dno FROM EMPLOYEE GROUPBY Dno HAVINGCOUNT(*) >5 ) SELECT Dno, COUNT(*) FROM EMPLOYEE WHERE Salary >40000AND Dno IN BIGDEPTS GROUPBY Dno;
與 IN (SELECT ... HAVING ...) 結果相同
CASE(條件表達式)
SQL 的 if–else
可用於 SELECT / UPDATE / INSERT
1 2 3 4
CASE WHENconditionTHENvalue ELSEvalue END
Recursive Queries 概念
用於階層式資料(如員工與主管)
同一資料表重複引用自己
Recursive CTE 範例
1 2 3 4 5 6 7 8
WITHRECURSIVE SUP_EMP(SupSsn, EmpSsn) AS ( SELECT SupervisorSsn, Ssn FROM EMPLOYEE UNION SELECT E.Ssn, S.SupSsn FROM EMPLOYEE E, SUP_EMP S WHERE E.SupervisorSsn = S.EmpSsn ) SELECT*FROM SUP_EMP;
Base case + Recursive step
重複直到無新資料
SQL 查詢完整結構
1
SELECT → FROM → WHERE → GROUP BY → HAVING → ORDER BY
WHERE:逐列
HAVING:逐組
7. ASSERTION(語意約束
定義超出 PK / FK / CHECK 的限制
多數 DBMS 不支援,考試知道即可
8. TRIGGER
Event + Condition + Action
事件發生時自動執行
1 2 3 4 5
CREATETRIGGER ... AFTER INSERTON ... FOREACHROW WHEN (condition) ...
13. Recursive Queries
1 2 3 4 5 6 7 8 9
WITHRECURSIVE SUP_EMP AS ( SELECT SupervisorSsn, Ssn FROM EMPLOYEE UNION SELECT E.SupervisorSsn, S.Ssn FROM EMPLOYEE E, SUP_EMP S WHERE E.SupervisorSsn = S.Ssn ) SELECT*FROM SUP_EMP;