四種聯系(join)的區別及用法
- 鏈接:
作為動詞,它表示將兩個或多個表的內容結合在一起并產生一個結果集,該結果集對每個表的列和行進行合并。表的聯接一般都使用它們共有的數據。例如,您可以對有一個共同 pub_id 列的 titles 表和 publishers 表聯接,產生一個包含書名信息和出版商信息的結果集。
作為名詞,表示對表進行聯接的過程或結果,如在術語"內部聯接"中表示對表聯接的一種特殊的方法。
- 聯接條件(join condition)
一個比較子句,它指定了表是如何通過它們的聯接字段相聯系的。最普通的聯接條件是相等(一個等聯接),在等聯接中聯接字段的值必須相同。例如,您可以通過在 titles 表和 publishers 表的 pub_id 列中查找相匹配的值聯接這兩個表。然而,任何比較運算符都可以是比較條件的一部分。
- 內部聯接(inner join)
一個聯接,在該聯接中只有當聯接字段的值滿足某些特定的準則時才將兩個表的記錄進行結合并添加到一個查詢結果中。例如,在查詢設計器視圖中,表之間的缺省聯接是一個內部聯接,它只有當聯接字段的值相等時才從兩個表中選擇記錄。
- 外部聯接(outer join)
一個聯接,該聯接還包括那些和聯接表中記錄不相關的記錄。您可以創建一個外部聯接的三種變形來指定所包括的不匹配行:左外部聯接、右外部聯接和完全外部聯接。
- 左外部聯接(left outer join)
一種外部聯接類型,在該聯接中包括第一個命名表(左邊的表,它出現在 JOIN 子句的最左邊)的所有行。右邊表中沒有匹配的行不出現。例如,您可以在 titles 表和 publishers 表之間創建一個左外部聯接,以包括所有的書名,不論書名有無出版商的信息。
- 右外部聯接(right outer join)
一種外部聯接,在該聯接中包括第二個命名表(右邊的表,出現在 JOIN 子句中的最右邊)的所有行。不包括左邊表中沒有匹配的行。例如,titles 表和 publishers 表之間的一個右外部聯接將包括所有的出版商,甚至包括那些在 titles 表中沒有書名的出版商。
以上是MSDN中對鏈接的定義。現在我們就從這四種鏈接所使用的不同方法來看他們的結果有什么不同。
titles 表sh(書號) ph(出版商編號)232342 0010432 00382478123 005
publishers 表
ph(出版商編號) mc(出版商名稱)
001 紅虎
002 rmh
003 hazl
現要把這兩個表的內容合成如下的表結構:
sh(書號) ph(出版商編號) mc(出版商名稱)
現在看看采用四種鏈接方法的結果會有什么不同。先說說他們的命令:
內聯接:
sele titles.sh,publishers.ph,publishers.mc ;
? from titles inner join publishers ;??? &&內聯接中的inner是可以省略的
??? on titles.ph=publishers.ph
外聯接:
sele titles.sh,publishers.ph,publishers.mc ;
? from titles outer join publishers ;
??? on titles.ph=publishers.ph
左聯接:
sele titles.sh,publishers.ph,publishers.mc ;
? from titles left join publishers ;
??? on titles.ph=publishers.ph
右聯接:
sele titles.sh,publishers.ph,publishers.mc ;
? from titles right join publishers ;
??? on titles.ph=publishers.ph
大家可能看到,除了在join之前的那個關鍵字不同之外,其他地方是一模一樣的,鏈接條件(即on那一部分)也是一樣的。結果:
內鏈接:
232342 001 紅虎0432 003 hazl
全鏈接:
232342 001 001 紅虎Null Null 002 rmh0432 003 003 hazl82478123 005 Null Null
左鏈接:
232342 001 001 紅虎0432 003 003 hazl82478123 005 Null Null
右鏈接:
232342 001 001 紅虎Null Null 002 rmh0432 003 003 hazl
所以我們很容易記住:
1、左鏈接:就是以join的左邊那個表為"主",以titles.ph=publishers.ph為判斷標準,不管右邊的表有沒有對應的記錄,都要把左邊表的記錄放在結果中去,但右邊表沒有相應的記錄那應該放個什么數值進去?答案是就放個Null,表示沒有。在左鏈接中,某記錄在右邊表,卻不在左邊表,那是不放進去結果去的,原因是左邊表才是"主",要不要放由它決定:它有的,就一定放進去,它沒有的,就不要了。
2、右鏈接:和左鏈接一樣,只不過為"主"的一方調過來了,換成是由右邊做"主"。
3、內鏈接:和左、右鏈接不同,它一定要左、右兩邊都有的記錄才會放進結果,如果有某個記錄不存在于任何一邊,那這個記錄是不會出現在結果中去的。
4、外鏈接:跟內聯接相,反,相當于左、右鏈接的合并:不管什么情況,只要某個記錄出現在這兩個表,就一定會出現在結果中去,然后象左、右鏈接的處理方法一樣,用Null來填充沒有對應值的字段。
注:以上說的"有"、"沒有",意思是以titles.ph=publishers.ph為判斷標準來下決定的。比如當前titles表的ph是"002",而在publishers中,沒有一個記錄的ph的值是"002"的,所以就說"002"這個值在titles有,在publisher中沒有,這樣titles.ph為"002"的記錄就會被選中,最后放在結果中去。
大家如果想一下,這個on的作用跟where、having似乎有點類似,都是起到過濾的作用:根據條件選取所取的記錄,而根據命令的工作流程,這個on是比where、having都要早執行的,而它里面的條件表達式又不一定是titles.ph=publishers.ph的形式,還可以繼續擴充,變成一個很復雜的條件表達式,從而完成一個很有效的、where和having都不能實現的過濾功能。具體的比較請看 on、where、having的區別 一節。
剛才舉的例子,表中的ph都是沒有重復的。現在以內聯接為例,舉個判斷字段中內容有重復的例子:
Temp1 temp2Aa aa1 11 22 23 2
sele temp1.aa,temp2.aa ; from temp1 join temp2 ; on temp1.aa=temp2.aa
運行結果是:
1 11 12 22 22 2
很明顯,有些記錄重復了幾遍。temp1.aa中的雖然只有1個2,但temp2.aa有3個2,所以結果就會有1*3=3個2了。如果temp1.aa而2個2的話,那結果就會有2*3=6個2了。
知道了這一點,在做多表鏈接查詢的時候很有用。你要考慮第一、二個鏈接后的結果跟第三個表鏈接時,會不會出現這種情況?如果有,那是不是你想要的?如果有,那怎么處理?有些朋友說做這個命令的結果中有些記錄會比正確的結果大幾倍,就要看看是不是出現了這種重復算的情況。
學會了鏈接,在開始做之前,先要說一個很重要的問題:在視圖設計器來看多個表的聯接關系,它們之間的鏈接是用一條線連接起來的,看起來就象一串糖葫蘆。如果一個表同時和三個表聯接,那看起來就象一支分叉的樹枝了,那這種情況結果就不對了。大家可能不明白我在說什么,我舉個例子大家就會明白了。
有一個產品表、一個進貨明細表、一個出貨明細表,現在的要求是要求產品表中所有的產品的進、出情況,也就是把三個表象join命令那樣合成一個表,如果沒有相應的進、出記錄,也照樣列出來但不計較null值。剛開始學的朋友很可就會這樣做:
1、 在設計器里添加這三個表;
2、 然后用產品表中的產品編號分別與其它二個表左鏈接,這樣產品表中就有二個鏈接(也就是二條線了);
3、 然后把三個表的字段都做為輸出字段。
但結果呢?不對。只有一個表的記錄出現在結果中,即使把四種鏈接類型都試一下,結果都是不對的。
為什么呢?我估計是以下原因:如果產品表只與進貨表鏈接的話,系統根據產品表和進貨表的聯接關系,以產品表為左表,和進貨這個右表組成一個臨時結果,然后又以臨時表為左表,再去找進貨表的右邊表。而進貨表的右邊沒有表,這時系統就停止鏈接,交給where去過濾了。但現在產品表同時跟二個表左聯接,系統會自動選其中一個先進行鏈接,鏈接結果出來后,這個臨時結果的右邊就沒有表了,系統就停止鏈接動作了。剩下的出貨表、退貨表都還沒鏈接,所以那個表等于沒用。
解決的方法是:進貨表用進貨表的產品編號全鏈接產品表,然后產品表又用產品表的產品編號全鏈接出貨表,進、貨表的順序可以調過來,但產品表一定要在中間,且兩個鏈接類型都是全鏈接,否則結果都不對。這樣的鏈接情況,在設計器里按鏈接中的各個表的左右順序排起來,很直觀的:就是一串!沒有分叉。這個方法的實現過程就是:
進貨表全鏈接產品表,即使某種產品沒有進貨,但得出來的結果也一樣有這個記錄,只是它的進貨內容是null值。然后這個臨時結果又跟出貨表全鏈接,這次的結果就前一步差不多,有出貨內容的記錄就有出貨數量,否則就是null值。因為沒有分叉,所以全部表都鏈接進去了,結果也就對了(當然如果鏈接類型錯了,結果也是不對的)。
看了剛才那個問題之后,還有一個問題也要說一下。在剛才那個例子中,如果產品表中某個產品編號出現了重復,有N個記錄的編號相同,而在進貨表里這個編號的記錄也出現M個,這樣一來,結果就有點不同了。首先在進貨表跟產品表的全鏈接結果里,這個編號就會出現N*M次,就不是一次了。然后這個臨時表再去跟出貨表全鏈接時,即使這個編號在出貨表里出現一次,但在最后的鏈接結果中,這個編號還是會出現N*M次,那它的出貨記錄也重復了N*M次了。如果現在要sum()出貨記錄的話,那出貨數量就會放大了N*M倍了,進貨記錄也不準了。所以如果產品表中的編號有重復的話,那結果就很可能會不對了。
但產品表的編號沒有重復,那結果就一定會正確呢?也未必。大家試一下,假設進貨表和產品表的編號"001"都是只出現一次,但出貨表中就出現了二次。那最后的結果中"001"還是出現了二次,二次的產品名稱、進貨數量都是相同的,只是出貨數量不同而已。如果這時sum(),結果還是不對。
所以如果想在多表鏈接后進行sum()之類的匯總操作,使用以上的方法是不行的。解決方法是使用union,用它來將進貨的匯總情況跟出貨的匯總情況合起來,從而避免互相干擾。
另注:3個表之間的連接
eg:????? select a.* b.field1,c.field2 from table a left outer jion table b on a,field1=b.field1 left outer join table?c on a.field2=c.field2
posted on 2007-02-09 12:00 小辭猬 閱讀(457) 評論(0) 編輯 收藏 所屬分類: DataBase