表结构与数据: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