翻译9

通往T-SQL的楼梯:要建立一个相关的子查询超出基础等级三

章节
本文是阶梯系列的一部分:阶梯到T-SQL:超出基础从他的阶梯到T-SQLDML,Gregory Larsen介绍了更高级的方面的SQL语言,比如子查询。

在这个阶梯的第2级中,我讨论了楼梯级别将扩展到subcl子查询。我将探讨如何在Transact-SQL语句中使用子查询。本主题通过讨论一种称为协关联子查询的子查询类型,以及它与普通子查询的区别。此外,我将向您提供一些事务-sql语句的示例,这些语句超出了基础,并使用相关的子查询来帮助识别结果集中返回的行,以满足复杂的业务需求。

什么是关联子查询?

在这个阶梯的第2级中,我们了解到一个普通的子查询只是另一个Transact-SQL语句中的SELECT语句。在这个语句中,如果独立于外部查询运行,子查询可以返回结果。关联子查询是不能独立于外部查询运行的子查询的一种形式,因为它包含来自外部查询的一个或多个列。关联子查询与普通子查询一样,有时被称为内部查询。如果关联子查询(内部查询)独立于外部查询运行,它将返回错误。由于内部查询的执行取决于外部查询的值,因此称为关联子查询。

相关子查询可以执行多次。对于外部查询中选择的每个候选行,它将运行一次。每个候选行的列值将用于为相关子查询的每次执行提供内部外部查询列的值。包含关联子查询的语句的最终结果将基于相关子查询每次执行的结果。

相关子查询示例的样本数据

为了演示如何使用关联子查询,我需要一些测试数据。我自己的测试数据。我的所有示例都将使用AdventureWorks2008R2数据库,然后在您的环境中继续并运行我的示例,然后您可以从这里下载AdventureWorks2008R2数据库:

https://archive.codeplex.com/?p=msftdbprodsamples

WHERE子句中关联子查询的示例

为了演示WHERE子句中相关子查询的使用,我希望识别那些在单个订单中购买了70多个商品的CustomerID。要完成这个需求,可以运行清单1中的代码

SELECT CustomerID FROM Sales.SalesOrderHeader OH

WHERE (SELECT COUNT(*) FROM Sales.SalesOrderDetail

         WHERE SalesOrderID = OH.SalesOrderID) > 70; 

 清单1:WHERE子句中的关联子查询

当我运行清单1中的代码时,我得到了报道 1中的输出

CustomerID

-----------

29712

29722

30048

30107

 Report 1:在运行清单1中的代码时返回的结果-如果您查看清单1中的代码-您将看到我通过使用关联子查询限制了我的位置,子查询是括号内的代码,我从清单1中提取了相关的子查询代码,并将其放在清单 2中。

SELECT COUNT(*) FROM Sales.SalesOrderDetail

         WHERE SalesOrderID = OH.SalesOrderID

 清单2:清单如果你运行子查询代码-

我在清单 2中运行了代码-我会发现我得到了一个错误,显示在报告2中

Msg 4104, Level 16, State 1, Line 3The multi-part identifier "OH.SalesOrderID" could not be bound.

报告2:清单2中的运行代码出错

我得到了报告2中显示的错误,因为关联子查询包含对OH.SalesOrderID列的引用,该列是来自外部查询的一列。由于所有相关子查询都引用外部查询中的一个或多个列,因此不能独立于与其关联的外部查询运行它们。不能独立于整个Transact-SQL语句运行子查询,这是关联子查询与普通子查询不同的地方。

这里提供的示例是在WHERE子句中使用关联子查询的一个非常简单的示例。希望通过这样一个简单的例子,可以很容易地理解普通子查询和相关子查询之间的区别。通常,相关子查询可能要复杂得多。此外,请记住,在不使用关联子查询的情况下,可能有其他方法来满足您的业务需求。

如您所见,编写关联子查询与正常的子查询非常相似-关联子查询独立于外部查询。

有子句中的关联子查询示例

有时候,您可能希望通过外部查询的不同值来约束HAVING子句-这是您可以在HAVING子句中使用关联子查询的时候。假设您必须编写一个查询,该查询将计算那些在2008年购买了价值超过150000美元的产品税前产品的客户的退税金额。清单3中的代码通过使用具有关联的子查询为那些有价值的客户计算回扣金额。

SELECT Outer_H.[CustomerID]

     , SUM(Outer_H.[SubTotal]) AS TotalPurchase

 , SUM(Outer_H.[SubTotal]) * .10 AS Rebate

FROM [Sales].[SalesOrderHeader] AS Outer_H

WHERE YEAR(Outer_H.[OrderDate]) = '2008'

GROUP BY Outer_H.[CustomerID]

HAVING (SELECT SUM(Inner_H.[SubTotal]) FROM [Sales].[SalesOrderHeader] AS Inner_H

        WHERE Inner_H.[CustomerID] = Outer_H.[CustomerID]

AND YEAR(Inner_H.[OrderDate]) = '2008') > 150000

ORDER BY Rebate DESC;

清单3:在HAVING子句中关联子查询

在清单5中运行代码时,我得到报告3中的结果

CustomerID  TotalPurchase         Rebate

----------- --------------------- ---------------------------------------

29923       220496.658            22049.665800

29641       210647.4929           21064.749290

29617       187964.844            18796.484400

29913       186387.5613           18638.756130

29818       179916.2877           17991.628770

29940       175358.3954           17535.839540

29987       172169.4612           17216.946120

29736       157700.6034           15770.060340

29995       156984.5148           15698.451480

