【译注:此文为翻译,由于本人水平所限,疏漏在所难免,欢迎探讨指正】

原文链接:传送门

性能考虑
产生了笛卡尔积的这个CROSS JOIN操作符具有一些性能方面的问题需要考虑。因为SQL引擎需要将一个数据集的每一行与另一个数据集的每一行进行关联,其结果集合将会非常巨大。如果我将一个具有 1,000,000行数据的表与另一张具有 1,000,000行数据的表进行CROSS JOIN,那么我的结果集将会包含 1,000,000* 1,000,000行数据,也就是100,000,000,000行数据。这是一个非常巨大结果集合并且它将占用SQL SERVER大量的时间来创建它。

CROSS JOIN操作符是一个其用来识别两个集合之间所有可能的组合的很好的解决方案,比如说是每个客户每个月所有的销售订单,甚至于在某几个月某些客户没有销售单。当使用CROSS JOIN操作符的时候,如果你想要优化性能,你应该试着最小化用来CROSS JOIN的集合的行数。举个例子,假设我有一个表包含了最近两个月的销售单,如果我要展示一个报告展示一个月内没有销售单的客户,那么识别一个月的日期数的方式可以改变我的查询的性能。为了演示这个我首先创建一个包含1000个客户在两个月之间的销售单集合,我会用列表7的代码来做这件事:

CREATE TABLE Cust (Id int, CustName varchar(20));
CREATE TABLE Sales (Id int identity
,CustID int
,SaleDate date
,SalesAmt money);
SET NOCOUNT ON;
DECLARE @I int = 0;
DECLARE @Date date;
WHILE @I < 1000
BEGIN
SET @I = @I + 1;
SET @Date = DATEADD(mm, -2, '2014-11-01');
INSERT INTO Cust
VALUES (@I,
'Customer #' + right(cast(@I+100000 as varchar(6)),5));
WHILE @Date < '2014-11-01'
BEGIN
IF @I%7 > 0
INSERT INTO Sales (CustID, SaleDate, SalesAmt)
VALUES (@I, @Date, 10.00);
SET @Date = DATEADD(DD, 1, @Date);
END
END

列表7:用来创建性能测试数据示例的TSQL

列表7的代码为1000个不同的客户创建了2个月的数据。这段代码为每第七个用户不插入销售记录。这段代码为Cust表插入了1000条记录,并且为Sales 表插入了52,338 条记录。

为了演示使用CROSS JOIN操作符依赖于输入数据集大小会有如何的不同,让我们运行列表8和列表9的代码,对于每一个测试我会记录下它们返回结果所需要的时间。

SELECT CONVERT(CHAR(6),S1.SaleDate,112) AS SalesMonth, C.CustName,
ISNULL(SUM(S2.SalesAmt),0) AS TotalSales
FROM Cust C
CROSS JOIN
(
SELECT SaleDate FROM Sales
) AS S1
LEFT OUTER JOIN
Sales S2
ON C.ID = S2.CustID
AND S1.SaleDate = S2.SaleDate
GROUP BY CONVERT(CHAR(6),S1.SaleDate,112),C.CustName
HAVING ISNULL(SUM(S2.SalesAmt),0) = 0
ORDER BY CONVERT(CHAR(6),S1.SaleDate,112),C.CustName

列表8:所有记录的CROSS JOIN

SELECT CONVERT(CHAR(),S1.SaleDate,) AS SalesMonth, C.CustName,
ISNULL(SUM(S2.SalesAmt),) AS TotalSales
FROM Cust C
CROSS JOIN
(
SELECT DISTINCT SaleDate FROM Sales
) AS S1
LEFT OUTER JOIN
Sales S2
ON C.ID = S2.CustID
AND S1.SaleDate = S2.SaleDate
GROUP BY CONVERT(CHAR(),S1.SaleDate,),C.CustName
HAVING ISNULL(SUM(S2.SalesAmt),) =
ORDER BY CONVERT(CHAR(),S1.SaleDate,),C.CustName

列表9:去重销售日期记录的CROSS JOIN

