T-SQL基礎(二)之關聯查詢
在上篇博文中介紹了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列,笛卡爾乘積後得到的表有m*n行,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也常常出於優化查詢的目的,在實際處理查詢過程中對聯接進行重新排序,但這不會影響到處理結果集的正確性。
:warning:不建議超過三張表進行關聯,過多的表關聯會使SQL變得複雜,難以維護且影響效能
小結
過多的表聯接會讓SQL邏輯變得複雜,對查詢效能產生負面影響,且難以維護。
SQL(任何程式碼)的書寫應將語義清晰作為第一追求,而不是為了“炫技”寫一些別人難以理解的程式碼。
StackOverflow中扣出的一張圖片,可以概述外聯接和內聯接查詢:
推薦閱讀
What is the difference between “INNER JOIN” and “OUTER JOIN”?