SQL 與程式語言最大不同:**聲明式** vs **過程式**。SQL 指定「要什麼」,引擎決定「怎麼做」。執行順序與書寫順序**完全不同**。
FROM / JOIN — 確定數據源,產生笛卡爾積(虛擬表 VT1)ON — 連接條件過濾(VT2)OUTER JOIN — 添加外部行(VT3)WHERE — 行級過濾(VT4)GROUP BY — 分組(VT5)HAVING — 組級過濾(VT6)SELECT — 投影列表(VT7)DISTINCT — 去重(VT8,內存臨時表+唯一索引)ORDER BY — 排序,返回**游標**(VC9,非表)LIMIT — 截取(最終結果)TABLE table1 (customer_id, city) TABLE table2 (order_id, customer_id) table1: (163,hangzhou), (9you,shanghai), (baidu,hangzhou), (tx,hangzhou) table2: (1,163), (2,163), (3,9you), (4,9you), (5,9you), (6,tx), (7,NULL)
SELECT a.customer_id, COUNT(b.order_id) AS total_orders FROM table1 AS a LEFT JOIN table2 AS b ON a.customer_id = b.customer_id WHERE a.city = 'hangzhou' GROUP BY a.customer_id HAVING COUNT(b.order_id) < 2 ORDER BY total_orders DESC;
| 步驟 | 操作 | 虛擬表關鍵變化 |
|---|---|---|
| 1 FROM | 笛卡爾積 | VT1:4×7=28 行 |
| 2 ON | a.cust_id=b.cust_id | VT2:6 行(匹配行) |
| 3 LEFT JOIN | 保留左表 | VT3:+1 行(baidu,NULL,NULL) |
| 4 WHERE | city=hangzhou | VT4:4 行(163×2, tx, baidu) |
| 5 GROUP BY | 按 customer_id | VT5:3 組(163, tx, baidu) |
| 6 HAVING | count<2 | VT6:2 組(tx:1, baidu:0) |
| 7 SELECT | 投影 + 聚合 | VT7:(tx,1), (baidu,0) |
| 8 DISTINCT | 無重複 | VT8:同 VT7 |
| 9 ORDER BY | total_orders DESC | VC9:游標 (tx,1) → (baidu,0) |
| 10 LIMIT | 無 | 最終結果同 VC9 |
SELECT 書寫在第一行,但**倒數第三執行**ORDER BY 是**唯一能使用 SELECT 別名**的階段(如 total_orders)ORDER BY 返回**游標**而非表,**不能用於子查詢/視圖/派生表**WHERE 在 GROUP BY 前,**不能使用聚合函數**HAVING 在 GROUP BY 後,**可使用聚合函數**| 錯誤寫法 | 原因 | 正確寫法 |
|---|---|---|
SELECT a, COUNT(*) FROM t WHERE COUNT(*)>1 | WHERE 階段無聚合結果 | HAVING COUNT(*)>1 |
SELECT a, b FROM t GROUP BY a | b 未分組未聚合 | GROUP BY a,b 或 MAX(b) |
CREATE VIEW v AS SELECT * FROM t ORDER BY id | 視圖不接受游標 | 去掉 ORDER BY 或加 LIMIT |
EXPLAIN 驗證實際執行計劃(物理順序可能經優化器調整)ORDER BY(除非配合 LIMIT)《SQL 邏輯查詢處理階段》 — Itzik Ben-Gan 著《Microsoft SQL Server 2012 T-SQL Fundamentals》