MySQL 官方文檔明確指出:EXISTS 子查詢的選擇列表會被忽略,SELECT *、SELECT 1、SELECT column 語義等價。但在 5.6.31 / 5.7.14 版本中,若子查詢包含聚合函數(如 MAX()),會導致錯誤返回 TRUE。
-- 環境:MySQL 5.6.31 / 5.7.14
CREATE TABLE t3 (id INT, t DATETIME);
INSERT INTO t3 VALUES (1, '20160812');
-- 正常情況:id=2 不存在,返回空集
SELECT 1 FROM dual WHERE EXISTS (SELECT id FROM t3 WHERE id = 2);
-- Empty set (0.15 sec)
-- 異常情況:包含 MAX() 聚合函數,錯誤返回 1
SELECT 1 FROM dual WHERE EXISTS (SELECT MAX(id) FROM t3 WHERE id = 2);
-- +---+
-- | 1 |
-- +---+
-- | 1 |
-- +---+
結論:id=2 明確不存在,但第二條語句誤判為 TRUE。
EXPLAIN EXTENDED SELECT 1 FROM dual WHERE EXISTS (SELECT id FROM t3 WHERE id = 2);
-- 改寫後 SQL:/* select#1 */ select 1 AS `1` from DUAL where 0
-- Extra: Impossible WHERE
優化器識別出子查詢永無滿足行,直接將 WHERE 條件常量折疊為 0(FALSE)。
EXPLAIN EXTENDED SELECT 1 FROM dual WHERE EXISTS (SELECT MAX(id) FROM t3 WHERE id = 2);
-- 改寫後 SQL:/* select#1 */ select 1 AS `1` from DUAL where 1
-- Extra: No tables used
優化器未能正確處理聚合函數語義,將條件常量折疊為 1(TRUE),導致外層查詢無條件執行。
-- 同樣觸發 Bug
SELECT 1 FROM dual WHERE EXISTS (SELECT MIN(id) FROM t3 WHERE id = 2);
SELECT 1 FROM dual WHERE EXISTS (SELECT SUM(id) FROM t3 WHERE id = 2);
SELECT 1 FROM dual WHERE EXISTS (SELECT COUNT(*) FROM t3 WHERE id = 2);
SELECT 1 FROM dual WHERE EXISTS (SELECT AVG(id) FROM t3 WHERE id = 2);
-- 非聚合函數正常
SELECT 1 FROM dual WHERE EXISTS (SELECT ABS(id) FROM t3 WHERE id = 2);
SELECT 1 FROM dual WHERE EXISTS (SELECT FLOOR(id) FROM t3 WHERE id = 2);
只有聚合函數觸發該 Bug,標量函數不受影響。
已提交至 MySQL Bug 系統:Bug #82562
EXISTS 子查詢統一使用 SELECT 1 或 SELECT *,避免任何列引用或函數調用EXISTS (SELECT MAX/MIN/SUM/COUNT/AVG ...) 模式IN 子查詢或 JOIN 重寫,邏輯等價且無此 Bug此 Bug 屬於優化器常量折疊邏輯缺陷,雖觸發條件較窄,但可能導致業務邏輯嚴重偏差(如權限判斷、存在性檢查),建議全量排查並規範寫法。