29770       151824.9944           15182.499440

报告3:运行清单3的结果

清单3中的关联子查询代码在关联子查询中的外部查询中使用了来自GROUP BY子句的Customer ID。关联子查询将对GROUP BY子句返回的每一行执行一次。这允许HAVING子句通过外部查询计算每个Customer ID销售给每个Customer ID的产品总量,方法是将每个Sales Order领导人记录上的小计列的值相加,其中记录与外部查询中的Customer ID相关联。清单3中的Transact-sql语句只返回一个行,其中Customer ID已经搜索了超过150.000的产品。

包含关联子查询的UPDATE语句示例

关联子查询不仅可以用于使用SELECT语句返回结果集,还可以使用它们更新SQLServer表中的数据以演示这一点,我将首先使用清单4中的代码在tempdb表中生成一些测试数据。

USE tempdb;

GO

SET NOCOUNT ON;

CREATE TABLE CarInventory (

ID int identity,

CarName varchar(50),

VIN varchar(50),

StickerPrice decimal (7,2),

InvoicePrice decimal (7,2));

GO

INSERT INTO CarInventory VALUES ('Explorer','EXP2014123456A',46198.45,38201.87),

('Explorer','EXP2014123493A',47129.98, 38201.87),                               

('Grand Cherokee','JGC20141234345X',41678.45,36201.86),

('Grand Cherokee','JGC20141234556W',44518.31,36201.86),

('Pathfinder','NPF2014987365A',32587.73,28917.10),

('Pathfinder','NPF2014239657B',33577.54,28917.10),

('Pathfinder','NPF2014098587C',35876.12,28917.10),

('Tahoe','TAH201409674A',52001.08,46000.01);

 

4中的代码创建一个表车的存货清单然后填充八行代表当前车的库存。

定期的销售经理可以看到他的发票的价格比率通过运行5中的查询。

SELECT CarName, InvoicePrice/StickerPrice*100.0 AS InvoicePriceRatio

FROM CarInventory;

当经理运行此查询她注意到有一些相似的有相同的汽车发票价金额,有不同的发票价格比率值。为了最大化她的发票价格比率她写一个了查询,它将更新她所有车的价格比率,每辆车以同样的车名值具有相同的发票价格比率值。她希望通过网络来设置价格比率的值相同作为最高标价。这样,所有的汽车都有相同的车名值都有相同的发票比率值。为了完成这个更新的车的存货清单表,运行6中Transact-SQL语句,其中包含一个相关子查询。

UPDATE CarInventory  

SET StickerPrice = (SELECT MAX(StickerPrice)

                    FROM CarInventory Inner_CI

                    WHERE Inner_CI.CarName = Outer_CI.CarName)  

FROM CarInventory Outer_CI;

 

8中的代码用车名从外部查询相关子查询以确定最大的发票比率为每一个独特的车名。这个最大的发票价格被发现在相关子查询,用来更新发票价格值为每个车的存货清单记录具有相同的车名。

相关子查询的性能考虑

有一些性能方面的考虑,你应该知道在编写Transact-SQL语句包含相关子查询。当外部查询包含一个小的行数表现不坏。但它不适用从性能的角度来看当外部查询中包含大量的排时。这是因为相关子查询需要执行外部查询中的每个候选行。因此,当外部查询包含越来越多的候选行时相关子查询被执行多次,因此Transact-SQL语句将需要更长的时间来运行。如果你发现你的相关子查询的SQL语句的性能不能满足你的要求,那么你应该寻找替代解决方案,例如使用内部或外部连接操作的查询,或从外部查询一次返回一个小的值。

 

概要

相关子查询是一种内在的查询,包括从外部查询的一个或多个列。相关子查询被执行一次外部查询的每个候选行。因为一个相关子查询包含一列从外部查询不能独立运行的外部查询。相关子查询,虽然没有很好的规模从性能的角度来看,有大量的候选行排在外查询鉴定。

 

问题和答案

在这一部分中你可以回顾一下你如何理解相关子查询的概念通过回答下列问题。

 

问题1:

当写一个相关子查询你需要

a. 一个或多个列从内部查询,用来限制相关子查询结果。

b. 一个或多个列从内部查询中使用相关子查询选择列表中。

c. 一个或多个列从外部查询,用来限制相关子查询结果。

d. 一个或多个列从外部查询中使用相关子查询的选择列表中。

问题2:

选择所有陈述是真实的相关子查询。

 

a.作为候选人的行数的增加性能的Transact-SQL语句包含一个相关子查询的提高。

b.相关子查询将会执行一次外部查询对每一个候选行。

c.相关子查询将从内部查询参考一个或多个列。

d当使用一个相关子查询在HAVING子句内的查询将被执行一次的每个候选行由GROUP BY子句返回的。

问题3:

相关子查询是一个正常的查询,以及相关子查询能够独立运行的Transact-SQL语句。

 

a.真的

b.假的

答案

问题1:

正确答案是C.一个相关子查询需要从外部查询一个或多个列在相关子查询语句中使用。这些外部列引用替换成每一个候选行执行相关子查询。

问题2:

正确答案是B和D。A错在作为候选的行数增加相关子查询被执行多次,和Transact-SQL语句性能越差。C错在相关子查询必须包含从外部查询的一行或多行,不是内部查询。

问题3:

正确答案是B。如果你尝试运行相关子查询独立的完整的Transact-SQL语句,相关子查询语句会将失败。

猜你喜欢

转载自blog.csdn.net/xtt_3170707038/article/details/80304189
今日推荐