在上篇博文中介紹了T-SQL查詢的基礎知識,本篇主要介紹稍微復雜的查詢形式。
表運算符
表運算符的作用是把為其提供的表作為輸入,經過邏輯查詢處理,返回一個表結果。SQL Server支持四個表運算符:JOIN、APPLY、PIVOT、UNPIVOT,其中JOIN是標准SQL中的運算符,APPLY、PIVOT和UNPIVOT是T-SQL的擴展。
JOIN:聯接查詢時使用
APPLY:用於FROM子句中,分為CROSS APPLY
和OUTER APPLY
兩種形式
PIVOT:用於行轉列
UNPIVOT:用於列傳行
聯接查詢
聯接查詢分為外聯接、內聯接、交叉聯接,三者的區別在於如何應用邏輯查詢處理階段:
- 交叉聯接僅應用一個階段——笛卡爾乘積;
- 內聯接應用兩個階段——笛卡爾乘積和基於謂詞
ON
的篩選; - 外聯結應用三個極端——笛卡爾乘積,基於謂詞
ON
的篩選,添加外部行;
內部行 & 外部行
內部行指的是基於謂詞ON與另一側匹配的行,外部行則是未匹配的行,外部行用NULL進行填充。內聯接結果集僅保留內部行,外聯接結果集返回內部行和外部行。
笛卡爾乘積
將一個輸入表的每一行與另一個表的所有行匹配,即,如果一張表有m行a列,另一張表n行b列,笛卡爾乘積后得到的表有mn行,a+b列*。由此可以看出,對於數據量較大的表進行關聯的話,會得到一張數據量更大的表,會有可能造成內存溢出的。
以下是網絡上關於笛卡爾乘積的解釋:
在數學中,兩個集合X和Y的笛卡兒積(Cartesian product),又稱直積,表示為X × Y,第一個對象是X的成員而第二個對象是Y的所有可能有序對的其中一個成員。假設集合A=a, b,集合B=0, 1, 2,則兩個集合的笛卡爾積為(a, 0), (a, 1), (a, 2), (b, 0), (b, 1), (b, 2)。類似的例子有,如果A表示某學校學生的集合,B表示該學校所有課程的集合,則A與B的笛卡爾積表示所有可能的選課情況。A表示所有聲母的集合,B表示所有韻母的集合,那么A和B的笛卡爾積就為所有可能的漢字全拼。
舉例如下:
USE WJChi; SELECT * FROM dbo.UserInfo; SELECT * FROM dbo.UAddress; SELECT * FROM dbo.UserInfo CROSS JOIN dbo.UAddress;
得到結果集如下:

交叉聯接
SQL中使用CROSS JOIN
語句進行交叉聯接查詢,在邏輯處理上,交叉聯接是最為簡單的聯接類型,它只獲取表的笛卡爾乘積。
交叉聯接兩種寫法:
USE WJChi; -- 使用CROSS JOIN,推薦使用這種方式 SELECT * FROM dbo.UserInfo CROSS JOIN dbo.UAddress; -- 不使用CROSS JOIN SELECT * FROM dbo.UserInfo,dbo.UAddress;
內聯接
SQL中使用INNER JOIN...ON...
語句進行內聯接查詢,INNER
關鍵字可選。內聯接的邏輯處理分為兩步:
- 生成笛卡爾乘積
- 根據謂詞
ON
對笛卡爾乘積進行篩選
與交叉聯接一樣,內聯接有兩種寫法:
USE WJChi; -- 使用JOIN,推薦使用這種方式 SELECT * FROM dbo.UAddress JOIN dbo.UserInfo ON UserInfo.UAddressId = UAddress.Id; -- 不使用JOIN,與交叉聯接類似,但比交叉聯接多了WHERE條件 SELECT * FROM dbo.UAddress,dbo.UserInfo WHERE UserInfo.UAddressId = UAddress.Id;
外聯接
外聯接分為左外聯接:LEFT OUT JOIN
、右外聯接:RIGHT OUT JOIN
和全聯接:FULL OUT JOIN
,其中,OUT
關鍵字是可選的。相比於交叉聯接和內聯接,外聯接則最為復雜。外聯接邏輯處理分為三步:
- 獲取表的笛卡爾乘積
- 根據謂詞
ON
對笛卡爾乘積進行篩選 - 添加外部行數據到結果集中
LEFT JOIN & RIGHT JOIN
LEFT JOIN
獲取的結果集中保留了左表(LEFT JOIN左側的表)中的所有數據,及右表中滿足篩選條件的數據。右表中不滿足篩選條件的空行(外部行)則用NULL值填充。
RIGHT JOIN
與LEFT JOIN
作用相反。
示例代碼如下,表UserInfo中有4條數據,表UAddress中有三條數據:
USE WJChi; SELECT * FROM dbo.UAddress LEFT JOIN dbo.UserInfo ON UserInfo.UAddressId = UAddress.Id; SELECT * FROM dbo.UAddress RIGHT JOIN dbo.UserInfo ON UserInfo.UAddressId = UAddress.Id;
查詢結果如下:

