場景描述:
由于生產環境的表比較復雜,字段很多。這里我們做下簡化,只為說明今天要聊的問題。
有兩張表 tab1,tab2:
- tab1 數據如下:
- tab2 數據如下:
然后給你看下,我用來統計 name='小白' 的兩條SQL:
//①
SELECT
count(1)
FROM
tab1 LEFT JOIN tab2 ON tab1.NAME = tab2.NAME
AND tab2.NAME = '小白';
// ②
SELECT
*
FROM
tab1
LEFT JOIN tab2 ON tab1.NAME = tab2.NAME
WHERE
tab2.NAME = '小白';
第①個 sql 執行結果如下:
結果并不是返回我預期中的 2,我想了倆小時也沒搞明白為啥,分明表里面是有兩條"小白"的數據,我已經限制了過濾條件怎么不是 2 呢?
第②個 sql 執行結果如下:
顯然按照我之前的理解是錯的:我以為將過濾條件放到 where 和 on 后面是一樣的效果,只是寫法的差異而已。
結果分析
我們直接將①、②這兩條 SQL 查出來的數據搞出來看看就會明白了。
注意:數據庫在通過連接兩張或多張表來返回記錄時,都會生成一張中間的臨時表,然后再將這張臨時表返回給用戶。
- SQL①結果:
通過結果,可以看出這個 SQL 的執行過程:它會以左表作為主表,然后不管 on 后面的條件是否為真也會返回主表的所有記錄。
如果滿足 on 后面的所有條件,那么中間表中左表和右表的字段值都會有,同時中間表的行數可能會大于左表總數,你可以思考下為什么,不明白的可以評論區留言。如果 on 條件不滿足,左表的記錄數也不會少的,這時候右表字段就補 NULL。
- SQL②結果:
這條 SQL,首先會通過 on 后面的條件關聯出一張中間表:
然后在對中間表執行 where 條件,過濾出 NAME = '小白'的數據:
踩坑總結
其實以上結果的關鍵原因就是 left join、 right join、full join 的特殊性,不管 on 上的條件是否為真都會返回 left 或 right 表中的記錄,full 則是 left 和 right 結果的并集。
而 inner jion 沒這個特殊性,滿足 on 后面的條件,表的數據才能查出,可以起到過濾作用。所以,條件放在 on 中和 where 中,返回的結果集是相同的。
在使用 left jion 時,on 和 where 條件的區別如下:
- on 條件是在生成臨時表時使用的條件,它不管 on 中的條件是否為真,都會返回左邊表中的記錄。
- where 條件是在臨時表生成好后,再對臨時表進行過濾的條件,條件不為真的就全部過濾掉。
在多表聯接查詢時,on 比 where 更早起作用。系統首先根據各個表之間的連接條件,把多個表合成一個臨時表后,再由 where 進行過濾,然后再計算。
由此可見,要想過濾條件起到正確的作用,首先要明白這個條件應該在什么時候起作用,然后再決定放在那里。