本篇重點
- 了解 DQL 指令的邏輯執行順序以及實際執行順序
- 每個步驟會產生一張虛擬表供下一步驟使用,但中間結果是不可見的,使用者只能取得最終查詢結果
- 各個 DQL 指令的使用重點
- MySQL 實際運作時,可能會有查詢優化器影響執行順序
- 必須使用
EXPLAIN指令才有辦法查看執行順序,沒辦法單純依靠邏輯順序推斷
在 MySQL 查詢語言(DQL,Data Query Language)中,每條 SELECT 查詢語句包含多個不同的指令。了解執行順序,能幫助理解查詢如何處理,並且優化 SQL 查詢,減少不必要的篩選與計算,提高效能。
DQL 指令的執行順序
SQL 查詢通常按照 SELECT … FROM … WHERE … GROUP BY … HAVING … ORDER BY … LIMIT … 的語法順序撰寫,但這只是是語法結構上的安排,並非資料實際被處理的先後順序。
邏輯執行順序
資料庫引擎依照以下邏輯步驟處理查詢:
- FROM / JOIN / ON:鎖定資料來源,進行資料表連接。
- WHERE:對原始資料進行列(Row)層級的過濾。
- GROUP BY:將資料集依照指定欄位進行分組。
- AGG_FUNC:執行聚合函數計算(如 SUM、COUNT)。
- HAVING:對分組後的數據進行過濾。
- SELECT:選取顯示的欄位或計算表達式。
- DISTINCT:去除重複的資料列。
- ORDER BY:對結果集進行排序。
- LIMIT / OFFSET:限制回傳的筆數與偏移量。
此邏輯順序用來說明查詢語意如何逐步運作,例如資料來源的組合、條件過濾、分組、聚合與最終輸出結果。每個步驟都會產生一個中間結果(可視為虛擬表),再交由下一個步驟處理,但這些中間結果在實際執行中是不可見的,使用者只能取得最終查詢結果。
範例資料
1 | -- 創建資料表 |
(1) FROM
1 | SELECT [欄位名稱] FROM [表格名稱]; |
- 來源資料表的讀取
範例:
1 | SELECT * FROM users; |
(2) JOIN … ON
1 | SELECT [欄位名稱] FROM [表格1] |
- 當有
JOIN操作時,MySQL 會先依據ON條件來篩選需要JOIN的資料,這有助於減少不必要的數據傳輸,提升查詢效能 JOIN … ON比WHERE更早執行,因此ON條件能過濾掉不必要的資料,提高效能
範例:
1 | SELECT u.uid, u.name, o.order_id, o.commodity_id, o.price |
(3) WHERE
1 | SELECT [欄位名稱] FROM [表格名稱] WHERE [篩選條件]; |
- 在
FROM抓取完表之後,會根據WHERE條件來篩選符合條件的資料 WHERE會在GROUP BY之前執行,因此WHERE不能對聚合函數 (SUM、COUNT) 進行篩選
範例:
1 | SELECT u.uid, u.name, o.order_id, o.commodity_id, o.price |
(4) GROUP BY
1 | SELECT [分組欄位], [聚合函數] FROM [表格名稱] |
- 將符合
WHERE條件的資料分組 GROUP BY後只能SELECT分組的欄位或聚合函數 (如 SUM、COUNT、AVG),如果SELECT分組以外的欄位會是同一分組中任意一行的值,因此可能會與預期結果不符
範例:
1 | SELECT team |
在查詢每個群組中的最新或特定條件的資料時,可以使用 GROUP BY 搭配聚合函數來篩選出所需的關鍵值(例如最新的 ID 或日期),再透過 JOIN 將篩選出的關鍵值與原表進行關聯,確保獲取完整的詳細資訊。
這種方式適用於各種情境,例如查詢每位使用者的最新留言、每個產品的最高價格紀錄、每個客戶的最近一次購買紀錄等。
範例:
查詢每個 uid 的最新訂單可以使用 GROUP BY 搭配 MAX()
- 先找出
orders表中每個uid最新的order_id(假設order_id越大代表越新) - 然後
JOIN回orders表,獲取完整的訂單資訊
1 | SELECT o.* |
(5) AGG_FUNC
1 | SELECT [分組欄位], [聚合函數]([欄位]) FROM [表格名稱] |
- 聚合函數計算(如 SUM、AVG、COUNT)
- 搭配
GROUP BY可針對每組資料進行聚合計算 - 如果沒有
GROUP BY,是將整張資料表當作一組來計算 - 可使用
AS為結果欄位命名,增加可讀性
範例:
1 | SELECT team, COUNT(*) AS team_count, AVG(age) AS age_avg |
(6) HAVING
1 | SELECT [分組欄位], [聚合函數]([欄位]) FROM [表格名稱] |
- 針對聚合函數的結果篩選
範例:
1 | -- 針對聚合函數的結果篩選 team_count > 1 的資料 |
(7) SELECT
1 | SELECT [欄位名稱] FROM [表格名稱]; |
- 選擇要顯示的欄位(
*為選擇所有欄位) - 所有條件已經套用,查詢結果已經確定
範例:
1 | SELECT * FROM users; |
(8) DISTINCT
1 | SELECT DISTINCT [欄位名稱] FROM [表格名稱]; |
- 和
SELECT搭配使用,在SELECT完成後過濾掉重複的資料 DISTINCT是對整個查詢結果去除重複資料DISTINCT用於多個欄位是根據這些欄位的組合值來判斷是否重複,不是單獨判斷某一欄位
範例:
1 | SELECT DISTINCT uid FROM orders; |
(9) ORDER BY
1 | SELECT [欄位名稱] FROM [表格名稱] |
- 在
SELECT完成後,結果會按照ORDER BY進行排序 - 未指定排序,預設為
ASC升冪,順序由小排到大,使用DESC為降冪,順序由大排到小 - 排序可能會影響效能,特別是在大量數據時,要確保排序欄位有索引
範例:
1 | SELECT * FROM orders ORDER BY price; |
(10) LIMIT / OFFSET
1 | SELECT [欄位名稱] FROM [表格名稱] |
- 取出特定範圍的資料
LIMIT用於限制返回的筆數,如果LIMIT使用兩個參數,第一個參數為資料跳過的筆數,第二個參數為跳過後選取的筆數OFFSET用於跳過前幾筆資料,不能單獨使用,必須與LIMIT搭配使用
範例:
1 | SELECT * FROM users LIMIT 3; |
提升查詢效能的方式
建立適當的索引
WHERE、JOIN、ORDER BY相關的欄位應建立索引,減少資料庫掃描時間。
例如:
1 | -- 對 users 表的 age 欄位建立索引 |
先篩選再分組
WHERE會在GROUP BY之前執行,因此應確保WHERE先過濾掉不必要的數據,減少GROUP BY的負擔。
避免 SELECT *,只選取需要的欄位
- 只選擇必要的欄位可以減少 I/O 負擔,提高查詢效能。
結論
了解 DQL 指令的執行順序能編寫更高效的 SQL 查詢。透過優化 WHERE 篩選順序、善用索引、避免 SELECT * 等方式,可以顯著提升查詢效能。



