今天我們來講講數據庫篩選條件放 ON 後和放 WHERE 後的區別。
ON 決定如何 "連接" 表,WHERE 決定連接後 "顯示" 哪些行。 這個根本區別導致了在 LEFT JOIN / RIGHT JOIN 外連接中,條件放置位置會產生巨大影響;而在 INNER JOIN 中,效果通常 等價。
ON 條件匹配 被驅動表 的行,生成 "臨時關聯結果集"。LEFT JOIN 會保留 驅動表 所有行,匹配不上的 被驅動表 字段填充為 NULL。
WHERE 會對 "臨時關聯結果集" 進行條件過濾,刪除不滿足的行。
接下來我們搞兩張測試表,一目瞭然。
-- 用户表(驅動表,左表)
CREATE TABLE `ysjz_user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(20) DEFAULT NULL,
`age` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
INSERT INTO `ysjz_user` VALUES (1,'張三',18),(2,'李四',25),(3,'王五',30);
-- 訂單表(被驅動表,右表)
CREATE TABLE `ysjz_order` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` int(11) DEFAULT NULL,
`amount` decimal(10,2) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
INSERT INTO `ysjz_order` VALUES (1,1,100),(2,2,200),(3,2,300),(4,4,500); -- 注:user_id=4無對應用户
場景一:使用 INNER JOIN,查詢 年齡 > 20 的用户及其訂單。
寫法1:條件放 ON 後
SELECT u.*, o.*
FROM `ysjz_user` u
INNER JOIN `ysjz_order` o ON u.id = o.user_id AND u.age > 20;
寫法2:條件放 WHERE 後
SELECT u.*, o.*
FROM `ysjz_user` u
INNER JOIN `ysjz_order` o ON u.id = o.user_id
WHERE u.age > 20;
兩種寫法的 結果一致。寫法1 更高效,因少關聯了 年齡 ≤ 20 的用户。
場景二:使用 LEFT JOIN,保留所有用户,同時顯示 年齡 > 20 的用户及其訂單(≤ 20 的用户訂單顯示為 NULL)。
寫法1:條件放 ON 後(符合要求)
SELECT u.*, o.*
FROM `ysjz_user` u
LEFT JOIN `ysjz_order` o ON u.id = o.user_id AND u.age > 20;
寫法2:條件放 WHERE 後(跟要求不符)
SELECT u.*, o.*
FROM `ysjz_user` u
LEFT JOIN `ysjz_order` o ON u.id = o.user_id
WHERE u.age > 20;
寫法2 將 張三 過濾了,並沒有 保留所有用户。
場景三:使用 LEFT JOIN,保留所有用户,同時顯示 訂單金額 > 200 的訂單(無符合條件訂單的用户填充為 NULL)。
寫法1:條件放 ON 後(符合要求)
SELECT u.*, o.*
FROM `ysjz_user` u
LEFT JOIN `ysjz_order` o ON u.id = o.user_id AND o.amount > 200;
寫法2:條件放 WHERE 後(跟要求不符)
SELECT u.*, o.*
FROM `ysjz_user` u
LEFT JOIN `ysjz_order` o ON u.id = o.user_id
WHERE o.amount > 200;
寫法2 過濾了 無符合條件訂單的用户。
場景二 和 場景三 其實相差不大,只是條件作用的表不一樣。
ON 後面優先放 "表之間的關聯鍵"(如 u.id = o.user_id),非關聯的篩選條件(如 u.age > 20)是否放 ON 後,取決於是否要保留驅動表的行。
總結:ON 管關聯,WHERE 管過濾;LEFT JOIN 用 ON 保行,INNER JOIN 用 ON 提效。
別人的嘴你堵不住,但自己的心卻任由自己掌控。-- 煙沙九洲