本篇重點
- 了解
DQL
指令的執行順序,從FROM
到LIMIT/OFFSET
- 每個步驟會產生一張虛擬表供下一步驟使用,透過執行順序,可以減少不必要的資料處理,提升查詢效能
- 各個
DQL
指令的使用重點
在 MySQL 查詢語言(DQL,Data Query Language)中,每條 SELECT
查詢語句包含多個不同的指令。了解執行順序,能幫助理解查詢如何處理,並且優化 SQL 查詢,減少不必要的篩選與計算,提高效能。
DQL 指令的執行順序
雖然撰寫 SQL 語句時通常按照 SELECT … FROM … WHERE … GROUP BY … HAVING … ORDER BY … LIMIT … 的順序,但實際上 MySQL 的執行順序是不同的,主要執行順序如下:
FROM → JOIN … ON → WHERE → GROUP BY → AGG_FUNC → 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
分組後,才能使用聚合函數- 可以使用
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 *
等方式,可以顯著提升查詢效能。