Join是關係型數據庫系統的重要操做之一,SQL Server中包含的經常使用Join:內聯接、外聯接和交叉聯接等。若是咱們想在兩個或以上的表獲取其中從一個表中的行與另外一個表中的行匹配的數據,這時咱們應該考慮使用Join,由於Join具體聯接表或函數進行查詢的特性html
本文將經過具體例子介紹SQL中的各類經常使用Join的特性和使用場合:sql
首先咱們在tempdb中分別定義三個表College、Student和Apply,具體SQL代碼以下:shell
USE tempdb ---- If database exists the same name datatable deletes it. IF EXISTS(SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'College') DROP TABLE College; IF EXISTS(SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'Student') DROP TABLE Student; IF EXISTS(SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'Apply') DROP TABLE Apply; ---- Create Database. create table College(cName nvarchar(50), state text, enrollment int); create table Student(sID int, sName nvarchar(50), GPA real, sizeHS int); create table Apply(sID int, cName nvarchar(50), major nvarchar(50), decision text);
內聯接(Inner join)是最經常使用的聯接類型之一,它查詢知足聯接謂詞的數據。數據庫
假設咱們要查詢申請表Apply中申請學校的相關信息,因爲Apply表中包含學校名字咱們並不能預知,因此咱們能夠根據cName來內聯接(Inner join)表College和Apply,從而找到Apply表中包含學校的信息。app
具體SQL代碼以下:函數
---- Gets college information from college table ---- bases on college name. SELECT DISTINCT College.cName, College.enrollment FROM College INNER JOIN Apply ON College.cName = Apply.cName
圖1查詢結果性能
cName | state | enrollment |
Stanford | CA | 15000 |
Berkeley | CA | 36000 |
MIT | MA | 10000 |
Cornell | NY | 21000 |
Harvard | MA | 29000 |
表1 College表中的數據測試
如上圖1所示,咱們把Apply表包含的學校信息查詢出來了,因爲Harvard並無被查詢出來,因此咱們知道暫時尚未學生申請Harvard。spa
內聯接(Inner join)知足交換律:「A inner join B」 和 「B inner join A」 是相等的。3d
假設咱們想看到全部學校信息;即便是那些沒有申請的學校(如:Harvard),這時咱們可使用外部聯接(Outer join)進行查詢。因爲外部聯接保存一個或兩個輸入表的全部行,即便沒法找到匹配聯接謂詞的行。
具體SQL代碼以下:
---- Gets all college information SELECT College.cName, College.state, College.enrollment, Apply.cName, Apply.major, Apply.decision FROM College LEFT OUTER JOIN Apply ON College.cName = Apply.cName
圖2左聯接查詢結果
如上圖3所示:因爲在Apply表中並無學生申請Harvard,可是咱們經過左聯接(left outer join)把全部學校信息查詢出來了。
因爲左聯接(left outer join)產生表College的徹底集,而Apply表中匹配的則有值,而不匹配的則以NULL值取代,因此咱們知道Apply表中沒有學生申請Harvard。
經過左聯接查詢咱們能夠獲取College的徹底集,假設如今咱們既要獲取College的徹底集又要獲取Apply的徹底集,那麼咱們能夠考慮使用完整外部聯接(full outer join)。使用完整外部聯接,咱們能夠查詢全部的學校,無論它們是否匹配聯接謂詞:
---- Gets all information from college and apply table. SELECT College.cName, College.state, College.enrollment, Apply.cName, Apply.major, Apply.decision FROM College FULL OUTER JOIN Apply ON College.cName = Apply.cName
圖3 完整外部聯接查詢結果
如今咱們獲取了College和Apply的徹底數據集,對於表中匹配的則有值,即便沒有找到匹配cName的則以NULL值取代。
下表顯示每種外部聯接(outer join)匹配時保留數據行的狀況:
聯接類型 |
保留數據行 |
A left outer join B |
all A rows |
A right outer join B |
all B rows |
A full outer join B |
all A and B rows |
表2 外部聯接保留數據行
完整外部聯接(full outer join)知足交換律:「A full outer join B」 和 「B full outer join A」 是相等的。
交叉聯接(cross join)執行兩個表的笛卡爾積(就是把表A和表B的數據進行一個N*M的組合)。也就是說,它匹配一個表與另外一個表中的每一行;咱們不能經過使用ON子句在交叉聯接指定謂詞,雖然咱們可使用WHERE子句來實現相同的結果,這是交叉聯接基本上是做爲一個內部聯接了。
交叉聯接相對於內部聯接使用率較低,並且兩個大表不該該進行交叉聯接,由於這將致使一個很是昂貴的操做和一個很是大的結果集。
具體SQL代碼以下:
---- College Cross join Apply. SELECT College.cName, College.state, College.enrollment, Apply.cName, Apply.major, Apply.decision FROM College CROSS JOIN Apply
圖4 College表和Apply表的行數
圖5 交叉聯接
如今咱們對College和Apply表進行交叉聯接,並且生成數據行爲College和Apply錶行數的笛卡爾積即5 * 20 = 100。
在SQL Server 2005中提供了Cross apply使表能夠和表值函數(table-valued functions TVF‘s)結果進行join查詢。例如,如今咱們想經過函數的結果值和表Student進行查詢,這時咱們可使用Cross apply進行查詢:
---- Creates a function to get data from Apply base on sID. CREATE FUNCTION dbo.fn_Apply(@sID int) RETURNS @Apply TABLE (cName nvarchar(50), major nvarchar(50)) AS BEGIN INSERT @Apply SELECT cName, major FROM Apply where [sID] = @sID RETURN END ---- Student cross apply function fn_Apply. SELECT Student.sName, Student.GPA, Student.sizeHS, cName, major FROM Student CROSS APPLY dbo.fn_Apply([sID])
咱們也可使用內部聯接實現和Cross apply相同的查詢功能,具體SQL代碼以下:
---- Student INNER JOIN Apply bases on sID. SELECT Student.sName, Student.GPA, Student.sizeHS, cName, major FROM Student INNER JOIN [Apply] ON Student.sID = [Apply].sID
圖6 Cross apply查詢
在介紹Cross apply和Outer join以後,如今讓咱們理解Out apply也就不難了,Outer apply使表能夠和表值函數(table-valued functions TVF‘s)結果進行join查詢,找到匹配值則有值,沒有找到匹配值則以NULL表示。
---- Student outer apply function fn_Apply. SELECT Student.sName, Student.GPA, Student.sizeHS, cName, major FROM Student OUTER APPLY dbo.fn_Apply([sID])
圖7 Outer apply查詢
首先咱們知道Inner join是表和表的聯接查詢,而Cross apply是表和表值函數的聯接查詢,在前面Cross apply例子中,咱們也能夠經過Inner join實現相同的查詢。
---- Student cross apply function fn_Apply. SET STATISTICS PROFILE ON SET STATISTICS TIME ON SELECT Student.sName, Student.GPA, Student.sizeHS, cName, major FROM Student CROSS APPLY dbo.fn_Apply([sID]) SET STATISTICS PROFILE OFF SET STATISTICS TIME OFF
---- Student INNER JOIN Apply base on sID. SET STATISTICS PROFILE ON SET STATISTICS TIME ON SELECT Student.sName, Student.GPA, Student.sizeHS, cName, major FROM Student INNER JOIN [Apply] ON Student.sID = [Apply].sID SET STATISTICS PROFILE OFF SET STATISTICS TIME OFF
Cross apply查詢執行時間:
CPU 時間= 0 毫秒,佔用時間= 11 毫秒。
Inner join查詢執行時間:
CPU 時間= 0 毫秒,佔用時間= 4 毫秒。
圖8 執行計劃
如圖8所示:Cross apply首先執行TVF(table-valued functions),而後對錶Studnet進行全表掃描,接着經過遍歷sID查找匹配值。
Inner join對錶Student和Apply進行全表掃描,而後經過哈希匹配查找匹配的sID值。
經過以上的SQL執行時間和執行計劃,咱們能不能說Inner join比Cross apply好呢?答案是否認的,若是表的數據量很大,那麼Inner join的全表掃描耗費時間和CPU資源就增長了(可經過數據量大的表進行測試)。
雖然大多數採用Cross apply實現的查詢,能夠經過Inner join實現,但Cross apply可能產生更好的執行計劃和更佳的性能,由於它能夠在聯接執行以前限制集合加入。
Semi-join從一個表中返回的行與另外一個表中數據行進行不徹底聯接查詢(查找到匹配的數據行就返回,再也不繼續查找)。
Anti-semi-join從一個表中返回的行與另外一個表中數據行進行不徹底聯接查詢,而後返回不匹配的數據。
不一樣於其餘的聯接運算,Semi-join和Anti-semi-join沒有明確的語法來實現,但Semi-join和Anti-semi-join在SQL Server中有多種應用場合。咱們可使用EXISTS子來實現Semi-join查詢,Not EXISTS來實現Anti-semi-join。如今讓咱們經過具體的例子說明吧!
假設要求咱們找出Apply和Student表中sID匹配的學生信息,這和前面的Inner join查詢結果將同樣,具體SQL代碼以下:
---- Student Semi-join Apply base on sID. SELECT Student.sName, Student.GPA, Student.sizeHS ----[Apply].cName, [Apply].major FROM Student WHERE exists ( SELECT * from [Apply] where [Apply].sID = Student.sID )
咱們發現經常使用的EXISTS子句,原來是經過Left Semi Join實現的,因此說Semi-join在SQL Server中又許多使用場合。
圖9 查詢結果
圖10 執行計劃
如今要求咱們找出尚未申請學校的學生信息,這時咱們馬上反應可使用NOT EXISTS子句來實現該查詢,具體SQL代碼以下:
---- Gets student still not apply for school. SELECT Student.sID, Student.sName, Student.GPA, Student.sizeHS ----[Apply].cName, [Apply].major FROM Student WHERE NOT EXISTS ( SELECT * FROM [Apply] WHERE [Apply].sID = Student.sID )
其實,咱們經常使用的NOT EXISTS子句的實現是經過Anti-semi-join,經過執行計劃咱們發如今查找匹配sID時,SQL使用 Left Anti Semi Join進行查詢。
圖11 查詢結果
圖12 執行計劃
本文介紹了SQL中經常使用了聯接查詢方式:Inner join、Outer join、Cross join和Cross apply的使用場合和特性。
系列博文導航
參考
SQL代碼