FULL JOIN
FULL JOIN
的結果是取LEFT JOIN
和RIGHT JOIN
查詢結果集的並集
USE WJChi; SELECT * FROM dbo.UAddress FULL JOIN dbo.UserInfo ON UserInfo.UAddressId = UAddress.Id;
查詢結果如下:

ON & WHERE
前面說到:內聯接結果集僅保留內部行,外聯接結果集返回內部行和外部行。換句話說,外聯接中ON
子句的作用是進行表之間關聯,如果外聯接需要對結果集做進一步的篩選的話不能使用ON...AND...
語句,而要使用WHERE
條件。示例如下:
USE WJChi; -- 內聯接使用ON...AND...篩選數據 SELECT * FROM dbo.UserInfo AS UI JOIN dbo.UAddress AS UA ON UA.Id = UI.UAddressId -- 獲取Name為xfh的數據 AND UI.Name='xfh'; -- 外聯接使用ON...AND...篩選數據 SELECT * FROM dbo.UserInfo AS UI LEFT JOIN dbo.UAddress AS UA ON UA.Id = UI.UAddressId -- 獲取Name為xfh的數據,無效 AND UI.Name='xfh'; -- 外聯接使用WHERE對結果集進行篩選 SELECT * FROM dbo.UserInfo AS UI LEFT JOIN dbo.UAddress AS UA ON UA.Id = UI.UAddressId WHERE UI.Name='xfh';
輸出結果如下:

復合聯接
復合聯接是指謂詞涉及表中多個字段的聯接,即,關聯條件使用ON...AND...
的形式。
自聯接
同一張表的多個實例之間相互聯接,稱為自聯接。所有基本聯接類型(內聯接、外聯接、交叉聯接)支持。
USE WJChi; SELECT * FROM dbo.UserInfo AS U1 CROSS JOIN dbo.UserInfo AS U2;
自聯接中要為表名指定別名,否則結果集中的列名都將不明確。
相等聯接 & 不等聯接
當聯接條件使用相等運算符時稱為相等聯接,否則稱為不等聯接:
USE WJChi; -- 相等聯接 SELECT * FROM dbo.UAddress FULL JOIN dbo.UserInfo ON UserInfo.UAddressId = UAddress.Id; -- 不等聯接 SELECT * FROM dbo.UAddress FULL JOIN dbo.UserInfo ON UserInfo.UAddressId <> UAddress.Id;
多聯接查詢
超過兩張表進行關聯查詢即為多聯接查詢。通常,當SQL中出現多個表運算符時,從左到右進行邏輯處理,前一個聯接的結果會作為下一個聯接的左側輸入。SQL Server也常常出於優化查詢的目的,在實際處理查詢過程中對聯接進行重新排序,但這不會影響到處理結果集的正確性。
不建議超過三張表進行關聯,過多的表關聯會使SQL變得復雜,難以維護且影響性能
小結
過多的表聯接會讓SQL邏輯變得復雜,對查詢性能產生負面影響,且難以維護。
SQL(任何代碼)的書寫應將語義清晰作為第一追求,而不是為了“炫技”寫一些別人難以理解的代碼。
StackOverflow中扣出的一張圖片,可以概述外聯接和內聯接查詢:

推薦閱讀
What is the difference between “INNER JOIN” and “OUTER JOIN”?