清单5中的代码包含两个SELECT语句。第一个SELECT语句使用交叉连接操作符,然后使用WHERE子句定义如何连接到交叉连接操作中的两个表。第二个SELECT语句使用一个正常的内部连接操作符和一个ON子句来连接两个表。SQL Server的查询优化器足够聪明,可以知道清单5中的第一个SELECT语句可以重写为内部连接。优化器知道,当一个交叉连接操作与一个WHERE子句一起使用时,它可以重新编写查询。WHERE子句提供了包含在交叉连接中的两个表之间的连接谓词。因此,SQL Server引擎为清单5中的SELECT语句生成相同的执行计划。当您不提供一个约束SQL Server不知道如何连接两个包含交叉连接操作的表时,它将在与交叉连接操作关联的两个集合之间创建一个Cartesian产品。
使用交叉连接查找未售出的产品。
前面几节中的示例将帮助您理解交叉连接操作符以及如何使用它。使用交叉连接操作符的一个功能是使用它来帮助在另一个表中查找没有匹配记录的表。例如,假设我想要在我的产品表中每一个产品的每一个产品被出售的日期,在我的产品表中报告每一个产品名称的总数量和总销售额。因为在我的例子中,每一个产品的名字都不是每天都有销售的,我的报告要求是指我需要显示一个0的数量,以及在某一天没有销售的产品的总销售额为0。这是交叉连接操作符与左边的外部连接操作一起,将帮助我识别那些没有被卖出一天的东西。满足这些报告需求的代码如清单6所示:清单6:查找不使用交叉连接销售的产品。
让我带你看看这段代码。我创建一个子查询来选择所有不同的SalesDate值。这个子查询给出了销售的所有日期。然后我将它与我的产品表连接起来。这允许我在每个SalesDate和每个产品行之间创建一个Cartesian产品。从交叉连接返回的集合将具有在最终结果集中所需要的所有值,除了每个产品的Qty和TotalSalesAmt之和。为了得到这些汇总值,我在SalesItem表上执行了一个左外连接,并将其与通过交叉连接操作创建的Cartesian产品连接起来。我基于ProductID和SalesDate列执行了这个连接。通过使用左外连接,我将返回笛卡尔产品中的每一行,如果有一个与ProductID和SalesDate相匹配的SalesDate记录,那么Qty和TotalSalesAmt值将与适当的行相关联。这个查询最后做的事情是使用GROUP BY子句来总结基于SalesDate和ProductName的Qty和TotalSalesAmount。
性能考虑
产生笛卡尔积的交叉连接运算符有一些性能方面需要考虑。因为SQL引擎需要将每一行与另一个集合中的每一行连接起来,因此结果集可以相当大。如果我做一个交叉连接,一个有1,000,000行的表,另一个表有100,000行,那么我的结果集将有1,000,000 X 100,000行,或100,000,000,000行。这是一个很大的结果集,它将花费大量的时间来创建它。
他交叉连接操作符可以是一个很好的解决方案,可以在所有可能的组合中确定一个结果集,就像每个月的所有客户的所有销售,即使在几个月的时间里一些客户没有销售。在使用交叉连接操作符时,如果希望优化性能,应该尽量减少交叉连接的大小。例如,假设我有一个表,其中包含了过去两个月的销售数据。如果我想要生成一个报告,显示一个月没有任何销售的客户,那么确定一个月的天数的方法可以极大地改变我的查询的性能。为了证明这一点,我首先为1000名顾客创造了一个为期两个月的销售记录。我将使用清单7中的代码来实现这一点。
清单7:TSQL为性能测试创建示例数据。
清单7中的代码为1000个不同的客户创建了两个月的数据。这段代码没有为每7个客户增加销售数据。此代码生成1000个Cust表记录和52338个Sales表记录。 为了演示如何使用交叉连接操作符执行不同的操作,这取决于在交叉连接输入集中使用的集合的大小,让我来运行清单8和清单9中的代码。对于每个测试,我将记录返回结果所需的时间。 清单8:与所有销售记录交叉连接。清单9:交叉连接到不同的销售日期列表。
在清单8中,CROSS JOIN操作符连接1000个Cust记录,其中有52,338个销售记录,生成一个记录集,记录为52,338,000个行,然后用来确定一个月销售为零的客户。在清单9中,我从Sales表中更改了选择标准,只返回一组不同的SalesDate值。这个不同的集合只产生了61个不同的SalesDate值,因此清单9中的CROSS JOIN操作的结果只产生了61,000条记录。通过减少交叉连接操作的结果集,清单9中的查询运行不到1秒,而清单8中的代码在我的机器上运行19秒。这种性能差异的主要原因是记录SQL Server需要为每个查询执行的不同操作的数量。如果您查看两个清单的执行计划,您将看到计划略有不同。但是,如果查看由嵌套循环(内连接)操作生成的记录数量,在图形化计划的右侧,您将看到清单8估计有52,338000条记录,而清单9中的相同操作只估计了61,000条记录。清单8的查询计划从交叉连接嵌套循环操作生成的这个大记录集,然后传递给多个额外的操作。因为清单8中的所有操作都必须处理5200万条记录。清单8比清单9慢得多。正如您所看到的,交叉连接操作中使用的记录数可以极大地影响查询运行的时间长度。因此,如果您可以编写查询来最小化交叉连接操作中涉及的记录的数量,那么您的查询将执行得更有效率。
总结
交叉连接运算符在两个记录集之间产生一个笛卡尔积。这个操作符有助于在另一个表中不具有匹配记录的表中识别项。应注意尽量减小与交叉连接操作符使用的记录集的大小。通过确保交叉连接的结果集尽可能小,您将确保代码尽可能快地运行。问答
问1: 交叉连接操作符根据on子句中指定的列匹配两个记录集,从而创建一个结果集。(真或假)? A. 真 B.假问2:
当表A和B包含重复行时,可以使用哪个公式来识别从两个表A和B之间不受约束的交叉连接返回的行数? A表A中的行数乘以表B中的行数。 B表A中的行数乘以表B中唯一行数。 C表A中唯一行数乘以表B中的行数。 D表A中唯一行数乘以表B中唯一行数。问3:
哪种方法提供了减少交叉连接操作产生的笛卡尔积的最大机会? A确保连接的两个集合有尽可能多的行。 B确保连接的两个集合尽可能少。 C确保在交叉联接操作的左侧设置的行数尽可能少。 D确保在交叉连接操作的右侧设置的行数越少越好。答案
1正确的答案是b。交叉连接操作符不使用ON子句来执行交叉连接操作。它将一个表中的每一行连接到另一个表中的每一行。当它连接两个集合时,交叉连接创建一个笛卡尔积。2正确的答案是A . b、c和d是不正确的,因为如果表A或b中有重复的行,那么在创建交叉连接操作的Cartesian产品时,将会连接到每个重复的行。
3正确的答案是b。通过减少交叉连接操作中所涉及的两个集合的大小,可以最小化由CROSS JOI操作创建的最终集的大小。c和d还有助于减小交叉连接操作所创建的最终集的大小,但不像确保交叉连接操作中涉及的两个集合具有最少的行数一样最优。