[翻译]通往t - sql的阶梯:超越基本级别2:编写子查询
通往t - sql的阶梯:超越基本级别2:编写子查询
格雷戈里·拉森(Gregory Larsen),2016/01/01(第一次出版:2014 /01/ 29)
该系列
这篇文章是楼梯系列的一部分:通往t - sql的楼梯:超越基础。
从他的阶梯到t - sql DML,Gregory Larsen涵盖了t - sql语言的更高级的方面,如子查询。
在开始创建比基本transact - SQL语句更复杂的SQL代码时,您可能会发现需要使用其他SELECT语句的结果来约束您的查询。当您在父transact - sql语句中嵌入SELECT语句时,这些嵌入的SELECT语句被称为子查询或相关子查询。在这个层次的基础上,我将讨论子查询的不同方面,在以后的级别中,我将讨论相关的子查询。
子查询是什么?
子查询只是在另一个transact - sql语句中包含的SELECT语句。可以在任何可以使用表达式的地方使用子查询。许多子查询返回单个列值,因为它们与比较运算符一起使用(=,!=,< =,>,> =)或表达式。当子查询不用作表达式或与比较运算符使用时,它可以返回多个值。此外,子查询甚至可以返回多个列和值,当它们在FROM子句或关键字中使用时。
在transact - sql语句中很容易发现子查询,因为它将是括号中包含的SELECT语句。由于子查询包含在transact - sql语句中,子查询通常被称为内部查询。而包含子查询的transact - sql语句被称为外部查询。子查询的另一个特性是,它可以独立于外部查询运行,并且将运行没有错误,并且可能返回一组行,或者返回一个空行集。
子查询的另一种形式是相关子查询。但是关联子查询不能独立于外部Transact SQL语句运行。关联子查询使用来自外部查询的列或列来约束从相关子查询返回的结果。对于本文的相关子查询,这已经足够了。我将在未来的楼梯文章中探索相关的子查询。
以下是使用子查询时需要考虑的其他事项:
•ntext、文本和图像数据类型不允许从子查询返回
•除非使用顶部运算符,否则不能在子查询中使用ORDER BY子句
•使用子查询的视图不能更新
•在子查询中不能使用compute和INTO子句
子查询示例的示例数据
为了演示如何使用子查询,我需要一些测试数据。我的所有示例都将使用AdventureWorks2008R2数据库,而不是创建自己的测试数据。如果您想要跟随并运行我在您的环境中的示例,那么您可以从这里下载AdventureWorks2008R2数据库:
http://msftdbprodsamples.codeplex.com/releases/view/93587
返回单个值的子查询示例
如上所述,在比较操作符的一侧使用表达式或返回值的子查询需要返回一个值。transact - sql语句中有许多不同的地方需要子查询返回一个列值,就像在一个选择列表,where子句,等等。在本节中,我将提供一系列的例子将演示使用子查询表达式或比较运算符,以满足不同的业务需求。
子查询列列表中
列表中的子查询是一个SELECT语句,它返回SELECT子句的列列表中的单个列值。为了演示如何在选择列表中使用子查询,我们假设必须从SELECT语句中生成一个具有以下业务需求的结果集:
•归还所有的销售。SalesOrderHeader记录了一个订单日期等于“2007 -02-19 00:00. 00”
•按SalesOrderID顺序订购返回的记录
•每一行返回最古老的顺序的行数为1的行数,下一个最大的行数为2,等等
•结果集需要一个名为TotalOrders的列,该列需要填满整个订单的数量,该订单的订单数量等于“2007 -02-19 00:00. 00”
满足这些需求的代码如清单1所示。
清单1:列列表中的子查询
在这个transact - sql语句中,您可以看到两个不同的SELECT子句。子查询是在清单1的语句中嵌入的SELECT语句,它周围有括号。我已经提取了子查询语句并将其放入清单2中,以防您想要测试验证它是否可以独立于完整的transact - sql语句运行。
清单2:在清单1中找到的子查询语句
通过在列列表中拥有这个子查询,Listing1中的transact - sql语句能够计算有一个OrderDate“2007 -02-19 00:00. 000”的SalesOrderHeader行的数量,并返回该信息以及关于销售的详细行信息。有相同的OrderDate值的SalesOrderHeader记录。
子查询在WHERE子句中的例子
有时您希望根据SELECT语句的结果来驱动WHERE子句条件。当您在WHERE子句中选择语句时,这个SELECT语句实际上是一个子查询。为了演示在WHERE子句中使用子查询,假设您需要显示销售。SalesOrderDetail记录包含购买超大的长袖标志运动衫。清单3中的代码使用子查询满足我的显示需求。
清单3:WHERE子句中的子查询
清单3中的子查询位于WHERE条件的右侧。这个子查询标识生产的产品。产品名称的产品名称为“长袖标志球衣,XL”。这个子查询允许我找到所有的销售。SalesOrderDetail记录有一个与“长袖Logo Jersey,XL”的产品名称相关的产品。
示例使用子查询来控制TOP子句
使用TOP子句返回的行数可以由表达式控制。清单5中的代码标识了销售数量。SalesOrderDetail行应该基于顶部子句中的子查询返回。
清单4:TOP子句中的子查询
清单4中的代码使用从子查询返回的OrderQty值来标识将在TOP子句中使用的值。通过使用子查询来控制TOP子句返回的行数,可以创建一个子查询,该子查询将动态识别在运行时从查询返回的行数。
子查询在HAVING子句中的例子
为了演示在“有”子句中使用子查询,假设您有以下业务需求:
生成包含sales . salesorderheader的结果集。订单日期和每个日期的订单数量,其中订单数量超过了“2006 -05- 01”的订单数量。
为了满足这一要求,我已经开发了清单6中的查询,其中使用了“HAVING子句”中的子查询。
清单5:HAVING子句中的子查询
清单5中的代码在有子句的右边有子查询,并在我的子查询中使用COUNT函数来确定在“2006-05-01”上放置的订单数量。
在函数调用中使用子查询的例子
为了演示在函数调用中使用子查询,假设您有要求在OrderDate和每个Sales的最大OrderDate之间显示天数。SalesOrderHeader记录。清单6中的代码满足这一需求。
清单6:函数调用中的子查询
清单6中的代码有两个不同的子查询。两个子查询都返回Sales中的max OrderDate。SalesOrderHeader表。但是,第一个子查询用于将日期传递给DATEDIFF函数的第二个参数。
返回多个值的子查询示例
到目前为止,我的所有示例都包含子查询,这些子查询仅在单个列中返回单个值。并不是所有的子查询都有这样的要求。接下来的几个示例将使用返回多个值和/或多个列的子查询。
子查询在FROM子句中的示例
在FROM子句中,您通常确定您的transact - sql语句将针对的表或表集合。每个表提供一组记录,您的查询将使用这些记录来确定您的查询的最终结果集。子查询可以看作是返回一组记录的查询,因此它可以像表一样在FROM子句中使用。清单7中的查询显示了我如何在FROM子句中使用子查询。在FROM子句中使用子查询时,子查询生成的结果集通常称为派生表。
清单7:FROM子句中的子查询
清单7中的代码使用FROM子句中的子查询创建一个表别名,名为Last10SalesOrders。我的子查询返回了最后10个销售。alesOrderDetail记录包含一个716的产品。
在清单7中,我的代码是一个非常简单的例子,说明如何在FROM子句中使用子查询。通过使用FROM子句中的子查询,您可以轻松地从语法中构造出更复杂的代码,这些语法将与其他表的子查询结果连接起来,或者添加其他子查询,如清单8所示。
清单8:加入一个有实表的派生表
在清单8中,我使用了在清单7中创建的子查询/派生表,并将它与SalesOrderHeader表连接起来。通过这样做,我可以确定一个不同的订单日期,这是最后10次人们订购的产品= 716。
使用IN关键字进行子查询的示例
另一个可以编写子查询的地方,该子查询返回一个列的多个值,当子查询生成一个用于关键字的记录集时。清单9中的代码演示了如何使用子查询将值传递到in关键字。
清单9:使用子查询将值传递到IN关键字
清单9中的代码使用子查询返回产品的不同值。包含字符“XL”的产品表。这些从子查询返回的ProductID值然后在in关键字中使用,以约束从销售中返回的行。SalesOrderDetail表。
在修改数据的语句中使用子查询的例子
到目前为止,我的所有示例都演示了如何在SELECT语句的不同部分中使用子查询。子查询也可以在插入、更新或删除语句中使用。清单10中的代码展示了如何在INSERT语句中使用子查询。
清单10:插入语句中的子查询
在清单10的代码中,我使用子查询来计算插入到列MaxOrderDate中的值。这仅仅是如何在INSERT语句中使用子查询的一个示例。请记住,子查询也可以在UPDATE和/或DELETE语句中使用。
子查询与连接之间的性能考虑
如果你读过微软生产的“子查询基础”文档(http://technet.microsoft.com/en-us/library/ms189575(v=sql.105).aspx)
然后,您可能遇到了关于包含子查询的语句的性能的语句:
“在transact - sql中,包含子查询和语义等价的语句之间通常没有性能差异。”
为了比较使用子查询和不使用子查询的查询的性能,我将重写清单3中的子查询以使用连接操作。清单11显示了我重新编写的JOIN查询,它相当于清单3中的查询。
清单11:连接查询,与清单3中的查询等效
为了比较清单3中使用子查询的查询的性能和使用JOIN的清单11中的查询,我将使用清单12中的代码运行两个查询。
清单12:测试清单3和清单4的性能的代码
在运行清单12中的代码后,我查看了由“SET STATISTICS”语句生成的消息。通过查看统计数据,我发现两个查询对SalesOrderDetail表有3,309个逻辑读取,2个逻辑读针对产品表,每个都使用31毫秒的CPU。另外,我回顾了为这两个查询创建的SQL Server的执行计划。我发现SQL Server对这两种情况都产生了相同的执行计划。因此,在我的情况下使用子查询或连接查询产生等价的性能,就像微软所记录的那样。
总结
子查询是嵌入另一个transact - sql语句的SELECT语句。子查询可以独立于外部查询运行,因此有时被称为独立查询。请记住,每当有一个子查询替代表达式时,或者它与比较运算符一起使用,它只能返回单个列和值。通常可以使用JOIN逻辑重写子查询。子查询是帮助您构建更复杂的transact - sql语句以满足业务需求的强大工具。
问题和回答
在本节中,您可以通过回答以下问题来回顾您如何理解使用子查询概念。
问题1:
完成这个句子“子查询是在另一个transact - sql语句中选择语句,它是_____________________”。
•不能独立于完整的查询运行。
•引用来自外部查询的列。
•当独立于外部查询运行时,它将返回结果。
问题2:
子查询何时只需要返回一个列和值(选择所有应用程序)?
•当子查询在FROM子句中使用时
•当子查询在in子句中使用时
•在表达式中使用子查询
•当子查询与比较运算符一起使用时
问题3:
在WHERE子句中使用子查询的transact - sql语句总是比不包含子查询(True或False)的等价查询执行速度慢。
•真
•假
回答:
问题1:
正确的答案是c。子查询可以独立于外部查询运行,它将返回结果。它不需要来自外部查询的任何列,如果它有来自外部查询的列,则称为相关子查询。
问题2:
正确的答案是c和d。子查询需要返回一个列值,当它用作表达式时,或者在比较操作中。当在关键字中使用子查询时,它可以为一列返回一个或多个值。如果在FROM子句中使用子查询,它只返回一个列和一个值,但它也可以返回多个列和值。
问题3:
正确的答案是错误的。SQL Server优化器非常聪明,很可能会为两个等价查询计算相同的执行计划。如果一个查询的执行计划包含一个子查询,而没有子查询的查询的执行计划最终都有相同的执行计划,那么这两个查询将具有相同的性能。
链接:http://www.sqlservercentral.com/articles/Stairway+Series/104517/