這兩種條件放置的位置不同很容易讓人造成混淆,以致經常查詢出莫名其妙的結果出來,特別是副本的條件與主表不匹配時,下面以A,B表為例簡單說下我的理解。
首先要明白的是:
跟在ON 后面的條件是對參與左聯接的數據進行篩選,即在左聯接之前起作用。
跟在WHERE后的條件是對左聯接得到的結果集進行篩選,即在左聯接之后起作用。
我直接把我的結論發出來,建議朋友們自行測試一下,下面是結論:
1) 如果條件是由主表和副表之間的字段構成,那么放在ON后與放在WHERE子條件中所得到的結果是一樣,即這種條件可以隨便放,甚至在SQL中建視圖時,會自動優化放到ON條件后。
如下:
select * from A LEFT OUTER JOIN B ON A.Id=B.UserId AND A.Name=B.UserName
select * from A LEFT OUTER JOIN B ON A.Id=B.UserId WHERE A.Name=B.UserName
上面兩個語句的結果是一樣的,如果用這條語句去建視圖,SQL管理器會自動優化成第一句的寫法,大家可以親自試驗下。
2)如果條件是由進行左聯接的兩個表中的一個表的字段構成,則結果會很不一樣。
1:select * from A LEFT OUTER JOIN B ON A.Id=B.UserId AND B.IsDel=0 (IsDel意思為記錄是否刪除,0為否,1為是。)
2:select * from A LEFT OUTER JOIN B ON A.Id=B.UserId WHERE B.IsDel=0
以上兩句語句使用時要非常注意,這兩種寫法得到的數據是不一樣的。
第一句的意思是:在進行左聯接前,先從表B中篩選出沒有標記為刪除的數據后得到的結果再與A表進行左聯接。
而第二句的意思是:在A,B表進行左聯接后的,再對得到的結果進行“B.IsDel=0”條件過濾。
如果A表中有2條數據,在B表中都能匹配上,那么得到的結果是一樣的,但是,假設A中只有一條記錄在B中能匹配上,那么兩條語句得到的結果就不一樣了。
因為兩表左聯接后得到的結果集中的對應B.IsDel的列(假設生命為B_IsDel)的值其實是NULL值,那么再進行“B_IsDel=0”,這條記錄就會被過濾掉了,
即最終的結果是第一條語句有兩條數據,第二條語句只有一條。
此外,還有下面這種更離奇的情況,假設我們的需求是對A,B兩表進行左聯接,同時希望過濾掉A表中已經被刪除了的數據和B表中已經被刪除了的數據。可能會有如下兩種寫法:
3:select * from A LEFT OUTER JOIN B ON A.Id=B.UserId AND A.IsDel=0 AND B.IsDel=0 (IsDel意思為記錄是否刪除,0為否,1為是。)
4:select * from A LEFT OUTER JOIN B ON A.Id=B.UserId WHERE A.IsDel=0 AND B.IsDel=0
語句4能得到想要的結果。
語句3,這種寫法很少見,執行后,大家會發現語句3的結果中包含了A表中A.IsDel=1的記錄,這是為什么呢?
原因很簡單,因為它是左聯接,下面解釋一下:
我們知道左聯接的邏輯是A表或者說主表中的數據都會出現在最終的結果集中,那么“A.IsDel=0”這個條件在語句3左聯接的過程起到了什么作用呢?
其作用是系統在進行左聯接時,先在A表中用"A.IsDel=0"條件過濾數據(假設過濾掉了R1這條數據),用過濾后的結果再與B表進行左聯接,
但最后整個語句返回的結果集中極會包含R1這條數據,只不過這條記錄對應的B表中的字段全部為NULL值。請大家自行測試理解。
此時,如果想要達到我們的目的,必需在WHERE子句中增加A.IsDel=0條件,即:
select * from A LEFT OUTER JOIN B ON A.Id=B.UserId AND A.IsDel=0 AND B.IsDel=0 WHERE A.IsDel=0
但上面這條語句比較邏輯,也完全沒必要這么寫,因此我的建議是:針對單表字段構成的篩選條件這種情況,最好的做法是直接將條件放到WHERE子句中。