-- 寫法 1:WHERE 直接過濾右表列
SELECT * FROM _test a
LEFT JOIN _test b ON a.id = b.id
WHERE a.level='20' AND a.month='04'
AND b.level='20' AND b.month='03';
-- 寫法 2:子查詢預過濾再 LEFT JOIN
SELECT a.*, b.*
FROM (SELECT * FROM _test WHERE level='20' AND month='04') AS a
LEFT JOIN (SELECT * FROM _test WHERE level='20' AND month='03') AS b
ON a.id = b.id;
寫法 1 中,右表列出現在 WHERE 條件中,導致 LEFT JOIN 退化為 INNER JOIN。
b.level='20' 要求右表列非 NULL,直接過濾掉了無匹配的左表行。SELECT * FROM _test a
LEFT JOIN _test b
ON a.id = b.id
AND b.level = '20'
AND b.month = '03'
WHERE a.level = '20' AND a.month = '04';
左表過濾放 WHERE,右表過濾放 ON,保留 LEFT JOIN 語義。
SELECT a.*, b.*
FROM (SELECT * FROM _test WHERE level='20' AND month='04') a
LEFT JOIN (SELECT * FROM _test WHERE level='20' AND month='03') b
ON a.id = b.id;
子查詢物化後再 JOIN,邏輯清晰,優化器也易生成優質計劃。
原文配圖顯示:
join_type:ALL/ref 正常,出現 eq_ref 且無 NULL 擴展則可能退化。WHERE b.id IS NULL 反向查詢比 NOT IN/EXISTS 更高效。LEFT JOIN 的核心是「保左表」。任何右表列出現在 WHERE 中(除 IS NULL 判斷),都會破壞外連接語義。養成「左 WHERE 右 ON」或「子查詢預過濾」習慣,從源頭杜絕此類 Bug。