表結構與數據:https://github.com/XuePeng87/TSQLV4git
邏輯上,交叉聯接是最簡單的聯接類型。交叉聯接僅執行一個邏輯查詢處理階段的笛卡兒乘積。好比說,一個表有m行,另外一個表有n行,獲得的結果中會有m*n行。程序員
下面,對Customers和Employees表應用了交叉聯接,而且在結果集中返回custid和empid屬性,Customers表中有91行,Employees表中有9行,結果會有819行:github
SELECT C.custid, e.empid FROM Sales.Customers AS C CROSS JOIN HR.Employees AS E;
能夠聯接同一個表的多個實例,此功能稱爲自聯接,例如,下面代碼在Employees表的兩個實例間執行自聯接:sql
SELECT E1.empid, E1.firstname, E1.lastname, E2.empid, E2.firstname, E2.lastname FROM HR.Employees AS E1 CROSS JOIN HR.Employees AS E2;
交叉聯接有一種用途是生成正數數列(一、二、3,以此類推)結果集,例如,先建立一個數字表:ui
IF OBJECT_ID('dbo.Digits', 'U') IS NOT NULL DROP TABLE dbo.Digits; CREATE TABLE dbo.Digits(digit INT NOT NULL PRIMARY KEY); INSERT INTO dbo.Digits(digit) VALUES (0), (1), (2), (3), (4), (5), (6), (7), (8), (9); SELECT digit FROM dbo.Digits;
編寫一個查詢,生成範圍1~1000的整數序列:spa
SELECT D3.digit * 100 + D2.digit * 10 + D1.digit + 1 AS n FROM dbo.Digits AS D1 CROSS JOIN dbo.Digits AS D2 CROSS JOIN dbo.Digits AS D3 ORDER BY n;
使用內部聯接須要在表名之間指定INNER JOIN關鍵字。因爲內部聯接是默認聯接,因此INNER關鍵字是可選的,能夠僅指定JOIN關鍵字。例如:下面查詢Employees和Orders表執行一個內部聯接,僱員和訂單匹配基於謂詞E.empid = O.empid:code
SELECT E.empid, E.firstname, E.lastname, O.orderid FROM HR.Employees AS E JOIN Sales.Orders AS O ON E.empid = O.empid;
複合聯接是謂詞涉及每側多個屬性的簡單聯接。當須要聯接兩個基於主外鍵關係而且是複合關係(基於多個屬性)的表時,一般須要複合聯接。例如:get
FROM dbo.Table1 AS T1 JOIN dbo.Table2 AS T2 ON T1.col1 = T2.col1 AND T1.col2 = T2.col2
當聯接條件僅涉及等號運算符時,聯接成爲相等聯接。當涉及等號以外的任何運算符時,聯接成爲不等聯接(Non-equi join),例如:qt
SELECT E1.empid, E1.firstname, E1.lastname, E2.empid, E2.firstname, E2.lastname FROM HR.Employees AS E1 JOIN HR.Employees AS E2 ON E1.empid < E2.empid
單個查詢中能夠有多聯接。也就是說,第一個表的結果被視爲第二個表的左側輸入,第二個表的結果被視爲第三個表的左側輸入,例如:it
SELECT C.custid, C.companyname, O.orderid, OD.productid, OD.qty FROM Sales.Customers AS C JOIN Sales.Orders AS O ON C.custid = O.custid JOIN Sales.OrderDetails OD ON O.orderid = OD.orderid
外部聯接須要在表名之間加入關鍵字LEFT OUTER JOIN、RIGHT OUTER JOIN或FULL OUTER JOIN。其中OUTER關鍵字是可選的。LEFT表示保留左表中的行,RIGHT表示保留右表中的行,FULL表示左右兩側的行都保留。對於聯接非保留側的屬性將使用NULL作佔位。例如:
SELECT C.custid, C.companyname, O.orderid FROM Sales.Customers AS C LEFT OUTER JOIN Sales.Orders AS O ON C.custid = O.custid;
外部聯接基本應用之一是包含缺失值,查詢Orders表的全部訂單,要求2012年1月1日至2014年12月31日中天天至少有一行輸出,原理是使用數字輔助表聯接Orders表,具體以下:
SELECT DATEADD(day, n-1, '20120101') AS orderdate, O.orderid, O.custid, O.empid FROM dbo.Nums LEFT OUTER JOIN Sales.Orders AS O ON DATEADD(day, Nums.n - 1, '20120101') = O.orderdate WHERE n <= DATEDIFF(day, '20120101', '20141231') + 1 ORDER BY orderdate;
注意,WHERE子句會過濾掉UNKNOWN值,致使全部外部行被過濾掉,事實上是抵消了外部聯接。好比:
SELECT C.custid, C.companyname, O.orderid, O.orderdate FROM Sales.Customers AS C LEFT OUTER JOIN Sales.Orders AS O ON C.custid = O.custid WHERE O.orderdate >= '20140101'
上面的代碼中,若是不執行WHERE條件,會把外部行顯示出來,並用NULL佔位,若是執行了WHERE條件,對全部外部行爲NULL的計算,結果爲UNKNOWN,會被篩選掉。
這意味着使用外部連接在這裏是徒勞的,程序員反了使用外部聯接或是WHERE謂詞的錯誤。
另外,在外部鏈接中的表,不能隨意的排列它們。假設編寫了一個多聯接查詢,在兩表之間使用了外部聯接,後接一個內部聯接第三張表。若是內部聯接子句中的謂詞對外部聯接非保留屬性和來自第三張表的屬性進行比較,全部外部行都會被過濾掉,例如:
SELECT C.custid, O.orderid, OD.productid, OD.qty FROM Sales.Customers AS C LEFT OUTER JOIN Sales.Orders AS O ON C.custid = O.custid JOIN Sales.OrderDetails AS OD ON O.orderid = OD.orderid
第一個聯接是外部聯接,返回客戶及其訂單,以及沒有任何訂單的客戶。表示客戶沒有訂單的外部行在訂單屬性中具備NULL標記。第二個聯接基於謂詞O.orderid = OD.orderid匹配OrderDetails表的訂單行和第一個聯接結果的行,而後,在表示客戶沒有訂單的行中,O.orderid屬性爲NULL。所以,謂詞計算爲UNKNOWN,這些航會被過濾掉。
若是但願在輸出中返回沒有訂單的客戶,有集中繞過次問題的方法。一種是繼續使用左外聯接:
SELECT C.custid, O.orderid, OD.productid, OD.qty FROM Sales.Customers AS C LEFT OUTER JOIN Sales.Orders AS O ON C.custid = O.custid LEFT OUTER JOIN Sales.OrderDetails AS OD ON O.orderid = OD.orderid
或者先使用內部聯接,而後使用一個右外部聯接:
SELECT C.custid, O.orderid, OD.productid, OD.qty FROM Sales.Orders AS O JOIN Sales.OrderDetails AS OD ON O.orderid = OD.orderid RIGHT OUTER JOIN Sales.Customers AS C ON O.custid = C.custid;
最後,還可使用括號將Orders和OrderDetails間的內部聯接變成一個獨立的邏輯階段,在使用一個作外部聯接查詢:
SELECT C.custid, O.orderid, OD.productid, OD.qty FROM Sales.Customers AS C LEFT OUTER JOIN (Sales.Orders AS O JOIN Sales.OrderDetails AS OD ON O.orderid = OD.orderid) ON C.custid = O.custid;
在外部聯接中使用COUNT,當對外部聯接結果進行分組並使用COUNT(*)時,聚合會考慮內部行和外部行,一般,不該該將外部行做爲計數目標。可是COUNT(*)會將NULL也算做1行,咱們須要將*替換成內部航的列來解決該問題:
SELECT C.custid, COUNT(O.orderid) AS numorders FROM Sales.Customers AS C LEFT OUTER JOIN Sales.Orders AS O ON C.custid = O.custid GROUP BY C.custid