通往t - sql的阶梯:超越基本级别2:编写子查询sql
格雷戈里·拉森(Gregory Larsen),2016/01/01(第一次出版:2014/01/29)数据库
该系列函数
这篇文章是楼梯的一部分系列:t - sql的阶梯:除了基础知识工具
从他的t - sql DML楼梯后,格雷戈里·拉森涵盖了更高级的子查询等方面的t - sql语言。性能
在某些时候当你开始建立更复杂的SQL代码,超越基本transact - SQL语句,您可能会发现须要限制你的查询使用其余SELECT语句的结果。测试
当你在父母transact - sql语句嵌入一个SELECT语句,这些嵌入的SELECT语句被称为子查询,或相关子查询。优化
在本层的楼梯的基本内容外,我将讨论子查询的不一样方面,在将来我将讨论相关子查询。code
子查询是什么?开发
子查询是一个SELECT语句是包含在数据库引擎执行另外一个sql语句。文档
可使用子查询可使用任何一个表达式。
许多子查询返回一列值,由于他们结合使用比较运算符(=,!
=、<、< =、>、> =),或者一个表达式。
当不使用子查询表达式或比较运算符能够返回多个值。
此外,子查询能够返回多个列和价值观在FROM子句中使用或与关键字的存在。
数据库引擎执行子查询是容易被发如今一个sql语句,由于它将SELECT语句包含在圆括号中。
因为数据库引擎执行子查询包含在一个sql语句查询一般被称为内部查询。
而transact - sql语句包含子查询被称为外部查询。
子查询的另外一个特色是它能够独立运行的外部查询和运行没有错误,并可能返回的一组行,或一个空行。
另外一种形式的子查询是相关子查询。
但相关子查询不能独立运行的外部交易的SQL语句。
相关子查询使用列或列从外部查询限制相关子查询返回的结果。
这是对这篇文章的相关子查询。
我将探索相关子查询在之后的楼梯。
这里有一些其余的事情时要考虑使用子查询:
ntext、文本和图像数据类型不容许从子查询返回
ORDER BY子句不能用于使用子查询,除非顶级运营商
视图使用子查询不能被更新
计算和条款不能使用子查询
样本数据的子查询的例子
为了演示如何使用子查询我须要一些测试数据。
而不是建立我本身的测试数据,我全部的示例将使用AdventureWorks2008R2数据库。
若是你想跟随并运行在您的环境中个人例子你能够从这里下载AdventureWorks2008R2数据库:http://msftdbprodsamples.codeplex.com/releases/view/93587
返回一个值的子查询的例子
如上所述,子查询中使用一个表达式或返回一个值比较运算符的一侧必须返回一个值。
transact - sql语句中有许多不一样的地方须要子查询返回一个列值,就像在一个选择列表,where子句,等等。在本节中,我将提供一系列的例子将演示使用子查询表达式或比较运算符,以知足不一样的业务需求。
子查询列列表中
子查询的列列表是一个SELECT语句返回一列值的列列表放置在一个SELECT子句。
为了演示如何使用选择列表的子查询假设咱们必须从一个SELECT语句产生一个结果集,如下业务需求:
返回全部的销售。
OrderDate SalesOrderHeader记录有什么等于“2007-02-19 00:00:00.000”
SalesOrderID顺序返回的记录
返回每一行数最古老秩序RowNumber 1,下一个古老的RowNumber 2,等等
须要一个结果集列名叫TotalOrders须要填充的总订单数量有OrderDate等于“2007-02-19 00:00:00.000”
知足这些需求的代码如清单1所示。
SELECT ROW_NUMBER() OVER (ORDER BY SalesOrderID) RowNumber , (SELECT COUNT(*) FROM [Sales].[SalesOrderHeader] WHERE ModifiedDate = '2007-02-19 00:00:00.000') AS TotalOrders , * FROM [Sales].[SalesOrderHeader] WHERE OrderDate = '2007-02-19 00:00:00.000';
清单1:子查询的列列表
在这个单一transact - sql声明中看到两个不一样的选择条款。
子查询的SELECT语句嵌入在清单1,括号中的语句。
我拿出了子查询语句和把它在清单2中,若是您想测试来验证它能够独立完成transact - sql语句的运行。
SELECT COUNT(*) FROM [Sales].[SalesOrderHeader] WHERE OrderDate = '2007-02-19 00:00:00.000'
清单2:子查询语句中找到清单1所示
经过这个子查询的列列表,这Listing1 transact - sql语句能够数一数的OrderDate SalesOrderHeader行有一个“2007-02-19 00:00:00.000”并返回信息以及有关销售的详细行信息。
OrderDate SalesOrderHeader记录有相同的价值。
子查询的WHERE子句的例子
有时你想开车WHERE子句条件基于一个SELECT语句的结果。
当你一个SELECT语句的WHERE子句这个SELECT语句是一个真正的子查询。
为了演示在WHERE子句中使用子查询,假设你须要显示销售。
SalesOrderDetail记录包含购买超大长袖商标运动衫。
清单3中的代码使用子查询知足个人显示要求。
SELECT * FROM [Sales].[SalesOrderDetail] WHERE ProductID = (SELECT ProductID FROM [Production].[Product] WHERE Name = 'Long-Sleeve Logo Jersey, XL');
清单3:子查询的WHERE子句
清单3中的查询是在右边的条件。
这个子查询标识一个ProductID生产。
产品记录的名称是“长袖标志的球衣,XL。
这个子查询容许我找到全部的销售。
SalesOrderDetail记录有ProductID与产品名称相关联的“长袖标志的球衣,XL”。
示例使用子查询控制条款
返回的行数使用上面的条款能够控制的一个表达式。
清单5中的代码标识的数量销售。
SalesOrderDetail行应该返回基于子查询在条款。
SELECT TOP (SELECT TOP 1 OrderQty FROM [Sales].[SalesOrderDetail] ORDER BY ModifiedDate) * FROM [Sales].[SalesOrderDetail] WHERE ProductID = 716;
清单4:子查询最佳条款
清单4中的代码使用OrderQty子查询返回的值来标识的值将用于条款。
经过使用子查询控制返回的行数,上面条款,容许您动态构建子查询,肯定在运行时从查询返回的行数。
子查询的例子有条款
为了演示使用子查询子句,假设你有如下业务需求:
产生一个结果集,其中包含Sales.SalesOrderHeader。
OrderDate和每一个日期,订单的数量,订单的数量超过了订单的数量了‘2006-05-01’。
为了达到这个要求我开发了清单6中的查询中使用子查询子句。
SELECT count(*), OrderDate FROM [Sales].[SalesOrderHeader] GROUP BY OrderDate HAVING count(*) > (SELECT count(*) FROM [Sales].[SalesOrderHeader] WHERE OrderDate = '2006-05-01 00:00:00.000');
清单5:子查询的条款
清单5中的代码的子查询右边有条款和使用计数函数在子查询来肯定订单的数量在“2006-05-01”。
在函数调用中使用子查询的例子
为了演示在函数调用中使用子查询,假设您有要求显示OrderDate之间的天数和最大OrderDate为每一个销售。
SalesOrderHeader记录。
清单6中的代码知足这个需求。
SELECT SalesOrderID , OrderDate ,DATEDIFF ( dd,OrderDate ,(SELECT MAX(OrderDate) FROM [Sales].[SalesOrderHeader]) ) AS DaysBetweenOrders ,(SELECT MAX(OrderDate) FROM [Sales].[SalesOrderHeader]) AS MaxOrderDate FROM [Sales].[SalesOrderHeader];
清单6:在函数调用子查询
清单6中的代码有两个不一样的子查询。
两个子查询返回的马克斯向销售。
SalesOrderHeader表。
但第一子查询是用来传递一个日期DATEDIFF函数的第二个参数。
子查询返回多个值的例子
到目前为止,我全部的例子包含子查询只返回一个值在一个列中。
并非全部的子查询需求。
接下来的几个例子将使用子查询返回多个值和/或多个列。
子查询的FROM子句的例子
在FROM子句一般肯定一个表或一组表,你transact - sql语句将对运做。
每一个表提供了一组记录您的查询将使用来肯定最终的查询结果集。
子查询能够被认为是一个查询,返回一组记录,所以它能够用于从条款就像一张桌子。
查询我在清单7显示了如何使用子查询的FROM子句。
子查询时FROM子句中使用子查询的结果集生产一般被称为一个派生表。
SELECT SalesOrderID FROM (SELECT TOP 10 SalesOrderID FROM [Sales].[SalesOrderDetail] WHERE ProductID = 716 ORDER BY ModifiedDate DESC) AS Last10SalesOrders
清单7:子查询的FROM子句
清单7中的代码使用子查询的FROM子句建立一个表别名,Last10SalesOrders命名。
个人子查询返回过去10销售。
alesOrderDetail记录包含ProductID 716。
个人代码在清单7中是一个很是简单的例子,如何使用子查询的FROM子句。
经过使用子查询的FROM子句你能够从语法构造更复杂的链接与其余表子查询的结果,或额外的子查询,就像我在清单8中。
SELECT DISTINCT OrderDate FROM (SELECT TOP 10 SalesOrderID FROM [Sales].[SalesOrderDetail] WHERE ProductID = 716 ORDER BY ModifiedDate DESC) AS Last10SalesOrders JOIN [Sales].[SalesOrderHeader] AS SalesOrderHeader ON Last10SalesOrders.SalesOrderID = SalesOrderHeader.SalesOrderID ORDER BY OrderDate
清单8:加入一个派生表与一个真实的表
在清单8中我建立的子查询/派生表我在清单7中,加入SalesOrderHeader表。
这样我能够肯定OrderDate大相径庭的最后一人命令ProductID = 716的10倍。
使用子查询的关键字的例子
另外一个地方,您能够编写一个查询,它返回多个值的列是当你的子查询生成一个记录集,使用的关键字。
清单9中的代码演示了如何经过使用子查询的关键字值。
SELECT * FROM [Sales].[SalesOrderDetail] WHERE ProductID IN (SELECT ProductID FROM [Production].[Product] WHERE Name like '%XL%');
清单9:传递值的关键字使用子查询
清单9中的代码使用子查询来从生产为ProductID返回不一样的值。
产品表,有一个名称包含字符“XL”。
这些ProductID值返回的子查询中使用在关键字来限制返回的行销售。
SalesOrderDetail表。
在一份声明中使用子查询的例子,修改数据
目前为止我全部的示例演示如何使用子查询在一个SELECT语句的不一样部分。
子查询也可被用来在一个INSERT、UPDATE或DELETE语句。
清单10中的代码展现了如何使用子查询在一个INSERT语句。
DECLARE @SQTable TABLE ( OrderID int, OrderDate datetime, TotalDue money, MaxOrderDate datetime); -- INSERT with SubQuery INSERT INTO @SQTable SELECT SalesOrderID, OrderDate, TotalDue, (SELECT MAX(OrderDate) FROM [Sales].[SalesOrderHeader]) FROM [Sales].[SalesOrderHeader] WHERE CustomerID = 29614; -- Display Records SELECT * FROM @SQtable;
清单10:子查询在一个INSERT语句
在个人代码在清单10中,我使用子查询计算值列MaxOrderDate插入。
这只是一个例子,如何在INSERT语句中使用子查询。
记住子查询也可被用来在一个更新和/或DELETE语句。
子查询之间的性能考虑,加入
若是你读过的“子查询基本面”文档由微软(http://technet.microsoft.com/en-us/library/ms189575(v = sql.105). aspx),那么您可能已经运行在这个声明中关于性能包含子查询的语句:
“在transact - sql,一般没有语句之间的性能差别,包括子查询和语义上等价版本不。”
比较查询的性能使用子查询和一个等价查询不使用子查询我重写个人子查询在清单3中使用链接操做。
清单11显示了查询重写链接查询,至关于我在清单3中。
SELECT SOD.* FROM [Sales].[SalesOrderDetail] AS SOD INNER JOIN [Production].[Product] AS P ON SOD.ProductID = P.ProductID WHERE P.Name = 'Long-Sleeve Logo Jersey, XL';
清单11:加入查询等价于清单3中的查询
比较查询的性能在清单3中使用子查询和查询在清单11中,使用加入我将使用清单12中的代码运行两个查询。
SET STATISTICS IO ON; SET STATISTICS TIME ON; -- Listing 3 query SELECT * FROM [Sales].[SalesOrderDetail] WHERE ProductID = (SELECT ProductID FROM Production.Product WHERE Name = 'Long-Sleeve Logo Jersey, XL'); -- Listing 11 query SELECT SOD.* FROM [Sales].[SalesOrderDetail] AS SOD INNER JOIN [Production].[Product] AS P ON SOD.ProductID = P.ProductIDo
WHERE P.Name = 'Long-Sleeve Logo Jersey, XL';
清单12:代码清单3和清单4的测试性能
清单12中的代码运行后,我回顾了消息产生的“统计”报表。
经过回顾两种查询统计我发现有3309对SalesOrderDetail表逻辑读,和对产品表2逻辑读,每31女士的CPU使用。
另外我回顾了执行计划,SQL Server建立这两个查询。
我发现SQL Server产生相同的执行计划。
所以使用子查询或加入查询个人处境产生等效性能,一样记录了微软。
总结
子查询是一个SELECT语句嵌入式数据库引擎执行与另外一个sql语句。
外查询的子查询能够独立运行,所以有时被称为一个独立的查询。
记住,任什么时候候你有一个查询的一个表达式,或是使用比较运算符,它能够只返回单个列和价值。
一般子查询可使用加入重写逻辑。
子查询是一个强大的工具来帮助你创建你的数据库引擎执行更复杂的sql语句来知足您的业务需求。
问题和答案
在本节中,您能够检查你理解如何使用子查询概念,回答下列问题。
问题1:
完成这个句子“子查询是一个数据库引擎执行SELECT语句在另外一个sql语句,_____________________。
不是不能独立运行完整的查询。
引用列从外部查询。
当独立运行的外部查询将返回的结果。
问题2:
何时子查询只须要返回一个列和值(选择全部适用)?
子查询时在FROM子句中使用
子查询时使用的条款
在子查询中使用一个表达式
子查询时使用比较运算符
问题3:
transact - sql语句的WHERE子句中使用子查询老是比等效的查询执行速度较慢,不包含子查询(真或假)?
真正的
假
答案:
问题1:
正确答案是c。外查询的子查询能够独立运行,它将返回结果。
它不须要任何列从外部查询,若是有列外查询称为相关子查询。
问题2:
正确答案是c和d。子查询时须要返回一列值用做表达式,或者比较操做。
子查询时使用的关键字能够返回一个或多个值的列。
若是子查询在FROM子句中使用它能够返回一个列和一个值,但它也能够返回多个列和价值观。
问题3:
正确的答案是错误的。
SQL Server优化器很聪明和极可能计算相同的两个等价的查询执行计划。
若是一个查询的执行计划,其中包含子查询和一个等价的无子查询都获得相同的执行计划而后查询都有相同的性能。