SELECT 是 MySQL 使用頻率最高、變體最豐富、優化空間最大的語句。本文系統梳理完整語法結構、核心子句行為、結果導出、集合運算、去重邏輯與性能優化實戰模式,助您寫出**精準、高效、可維護**的查詢代碼。
SELECT
[ALL | DISTINCT | DISTINCTROW]
[HIGH_PRIORITY] [STRAIGHT_JOIN]
[SQL_SMALL_RESULT] [SQL_BIG_RESULT] [SQL_BUFFER_RESULT]
[SQL_CACHE | SQL_NO_CACHE] [SQL_CALC_FOUND_ROWS]
select_expr, ...
[INTO OUTFILE 'file_name' export_options | INTO DUMPFILE 'file_name']
[FROM table_references
[WHERE where_definition]
[GROUP BY {col_name | expr | position} [ASC | DESC], ... [WITH ROLLUP]]
[HAVING where_definition]
[ORDER BY {col_name | expr | position} [ASC | DESC], ...]
[LIMIT {[offset,] row_count | row_count OFFSET offset}]
[PROCEDURE procedure_name(argument_list)]
[FOR UPDATE | LOCK IN SHARE MODE]
WHERE vs HAVING:過濾邊界劃分| 維度 | WHERE | HAVING |
|---|---|---|
| 執行階段 | 分組前、聚合前 | 分組後、聚合後 |
| 可用列 | 表列、衍生列 | 分組列、聚合函數、別名 |
| 索引利用 | 可用索引 | 不可用索引(全掃描) |
| 典型用法 | WHERE status=1 | HAVING COUNT(*) > 10 |
反模式:SELECT col FROM t HAVING col > 0 → 應改為 WHERE col > 0。
GROUP BY 與 WITH ROLLUP:多維聚合
-- 基礎分組
SELECT dept, COUNT(*) AS cnt FROM emp GROUP BY dept;
-- 帶排序(MySQL 5.7+ 默認不再隱式排序,需顯式 ORDER BY)
SELECT dept, COUNT(*) AS cnt FROM emp GROUP BY dept ORDER BY cnt DESC;
-- 超級聚合:小計+總計
SELECT dept, job, COUNT(*), SUM(sal) FROM emp GROUP BY dept, job WITH ROLLUP;
-- 結果包含:(dept,job)、(dept,NULL)、(NULL,NULL) 三層聚合
ORDER BY 多維排序技巧
-- 別名排序
SELECT college, region AS r, seed AS s FROM tournament ORDER BY r, s;
-- 位置排序(不推薦,易維護性差)
SELECT college, region, seed FROM tournament ORDER BY 2, 3;
-- 降序
SELECT a, COUNT(b) FROM test_table GROUP BY a ORDER BY a DESC;
LIMIT 分頁:從淺分頁到深分頁優化
-- 前 10 行
SELECT * FROM tbl LIMIT 10; -- 等同 LIMIT 0,10
-- 第 2 頁(每頁 10 行)
SELECT * FROM tbl LIMIT 10, 10; -- offset=10, count=10
-- 尾頁優化:大 offset 性能劣化(需掃描 offset+count 行)
-- 方案 1:延遲關聯(覆蓋索引定位主鍵再回表)
SELECT * FROM tbl INNER JOIN (SELECT id FROM tbl ORDER BY id LIMIT 100000, 10) AS t USING (id);
-- 方案 2:游標分頁(記住上頁最大 ID)
SELECT * FROM tbl WHERE id > 100000 ORDER BY id LIMIT 10;
SELECT ... INTO OUTFILE:高性能結果導出
SELECT a, b, a+b INTO OUTFILE '/tmp/result.txt'
FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '"'
LINES TERMINATED BY '\n'
FROM test_table;
FILE 權限,文件不能預存在。mysql -e "SELECT ..." > file.txt。INTO DUMPFILE:單行無分隔符,適合導出 BLOB 二進制數據。UNION 集合運算:去重與性能權衡
(SELECT a FROM t WHERE a=10) UNION (SELECT a FROM t WHERE a=11) ORDER BY a LIMIT 10;
| 類型 | 行為 | 性能 |
|---|---|---|
UNION / UNION DISTINCT | 去重(全局排序/哈希) | 慢,臨時表開銷大 |
UNION ALL | 保留重複,直接拼接 | 快,流式輸出 |
原則:業務允許重複或已知無重複時,**強制用 UNION ALL**。
DISTINCT 去重:代價與替代方案
SELECT DISTINCT a FROM t;
SELECT COUNT(DISTINCT a) FROM t;
DISTINCT = DISTINCTROW,觸發臨時表+排序/哈希去重。GROUP BY(可用索引)、應用層去重、HyperLogLog 估算。| 提示 | 作用 | 慎用場景 |
|---|---|---|
SQL_CACHE / SQL_NO_CACHE | 控制 Query Cache 行為 | Query Cache 已棄用/禁用 |
SQL_SMALL_RESULT | 結果集小,用臨時表/內存表 | 優化器通常能自動判斷 |
SQL_BIG_RESULT | 結果集大,用磁盤臨時表 | 同左 |
SQL_BUFFER_RESULT | 強制結果放入臨時表,釋放鎖 | 長查詢阻塞寫入時 |
SQL_CALC_FOUND_ROWS | 配合 FOUND_ROWS() 獲取無 LIMIT 總行數 | 全表掃描開銷大,分頁建議改用 COUNT(*) 獨立查詢 |
STRAIGHT_JOIN | 強制按 FROM 順序連接 | 優化器選錯連接順序時(極少見) |
HIGH_PRIORITY | 提升查詢優先級(MyISAM) | InnoDB 無效 |
-- 當前讀(加排他鎖,阻塞其他寫/當前讀)
SELECT * FROM t WHERE id=1 FOR UPDATE;
-- 當前讀(加共享鎖,阻塞寫,允許其他共享鎖)
SELECT * FROM t WHERE id=1 LOCK IN SHARE MODE;
-- 快照讀(MVCC,無鎖,讀已提交/可重複讀隔離級別下)
SELECT * FROM t WHERE id=1;
原則:業務允許下**優先快照讀**,必要時用最小範圍、最短持有時間的當前讀。
type (ALL/index/range/ref/eq_ref/const/system)、key、rows、Extra (Using where/Using index/Using temporary/Using filesort)。SELECT 列 ⊆ 索引列 → Using index,避免回表。SELECT *:增加 I/O、網絡、緩存污染、無法用覆蓋索引。WHERE 條件盡量貼近存儲引擎層(索引條件下推 ICP)。IN (SELECT ...) / EXISTS 在 5.6+ 多優化為 Semi-Join,但複雜子查詢建議顯式改寫。ANALYZE TABLE 定期更新,誘導優化器選擇最優執行計劃。SELECT 是查詢的藝術,亦是優化的戰場。從「只取所需列」的覆蓋索引,到「深分頁」的延遲關聯重構;從 UNION ALL 的流式拼接,到 FOR UPDATE 的精準加鎖——每一個子句的選擇,都在為性能投票。願本文手冊助您在海量數據中,精準定位、極速歸航、穩健交付。