高级T-SQL进阶系列 (一)【上篇】:使用 CROSS JOIN 介绍高级T-SQL
【译注:此文为翻译,由于本人水平所限,疏漏在所难免,欢迎探讨指正】
原文连接:传送门
这是一个新进阶系列的第一篇文章,我们将浏览Transact-SQL(T-SQL)的更多高级特性。这个进阶系列将会包含一系列的文章,它们会扩展你在之前的两个TSQL进阶系列所掌握的TSQL的基础。
本系列“高级Transact-SQL”将会包含如下T-SQL主题:
- 使用CROSS JOIN 操作符
- 使用APPLY操作符
- 理解通用表表达式(CTE's)
- 使用TSQL游标的记录级别处理
- 使用UNPIVOT实现列转行
- 使用排序函数对数据进行排序
- 使用相关函数来操作日期和时间
- 理解OVER子句的变量
本进阶的读者应该已经很好的理解了如何从数据库表进行查询,更改,插入,删除数据。此外他们应该具有实际的工作经验用以控制TSQL代码的执行流,以及能够测试和操作数据。
本进阶应该可以帮助读者准备通过微软认证考试70-461:查询SQL SERVER 2012.
对于本进阶系列的第一部分,我们将讨论一下CROSS JOIN操作符。
介绍CROSS JOIN 操作符
CROSS JOIN操作符可以用来将一个数据集的所有记录和另一个数据集的所有数据进行比较。通过在两个数据集之间使用CROSS JOIN操作符,你便创建了成为笛卡尔积的东西。
这儿有一个简单的示例,其使用CROSS JOIN操作符来连接两张表A和B:
SELECT * FROM A CROSS JOIN B
注意当使用CROSS JOIN操作符时,没有像你使用INNER,OUTER JOIN 那样,有一个JOIN子句来连接两张表。
你应该意识到使用CROSS JOIN 操作符会产生很大的数据集合。为了浏览这种行为让我们看看两个不同的示例,看看从一个CROSS JOIN产生的数据集会多么的巨大。对于第一个示例我们假设你会交叉连接两个表,其中表A具有10行而表B具有3行,那么结果集合便会具有10*3行或者说是30行。对于第二个示例我们假定表A具有1000万条数据而表B具有300万条数据。那么表A和表B的CROSS JOIN 产生的结果集会有多少条数据呢?那会产生高达30,000,000,000,000条数据。那是许多行并且它会占用SQL SERVER大量的时间和资源来创建结果集。因此当在大的记录集合上使用CROSS JOIN操作符时,你需要特别小心。
让我们通过几个示例来更近一点查看CROSS JOIN 操作符。
使用CROSS JOIN操作符的基本示例
对于第一组示例我们将连接两个示例表。列表1的代码用来创建这两个示例表。确保你在一个用户数据库上执行此脚本而不是在master库上。
CREATE TABLE Product (ID int, ProductName varchar(100), Cost money); CREATE TABLE SalesItem (ID int, SalesDate datetime, ProductID int, Qty int, TotalSalesAmt money); INSERT INTO Product VALUES (1,'Widget',21.99), (2,'Thingamajig',5.38), (3,'Watchamacallit',1.96); INSERT INTO SalesItem VALUES (1,'2014-10-1',1,1,21.99), (2,'2014-10-2',3,1,1.96), (3,'2014-10-3',3,10,19.60), (4,'2014-10-3',1,2,43.98), (5,'2014-10-3',1,2,43.98);
列表1 CROSS JOIN的测试表
对于第一个CROSS JOIN 示例,我们将运行列表2的代码。
SELECT * FROM Product CROSS JOIN SalesItem;
列表2:简单的CROSS JOIN示例
当我们在SSMS窗口运行列表2的代码时,在我的结果窗口我们得到了报告1的输出:
ID ProductName Cost ID SalesDate ProductID Qty TotalSalesAmt --- --------------------- -------- ---- ----------------------- --------- ---- --------------- 1 Widget 21.99 1 2014-10-01 00:00:00.000 1 1 21.99 1 Widget 21.99 2 2014-10-02 00:00:00.000 3 1 1.96 1 Widget 21.99 3 2014-10-03 00:00:00.000 3 10 19.60 1 Widget 21.99 4 2014-10-03 00:00:00.000 1 2 43.98 1 Widget 21.99 5 2014-10-03 00:00:00.000 1 2 43.98 2 Thingamajig 5.38 1 2014-10-01 00:00:00.000 1 1 21.99 2 Thingamajig 5.38 2 2014-10-02 00:00:00.000 3 1 1.96 2 Thingamajig 5.38 3 2014-10-03 00:00:00.000 3 10 19.60 2 Thingamajig 5.38 4 2014-10-03 00:00:00.000 1 2 43.98 2 Thingamajig 5.38 5 2014-10-03 00:00:00.000 1 2 43.98 3 Watchamacallit 1.96 1 2014-10-01 00:00:00.000 1 1 21.99 3 Watchamacallit 1.96 2 2014-10-02 00:00:00.000 3 1 1.96 3 Watchamacallit 1.96 3 2014-10-03 00:00:00.000 3 10 19.60 3 Watchamacallit 1.96 4 2014-10-03 00:00:00.000 1 2 43.98 3 Watchamacallit 1.96 5 2014-10-03 00:00:00.000 1 2 43.98
报告1:当运行列表2的结果
如果你查看报告1的结果你将会发现会有15条不同的记录。前5条记录包含了列值,是从Product表的第一行与SalesItem 表的5条不同的记录进行连接。同样对于Product表的第二行与第三行也是如此。最终返回的总行数是Product表的行数乘以SalesItem 表的行数,也就是15。
为什么产生笛卡尔积是有用的一个原因或许是为了产生测试数据。假设我想要使用Product表和SalesItem表的数据产生一些测试数据,我便可以使用CROSS JOIN来做这个,如同我在列表3中所做的:
SELECT ROW_NUMBER() OVER(ORDER BY ProductName DESC) AS ID, Product.ProductName + CAST(SalesItem.ID as varchar(2)) AS ProductName, (Product.Cost / SalesItem.ID) * 100 AS Cost FROM Product CROSS JOIN SalesItem;
列表3:简单的CROSS JOIN示例
当我运行列表3的代码我得到报告2的输出:
ID ProductName Cost ----- ----------------------------------------------------------- --------------------- 1 Widget1 2199.00 2 Widget2 1099.50 3 Widget3 733.00 4 Widget4 549.75 5 Widget5 439.80 6 Watchamacallit1 196.00 7 Watchamacallit2 98.00 8 Watchamacallit3 65.33 9 Watchamacallit4 49.00 10 Watchamacallit5 39.20 11 Thingamajig1 538.00 12 Thingamajig2 269.00 13 Thingamajig3 179.33 14 Thingamajig4 134.50 15 Thingamajig5 107.60
报告2:当运行列表3的结果
如你所见通过查看列表3的代码我生成了许多数据行,它们包含了和Product表的数据相似的数据。通过使用ROW_NUMBER函数我们能够在每一行生成唯一列。另外我使用SalesItem 表的ID列来创建ProductName,Cost 列的值。产生的行数等于Product 表的行数乘以SalesItem 表的行数。
到现在为止本节的示例仅仅展示了两个表之间的CROSS JOIN。你也可以使用CROSS JOIN操作符来进行多个表的CROSS JOIN。列表4的示例展示了三个表的笛卡尔积。
SELECT * FROM sys.tables CROSS JOIN sys.objects CROSS JOIN sys.sysusers;
列表4:使用CROSS JOIN创建三个表的笛卡尔积
从运行列表4的输出具有两个不同的CROSS JOIN操作符。从这代码创建的笛卡尔积将会产生一个结果集,它的总行数等于sys.tables表的行数乘以sys.objects表的行数再乘以sys.sysusers表的行数。