在列表8中,CROSS JOIN操作符将1000条Cust表记录和52,338条Sales表记录进行关联用来产生52,338,000行的数据集合,这些记录随后用来决定在一个月中具有0条销售记录的客户。在列表9中,我改变了查询条件,从Sales表中仅仅返回SalesDate值的去重集合。这个去重的集合仅仅产生了61条不同的SalesDate值,因此列表9的CROSS JOIN操作符仅仅产生了61,000条记录。通过减少CROSS JOIN操作符的结果集,我的列表9的查询运行了少于1秒的时间,同时列表8的代码在我的机器上运行了19秒。这些性能差异的主要原因是SQL SERVER对每个查询的每个操作需要处理的记录数的不同。如果你查看两个列表的执行计划,你将会看到两个计划有稍微的不同,但是如果你查看从嵌套循环(INNER JOIN)生成的预估记录数,在图形化计划的左边,你将会看到列表8预估了52,338,000条记录,同时列表9的相同的操作符预估了61,000条记录,列表的查询计划从CROSS JOIN嵌套循环生成的这么巨大的数据集随后被传递给额外的几个操作。正是因为列表8的这些操作符都需要处理52,000,000条数据,列表8当然会比列表9慢很多。

正如你所见到的,使用在CROSS JOIN操作符中的记录数会显著的影响一个计划的运行时间,因此如果你能够写你的查询来最小化涉及到CROSS JOIN操作符的记录数,你的查询便会更高效。

结论

CROSS JOIN操作符在两个数据集之间产生笛卡尔积。这个操作符有助于我们识别在一个表中而在另一个表中没有匹配记录的数据项。我们应该注意最小化涉及到CROSS JOIN操作符的数据集的大小,通过确保CROSS JOIN操作符的结果集尽可能的小,你便可以确保你的代码运行得尽可能的快。

问题和答案

在这一部分你可以通过回答下面的问题来查看你有多么的理解CROSS JOIN操作符。

问题1:CROSS JOIN操作符基于在ON子句中指定的列进行匹配来产生结果集?

问题2:下面哪一个公式可以用来定义表A和表B CROSS JOIN 返回的行数,其中A表和B表包含重复的行?

  • 表A的行数*表B的行数
  • 表A的行数*表B的唯一的行数
  • 表A的唯一行数*表B的行数
  • 表A的唯一行数*表B的唯一行数

问题3:哪一项措施提供了最好的机会可以用来减少CROSS JOIN产生的笛卡尔积的大小?

  • 确保关联的两个数据集有尽可能多的行
  • 确保关联的两个数据集有尽可能的少的行
  • 确保CROSS JOIN操作符的左边集合具有尽可能少的行
  • 确保CROSS JOIN操作符右边集合具有尽可能少的行

答案解析

问题1:正确答案是b:CROSS JOIN操作符不会使用ONziju 来进行CROSS JOIN,它将一个表的每一行与另一个表的每一行进行关联,从而产生两个的笛卡尔积。

问题2:正确答案是a。因为表A和表B如果有重复的行,当为CROSS JOIN操作符创建笛卡尔积时候每一行都会被关联。

问题3:通过减少CROSS JOIN操作符涉及的两个数据集的大小来最小化CROSS JOIN操作符最终创建的结果集的大小。c,d也可以帮助减少由CROSS JOIN操作符创建的结果集的大小但并不如确保CROSS JOIN操作符涉及的俩个表都包含最少的行数,其更加优化。

【完结】

