1. 程式人生 > >T-SQL基礎(二)之關聯查詢

T-SQL基礎(二)之關聯查詢

在上篇博文中介紹了T-SQL查詢的基礎知識,本篇主要介紹稍微複雜的查詢形式。

表運算子

表運算子的作用是把為其提供的表作為輸入,經過邏輯查詢處理,返回一個表結果。SQL Server支援四個表運算子:JOIN、APPLY、PIVOT、UNPIVOT,其中JOIN是標準SQL中的運算子,APPLY、PIVOT和UNPIVOT是T-SQL的擴充套件。

JOIN:聯接查詢時使用

APPLY:用於FROM子句中,分為CROSS APPLYOUTER 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關鍵字是可選的。相比於交叉聯接和內聯接,外聯接則最為複雜。外聯接邏輯處理分為三步:

  1. 獲取表的笛卡爾乘積
  2. 根據謂詞ON對笛卡爾乘積進行篩選
  3. 新增外部行資料到結果集中
LEFT JOIN & RIGHT JOIN

LEFT JOIN獲取的結果集中保留了左表(LEFT JOIN左側的表)中的所有資料,及右表中滿足篩選條件的資料。右表中不滿足篩選條件的空行(外部行)則用NULL值填充。

RIGHT JOINLEFT 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 JOINRIGHT 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中扣出的一張圖片,可以概述外聯接和內聯接查詢:

推薦閱讀

T-SQL基礎(一)之簡單查詢

What is the difference between “INNER JOIN” and “OUTER JOIN”?