SELECT 是 SQL 查詢的核心,掌握完整語法順序與各子句語義,能寫出高效、可讀、可維護的查詢。本文系統梳理標準語法、常用模式、性能陷阱。
SELECT [DISTINCT] select_list
FROM table_list
[WHERE where_condition]
[GROUP BY group_by_list]
[HAVING having_condition]
[ORDER BY order_by_list]
[LIMIT [offset,] row_count]
執行順序:FROM → WHERE → GROUP BY → HAVING → SELECT → DISTINCT → ORDER BY → LIMIT。理解此順序可解釋為何 WHERE 不能用別名、HAVING 可用聚合別名。
| 需求 | SQL 範例 | 說明 |
|---|---|---|
| 全表掃描 | SELECT * FROM pet; | 生產禁用,必加 WHERE/LIMIT |
| 指定列 | SELECT name, birth FROM pet; | 減少網路傳輸、利用覆蓋索引 |
| 條件篩選 | SELECT * FROM pet WHERE owner='Gwen'; | 字符串用單引號,日期 '2024-01-01' |
| 多條件 AND/OR | WHERE age>=5 AND sex='f' / OR | 括號控制優先級:(a AND b) OR c |
| 模糊匹配 | WHERE name LIKE 'Tom%' | % 任意長度,_ 單字符;前導 % 無法用索引 |
| 範圍查詢 | WHERE birth BETWEEN '2020-01-01' AND '2020-12-31' | 等價 >= AND <=,含邊界 |
| 集合查詢 | WHERE owner IN ('Gwen','Tom') | 優於多個 OR,優化器可優化 |
| 空值判斷 | WHERE death IS NULL / IS NOT NULL | 不能用 = NULL |
-- 多欄位排序:先按 birth 升序,同 birth 再按 name 降序
SELECT name, birth FROM pet ORDER BY birth ASC, name DESC;
-- 分頁:LIMIT offset, rows(offset 從 0 開始)
SELECT * FROM pet ORDER BY birth LIMIT 0, 10; -- 第 1 頁
SELECT * FROM pet ORDER BY birth LIMIT 10, 10; -- 第 2 頁
-- 深分頁優化(offset 1000000 會掃描 100 萬行再丟棄)
-- 方案:游標分頁 / 覆蓋索引 + 子查詢
SELECT * FROM pet WHERE id > 1000000 ORDER BY id LIMIT 10;
-- 每個 owner 的寵物數量、最早/最晚出生
SELECT owner, COUNT(*) AS cnt, MIN(birth), MAX(birth)
FROM pet
GROUP BY owner
HAVING cnt > 1; -- 過濾聚合後結果
只出現在 SELECT/ORDER BY/HAVING 中的列,必須出現在 GROUP BY 中(ONLY_FULL_GROUP_BY 模式,5.7+ 默認開啟)。
WHERE DATE(created_at) = '2024-01-01' 導致全表掃描,改 WHERE created_at >= '2024-01-01' AND created_at < '2024-01-02'。MATCH...AGAINST 或 Elasticsearch。WHERE a=1 OR b=2 通常不走索引,改 UNION ALL 或建立複合索引。WHERE varchar_col = 123 觸發全表隱式 CAST,嚴重拖慢,統一參數類型。| 欄位 | 優秀值 | 警示值 |
|---|---|---|
type | ref / eq_ref / range | ALL (全表掃描) / index (全索引掃描) |
key | 實際用到的索引名 | NULL (無索引可用) |
rows | 預估掃描行數少 | 遠大於實際返回行數 |
Extra | Using index / Using where | Using filesort / Using temporary |
寫好 SELECT,始於理解語法順序,精於 EXPLAIN 讀懂執行計劃,成於持續重構慢查詢。