这篇文章是楼梯系列的一部分:通往T-SQL的楼梯:超越基础sql
从他的阶梯到T - SQL DML, Gregory Larsen涵盖了T-SQL语言的更高级的方面,如子查询。数据库
当你在开始建立比基本Transact-SQL语句更复杂的SQL代码时,你可能会发现须要使用其余SELECT语句的结果来约束您的查询。当您在父Transact-SQL语句中嵌入SELECT语句时,这些嵌入的SELECT语句被称为子查询或相关子查询。在这个层次的基础上,我将讨论子查询的不一样方面,在之后的级别中,我将讨论相关的子查询。函数
一个子查询就是一个包含其余Transact-SQL语句的查询语句,一个子查询能够被用在任何表达式,许多子查询结果返回单个结果由于他们被用在链接比较操做(=,!=,<=,>,>=)或者表达。当子查询不用做表达式或与比较运算符使用时,它能够返回多个值。此外,子查询甚至能够返回多个列和值,当它们在FROM子句或关键字中使用时。工具
在Transact-SQL语句中很容易发现子查询,由于它将是括号中包含的SELECT语句。因为子查询包含在Transact-SQL语句中,子查询一般被称为内部查询。而包含子查询的Transact-SQL语句被称为外部查询。子查询的另外一个特性是,它能够独立于外部查询运行,而且将运行没有错误,而且可能返回一组行,或者返回一个空行集。sqlserver
子查询的另外一种形式是相关子查询。可是关联子查询不能独立于外部Transact SQL语句运行。关联子查询使用来自外部查询的列或列来约束从相关子查询返回的结果。对于本文的相关子查询,这已经足够了。我将在将来的楼梯文章中探索相关的子查询。性能
如下是使用子查询时须要考虑的其余事项:测试
ntext、文本和图像数据类型不容许从子查询返回spa
除非使用顶级运算符,不然不能在子查询中使用ORDER BY子句code
不能更新使用子查询的视图server
不能在子查询中使用COMPUTE和INTO子句
为了演示如何使用子查询,我须要一些测试数据。个人全部示例都将使用AdventureWorks2008R2数据库,而不是建立本身的测试数据。若是你想跟随并运行在您的环境中个人例子你能够从这里(http://msftdbprodsamples.codeplex.com/releases/view/93587 )下载AdventureWorks2008R2数据库。
如上所述,在比较操做符的一侧使用表达式或返回值的子查询须要返回一个值。Transact SQL语句中有许多不一样的地方须要子查询返回一个列值,就像在一个选择列表,where子句等。在本节中,我将提供一系列的例子将演示使用子查询表达式或比较运算符,以知足不一样的业务需求。
列表中的子查询是一个SELECT语句,它返回SELECT子句的列列表中的单个列值。为了演示如何在选择列表中使用子查询,咱们假设必须从SELECT语句中生成一个具备如下业务需求的结果集:
返回全部的销售数据。SalesOrderHeader记录了一个订单日期等于“2007 -02-19 00:00. 00”
按SalesOrderID顺序订购返回的记录
每一行返回最古老的顺序的行数为1的行数,下一个最大的行数为2,等等
结果集须要一个名为TotalOrders的列,该列须要包含有一个与“2007 -02-19 00:00. 00”相同的OrderDate的订单总数。
知足这些需求的代码如清单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的语句中嵌入的SELECT语句,它周围有括号。我已经提取了子查询语句并将其放入清单2中,以防您想要测试验证它是否能够独立于完整的Transact -SQL语句运行。
SELECT COUNT(*) FROM [Sales].[SalesOrderHeader] WHERE OrderDate = '2007-02-19 00:00:00.000'
经过在列列表中拥有这个子查询,Listing1中的transact - sql语句可以计算有一个OrderDate“2007 -02-19 00:00. 000”的SalesOrderHeader行的数量,并返回该信息以及关于销售的详细行信息。有相同的OrderDate值的SalesOrderHeader记录。
有时您但愿根据SELECT语句的结果来驱动WHERE子句条件。当您在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中的子查询位于WHERE条件的右侧。这个子查询标识生产的产品。产品名称的产品名称为“长袖标志球衣,XL”。这个子查询容许我找到全部的销售。SalesOrderDetail记录有一个与“长袖Logo Jersey,XL”的产品名称相关的产品。
使用TOP子句返回的行数能够由表达式控制。清单5中的代码标识了销售数量。SalesOrderDetail行应该基于顶部子句中的子查询返回。
SELECT TOP (SELECT TOP 1 OrderQty FROM [Sales].[SalesOrderDetail] ORDER BY ModifiedDate) * FROM [Sales].[SalesOrderDetail] WHERE ProductID = 716;
清单4:TOP子句中的子查询
清单4中的代码使用从子查询返回的OrderQty值来标识将在TOP子句中使用的值。经过使用子查询来控制TOP子句返回的行数,能够建立一个子查询,该子查询将动态识别在运行时从查询返回的行数。
为了演示在“有”子句中使用子查询,假设您有如下业务需求:
生成包含sales . salesorderheader的结果集。订单日期和每一个日期的订单数量,其中订单数量超过了“2006 -05- 01”的订单数量。
为了知足这一要求,我已经开发了清单6中的查询,其中使用了“HAVING子句”中的子查询。
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:HAVING子句中的子查询
清单5中的代码在有子句的右边有子查询,并在个人子查询中使用COUNT函数来肯定在“2006-05-01”上放置的订单数量。
为了演示在函数调用中使用子查询,假设您有要求在OrderDate和每一个Sales的最大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中的代码有两个不一样的子查询。两个子查询都返回Sales中的max OrderDate。SalesOrderHeader表。可是,第一个子查询用于将日期传递给DATEDIFF函数的第二个参数。
返回多个值的子查询示例
到目前为止,个人全部示例都包含子查询,这些子查询仅在单个列中返回单个值。并非全部的子查询都有这样的要求。接下来的几个示例将使用返回多个值和/或多个列的子查询。
子查询在FROM子句中的示例
在FROM子句中,您一般肯定您的transact - sql语句将针对的表或表集合。每一个表提供一组记录,您的查询将使用这些记录来肯定您的查询的最终结果集。子查询能够看做是返回一组记录的查询,所以它能够像表同样在FROM子句中使用。清单7中的查询显示了我如何在FROM子句中使用子查询。在FROM子句中使用子查询时,子查询生成的结果集一般称为派生表。
SELECT SalesOrderID FROM (SELECT TOP 10 SalesOrderID FROM [Sales].[SalesOrderDetail] WHERE ProductID = 716 ORDER BY ModifiedDate DESC) AS Last10SalesOrders;
清单7中的代码使用FROM子句中的子查询建立一个表别名,名为last10 salesorders。个人子查询返回了最后10个销售。alesOrderDetail记录包含一个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中,我使用了在清单7中建立的子查询/派生表,并将它与SalesOrderHeader表链接起来。经过这样作,我能够肯定一个不一样的订单日期,这是最后10次人们订购的产品= 716。
使用IN关键字进行子查询的示例
另外一个能够编写子查询的地方,该子查询返回一个列的多个值,当子查询生成一个用于关键字的记录集时。清单9中的代码演示了如何使用子查询将值传递到in关键字。
SELECT * FROM [Sales].[SalesOrderDetail] WHERE ProductID IN (SELECT ProductID FROM [Production].[Product] WHERE Name like '%XL%');
清单9中的代码使用子查询返回产品的不一样值。包含字符“XL”的产品表。这些从子查询返回的ProductID值而后在in关键字中使用,以约束从销售中返回的行。SalesOrderDetail表。
在修改数据的语句中使用子查询的例子
到目前为止,个人全部示例都演示了如何在SELECT语句的不一样部分中使用子查询。子查询也能够在插入、更新或删除语句中使用。清单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的代码中,我使用子查询来计算插入到列MaxOrderDate中的值。这仅仅是如何在INSERT语句中使用子查询的一个示例。请记住,子查询也能够在UPDATE和/或DELETE语句中使用。
子查询与链接之间的性能考虑
若是你读过的“子查询基本面”文档由微软(http://technet.microsoft.com/en-us/library/ms189575(v = sql.105). aspx),那么您可能已经运行在这个声明中关于性能包含子查询的语句:
“在transact - sql中,包含子查询和语义等价的语句之间一般没有性能差别。”
为了比较使用子查询和不使用子查询的查询的性能,我将重写清单3中的子查询以使用链接操做。清单11显示了我从新编写的JOIN查询,它至关于清单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';
为了比较清单3中使用子查询的查询的性能和使用JOIN的清单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.ProductID WHERE P.Name = 'Long-Sleeve Logo Jersey, XL';
在运行清单12中的代码后,我查看了由“SET STATISTICS”语句生成的消息。经过查看统计数据,我发现两个查询对SalesOrderDetail表有3,309个逻辑读取,2个逻辑读针对产品表,每一个都使用31毫秒的CPU。另外,我回顾了为这两个查询建立的SQL Server的执行计划。我发现SQL Server对这两种状况都产生了相同的执行计划。所以,在个人状况下使用子查询或链接查询产生等价的性能,就像微软所记录的那样。
子查询是嵌入另外一个transact - sql语句的SELECT语句。子查询能够独立于外部查询运行,所以有时被称为独立查询。请记住,每当有一个子查询替表明达式时,或者它与比较运算符一块儿使用,它只能返回单个列和值。一般可使用JOIN逻辑重写子查询。子查询是帮助您构建更复杂的transact - sql语句以知足业务需求的强大工具。
原文连接:http://www.sqlservercentral.com/articles/Stairway+Series/104517/