高级T-SQL进阶系列 (一)【下篇】:使用 CROSS JOIN 介绍高级T-SQL的更多相关文章

  1. 高级T-SQL进阶系列 (一)【上篇】:使用 CROSS JOIN 介绍高级T-SQL

    [译注:此文为翻译,由于本人水平所限,疏漏在所难免,欢迎探讨指正] 原文连接:传送门 这是一个新进阶系列的第一篇文章,我们将浏览Transact-SQL(T-SQL)的更多高级特性.这个进阶系列将会包 ...

  2. 高级T-SQL进阶系列 (一)【中篇】:使用 CROSS JOIN 介绍高级T-SQL

    [译注:此文为翻译,由于本人水平所限,疏漏在所难免,欢迎探讨指正] 原文连接:传送门. 当一个CROSS JOIN 表现得如同一个INNER JOIN 在上一章节我提到当你使用一个CROSS JOIN ...

  3. Linq To Sql进阶系列(六)用object的动态查询与保存log篇

    动态的生成sql语句,根据不同的条件构造不同的where字句,是拼接sql 字符串的好处.而Linq的推出,是为了弥补编程中的 Data != Object 的问题.我们又该如何实现用object的动 ...

  4. SQL进阶系列之7用SQL进行集合运算

    写在前面 集合论是SQL语言的根基,因为这种特性,SQL也被称为面向集合语言 导入篇:集合运算的几个注意事项 注意事项1:SQL能操作具有重复行的集合(multiset.bag),可以通过可选项ALL ...

  5. SQL进阶系列之9用SQL处理数列

    写在前面 关系模型的数据结构里,并没有顺序的概念,但SQL处理有序集合也有坚实的理论基础 生成连续编号 --生成连续编号 CREATE TABLE Digits (digit INTEGER PRIM ...

  6. SQL进阶系列之8EXISTS谓词的用法

    写在前面 支撑SQL和关系数据库的基础理论:数学领域的集合论和逻辑学标准体系的谓词逻辑 理论篇 什么是谓词?谓词是返回值为真值(true false unknown)的函数 关系数据库里,每一个行数据 ...

  7. SQL进阶系列之5外连接的用法

    写在前面 SQL本身是作为一种数据提取工具而出现,使用SQL生成各种定制化报表和非定制化报表并非SQL原本用途的功能,但这并不意味着SQL无法实现这些功能. 用外连接进行行列转换(1)(行 → 列): ...

  8. SQL进阶系列之12SQL编程方法

    写在前面 KISS -- keep it sweet and simple 表的设计 注意命名的意义 英文字母 + 阿拉伯数字 + 下划线"_" 属性和列 编程的方针 写注释 注意 ...

  9. SQL进阶系列之10HAVING子句又回来了

    写在前面 HAVING子句的处理对象是集合而不是记录 各队,全队点名 --各队,全体点名! CREATE TABLE Teams (member CHAR(12) NOT NULL PRIMARY K ...

随机推荐

  1. Git-配置SSH公钥

    前言:Git是分布式的代码管理工具,远程的代码管理是基于SSH的,所以要使用远程的Git则需要SSH的配置. 以下操作都在git-bash命令行中进行. 查看所有配置项: git config --l ...

  2. vs 安装svn插件

    在很多互联网开发的团队里面,用到的代码管理器都是SVN,svn目前有客户端和集成到VS里面两种(不清楚分类是否正确).客户端的在这里我就不写了,我目前用到比较多的都是集成到VS里面的,而且目前用着还是 ...

  3. AVR单片机教程——DAC

    本文隶属于AVR单片机教程系列.   单片机的应用场景时常涉及到模拟信号.我们已经会使用ADC把模拟信号转换成数字信号,本讲中我们要学习使用DAC把数字信号转换成模拟信号.我们还将搭建一个简单的功率放 ...

  4. MyBatis(8)——联表多对一的处理

    xml说明: <!--column不做限制,可以为任意表的字段,而property须为type 定义的pojo属性--> <resultMap id="唯一的标识" ...

  5. 虚拟机安装的ubuntu不能联网解决

    安装双系统从没遇到的问题,再虚拟机上遇到了不能联网的问题: 下面给出我的解决方法(win10系统.ubuntu 16.04) 我的电脑-管理-设备管理器 看是否虚拟机的虚拟网卡在: 在去设置-控制面板 ...

  6. 7_1 除法(UVa725)<选择合适的枚举对象>

    如果把数字0到9分配成2个整数(各五位数),现在请你写一支程序找出所有的配对使得第一个数可以整除第二个数,而且商为N(2<=N<=79),也就是:abcde / fghijk = N这里每 ...

  7. gVim for windows 简单使用教程

    vim 是一个具有很多命令的功能非常强大的编辑器.限于篇幅,在本教程当中    就不详细介绍了.本教程的设计目标是讲述一些必要的基本命令,而掌握好这    些命令,您就能够很容易将vim当作一个通用的 ...

  8. 两台linux之间传输文件

    scp传输 当两台LINUX主机之间要互传文件时可使用SCP命令来实现 scp传输速度较慢,但使用ssh通道保证了传输的安全性 复制文件 将本地文件拷贝到远程 scp local_file remot ...

  9. Java进阶学习(4)之继承与多态(上)

    继承 媒体资料库的设计 代码复制是质量不良的表现 不具有可扩展性和可维护性 继承 子类父类关系 子类继承了什么 先定义初始化,后构造器 子类和父类的关系 子类有变量和父类变量相同时,父类变量隐藏 父类 ...

  10. zxEditor

    <!DOCTYPE html> <html lang="en"> <head> <meta http-equiv="X-UA-C ...