翻译:通往t - sql的阶梯:超越基本级别2:编写子查询

通往t - sql的阶梯:超越基本级别2:编写子查询

格雷戈里·拉森(Gregory Larsen),2016/01/01(第一次出版:2014/01/29)

该系列

这篇文章是楼梯的一部分系列:t - sql的阶梯:除了基础知识

 

从他的t - sql DML楼梯后,格雷戈里·拉森涵盖了更高级的子查询等方面的t - sql语言。

 

在某些时候当你开始创建更复杂的SQL代码,超越基本transact - SQL语句,您可能会发现需要限制你的查询使用其他SELECT语句的结果。

当你在父母transact - sql语句嵌入一个SELECT语句,这些嵌入的SELECT语句被称为子查询,或相关子查询。

在本层的楼梯的基本内容外,我将讨论子查询的不同方面,在未来我将讨论相关子查询。

 

子查询是什么?

子查询是一个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优化器很聪明和很可能计算相同的两个等价的查询执行计划。

如果一个查询的执行计划,其中包含子查询和一个等价的无子查询都得到相同的执行计划然后查询都有相同的性能。

 

posted @ 2017-10-31 19:18  奔跑吧菜鸟  阅读(234)  评论(0编辑  收藏  举报