SQL 標準查詢執行順序(邏輯階段,非物理執行計畫):
別名在 **第 7 階段 (SELECT)** 才產生,故 **WHERE/GROUP BY/HAVING (第 4/5/6 階段) 不可引用別名**,只有 **ORDER BY (第 9 階段)** 可用。
MySQL 為了便利性,**延後 HAVING 執行至 SELECT 之後**,故下列寫法在 MySQL 合法、標準 SQL/ SQL Server 報錯:
-- MySQL ✅ / SQL Server ❌
SELECT COUNT(*) AS c FROM Score GROUP BY Mid HAVING c > 2;
-- 標準寫法(跨平台相容)
SELECT COUNT(*) AS c FROM Score GROUP BY Mid HAVING COUNT(*) > 2;
⚠️ **生產環境請遵循標準寫法**,避免遷移資料庫時語法失效。
-- ❌ 錯誤:CASE 內引用 SELECT 別名 rscore (SQL Server/標準 SQL)
SELECT
m.MName AS UserName,
AVG(s.Score) AS RScore,
CASE
WHEN RScore < 60 THEN '差' -- 別名在此不可用
WHEN RScore BETWEEN 60 AND 80 THEN '良'
WHEN RScore > 80 THEN '優'
ELSE '不合法'
END AS Grade
FROM Score s JOIN Student m ON s.Mid = m.Mid
GROUP BY m.MName
ORDER BY RScore DESC; -- ✅ ORDER BY 可用別名
-- ✅ 正確:重寫聚合或用子查詢/CTE
;WITH Base AS (
SELECT m.MName, AVG(s.Score) AS AvgScore
FROM Score s JOIN Student m ON s.Mid = m.Mid
GROUP BY m.MName
)
SELECT
MName AS UserName,
AvgScore AS RScore,
CASE
WHEN AvgScore < 60 THEN '差'
WHEN AvgScore BETWEEN 60 AND 80 THEN '良'
WHEN AvgScore > 80 THEN '優'
ELSE '不合法'
END AS Grade
FROM Base
ORDER BY RScore DESC;
FROM Student s
JOIN Score sc ON s.Id = sc.StudentId
WHERE sc.Subject = 'Math' -- VT4: 篩選後
GROUP BY s.Name -- VT5: 分組
HAVING AVG(sc.Value) > 60 -- VT6: 分組篩選 (不可用別名)
SELECT s.Name, AVG(sc.Value) AS Avg -- VT7: 投影,別名在此誕生
ORDER BY Avg DESC; -- VT9: 排序,別名可用
| 子句 | 可否用別名 | 替代方案 |
|---|---|---|
| WHERE | ❌ | 重寫條件 / 子查詢 / CTE |
| GROUP BY | ❌ | 重寫運算式 / 子查詢 / CTE |
| HAVING | 標準 ❌ / MySQL ✅ | 重寫聚合運算式(建議) |
| SELECT (同層其他欄位) | ❌ | 重寫 / CTE / CROSS APPLY |
| ORDER BY | ✅ | 直接使用 |
「別名不能用」不是 Bug,是 **SQL 邏輯執行順序** 決定的必然結果。理解「虛擬表逐階段產生、別名在 SELECT 階段誕生、ORDER BY 最後執行」三鐵律,配合 **CTE/子查詢** 將需重用的運算式前置,即可寫出跨平台相容、邏輯清晰、效能穩定的查詢。下次寫到想在 WHERE 用別名時,請先問自己:這個別名在虛擬表的哪個階段才存在?