一文搞懂SQL中的各種聯結——內聯結、自然聯結、自聯結、交叉聯結
一、概述
所謂“ 聯結 ”指的是資料表和本身,以及不同資料表之間的“ 聯結關係 ”。常見的聯結有自連線、自然連線、內連線、外聯結、完全連線等等。本文以SQLite資料庫作為例項講解,建立了一個名為product的資料庫用來儲存產品資訊。資料庫中有兩個表,一個productinfo,用來儲存每一種產品的詳細資訊,另一個是vendors,用來儲存提供產品的供應商資訊,他們的結構分別如下:
productinfo表的資訊如下,productname設定為主鍵(primary key)
vendors表的資訊如下,vendname設定為主鍵(primary key):
二、自連線(self-join)
所謂自連線,指的是一個表自己和自己連線,用來解決什麼例子呢?比如我要查詢所有的和productname=‘hc_002’來自於同一個生產廠商的的產品資訊(假設我不知道hc_002是華測生產的,因為如果我知道,直接篩選就可以了,沒必要聯結);那就要分為兩步走,第一我要先查詢‘hc_001'到底是那一家公司生產,第二步才是查詢公司是華測生產的所有產品的資訊。再比如,我想給於’jim‘在同一個公司的所有員工傳送一封郵件(假設我不知道Jim在哪個公司)同樣要兩步走,第一先查詢Jim在哪一家公司,然後我再用查到的公司進行篩選。
針對上面的問題,我當然可以分步去查詢了,但是為了更快速查詢,“ 自聯結 “可以一步到位。一本文的productinfo表格而言。
SELECT C1.vendname,C1.productname,C1.productprice,C1.weight,C1.guide
FROM productinfo AS C1,productinfo AS C2 /*注意給同一個表區別名,為了區分*/
WHERE C1.vendname=C2.vendname AND C2.productname='hc_002'
當然同樣的問題也可以用“ 子查詢 ”去完成。
SELECT vendname,productname,productprice,weight,guide
FROM productinfo
WHERE vendname=(SELECT vendname FROM productinfo WHERE productname='hc_002')
上面兩種查詢的效果都是一樣的,如下所示:
總結:子查詢和自聯結都可以完成上面所說的那種情況,但是有兩個注意點
(1)自聯結需要使用別名 (AS關鍵字); (2)推薦使用自聯結,不使用子查詢,查詢效率相對較高。
三、內聯結(inner join)——也稱之為“ 等值聯結 ” (equijoin)
適用情況:適用於兩個表或者是多個表有關係的情況,比如上面,如果將vendors表格也合併到productinfo表格裡面去,因為每個廠商的公司地址、公司電話都是一樣的,所以會出現很多重複的資料,這不僅浪費儲存資源,而且降低了查詢效率,為了解決問題,是用兩個表,一個表專門用來儲存公司地址和電話資訊,及vendors,另一個專門儲存各個公司所生產的產品詳細資訊。
現在我要查詢每一公司所生產的所有產品的詳細資訊,怎麼辦?
(1)方法一——笨辦法
select vendname,productname,productprice,weight
from productinfo
where vendname=’上海華測‘ORvendname=’南方數碼’ORvendname=’北京吉威‘。。。。。。這樣逐個刪選
如果只有一個表,這麼做自然沒問題,既然有另一個表,這麼做都沒用到另一個表,自然不可取。
(2)方法二——內聯結
select vendname,productname,productprice,weight
from productinfo,vendors
where productinfo.vendname=vendors.vendname
除此之外,有專門的內聯結語句
select vendname,productname,productprice,weight
from productinfo INNER JOIN vendors
ON productinfo.vendname=vendors.vendname
結果如下:
兩個表格中具有對應關係的 5 家公司都選出來了,至於什麼 阿里巴巴、四維圖新、拓普康是沒有對應關係的。如果我不需要這麼多,只需要選出兩家指定公司的的,南方數碼 武漢吉奧這兩家呢,新增一點即可,如下:
select vendname,productname,productprice,weight
from productinfo,vendors
where productinfo.vendname=vendors.vendname and (vendors.vendname='南方數碼' or vendors.vendname='武漢吉奧')
或者是使用專門的內聯結語句
select vendname,productname,productprice,weight
from productinfo INNER JOIN vendors
ON productinfo.vendname=vendors.vendname and (vendors.vendname='南方數碼' or vendors.vendname='武漢吉奧')
結果為
從上面可以看出,專門的內聯結語句就是將 逗號,替換成 inner join,然後將where 替換成 on 即可。
注意,如果不使用 where 或者是 on子句會怎麼樣呢?如下:
select vendname,productname,productprice,weight
from productinfo inner join vendors /*沒有限制欄位,直接內聯結兩個資料表*/
這樣做的結果是產生了168條記錄,因為productinfo表格有24行,vendors表有7行,,故而總共有24*7=168條記錄。這稱之為
“ 笛卡爾積 ”,笛卡爾積檢索出的行目是兩個資料表中行數的乘積。返回笛卡爾積的聯結稱之為“ 叉聯結 ”(cross join)。
孤兒為了獲取需要的資料,一定不能忘記了where子句或者是on子句。
總結:(1)內聯結是很有必要的 (2)內聯結因為有兩個表,注意“ 完全限定名 ”的使用。
(3)不要過多對三個及三個以上表使用內聯結,會降低效能,而且連線表有最大數目限制,不同資料庫不一樣,可參考文件。
(4)內聯結一定不能忘了 where 子句或者是 on 子句,否則會產生出乎意料的結果(笛卡爾積)
四、叉聯結(cross join)——參見上面的笛卡爾積。
五、自然聯結(Natural join)
1、內連線與等值連線是一回事情。
等值連線是條件連線在連線運算子為“=”號時的特例。它是從關係R與S的廣義笛卡爾積中選取A,B屬性值相等的那些元組
自然連線是一種特殊的等值連線,它要求兩個關係中進行比較的分量必須是相同的屬性組,並且在結果中把重複的屬性列去掉
等值連線表示為RA=BS,自然連線表示為RS;自然連線是除去重複屬性的等值連線。兩者之間的區別和聯絡如下:
比如:本文以SQLite為例加以說明,不同資料庫會稍有區別。
內聯結程式碼如下:
select p.*,v.*
from productinfo as p inner join vendors as v
on p.vendname=v.vendname
執行結果如下:
自然聯結程式碼如下:
select p.*,v.*
from productinfo as p natural join vendors as v
結果如下:
可以發現,上面二者的結果是一樣的,但是“ 自然聯結 ”不需要使用where 或者是 on 限定條件,它會自動根據欄位的只加以匹配,這就是“自然”兩個字的含義,即自發的匹配。
但是,自然結合 這種自發的匹配並不是隨意的 它必須有一個嚴格的限制條件,那就是兩個表中必須要具有公共欄位,而且這兩個公共欄位的名稱必須一樣,值得型別也必須一樣,才能讓其“ 自發 ”的匹配。即productinfo和vendors都有一個相同的欄位vendname,且他們的返回值一樣。
2、比如現在有一個新的表vendors01,他的結構如下:
該表於vendors表格的區別在於,他不再是vendname,而是vendname01,這時候就沒辦法進行自然匹配了,那會產生怎樣的結果呢?
select p.*,v.*
from productinfo as p natural join vendors01 as v
這時候進行自然匹配會產生一個 行數為72的結果,因為雖然vendname01裡面依然有 南方數碼 和 上海華測,但是因為欄位的名稱不一樣,會預設為他們不是一樣的列。故而會產生笛卡爾積。此時對這種列名稱不一樣的情況,只有進行內聯結了,內聯結的程式碼如下:
select p.*,v.*
from productinfo as p inner join vendors01 as v
on p.vendname=v.vendname01 /*對於1內聯結來說,列名不一樣沒關係*/
結果為:
由此可見,及時列名不一樣,只要該列的值有一樣的,內聯結依然適用,這就是為什麼稱之為“ 等值聯結”的原因了。
總結
1、自然連線是特殊的內聯結(等值聯結),自然聯結不能有where和on去限制篩選
2、等值連線要求相等的分量,不一定是公共屬性(即相同的列名);而自然連線要求相等的公共屬性(列名)。
關於“ 外聯結 ”,請參見下文。