使用派生表的查询相对于不使用它们的查询有什么优势?

Asp*_*Net 4 sql t-sql sql-server

我知道如何使用派生表,但我仍然无法真正看到使用它们的任何真正优势.

例如,在下面的文章http://techahead.wordpress.com/2007/10/01/sql-derived-tables/中,作者尝试使用派生表在没有示例的情况下使用派生表显示优势,我们想要生成一份报告,显示每个客户在1996年下达的订单总数,我们希望这个结果集包括所有客户,包括那些没有下订单的客户和那些从未放置任何订单的客户.订单(他使用Northwind数据库).

但是当我比较两个查询时,我没有看到使用派生表的查询的任何优点(如果没有别的,使用派生表似乎不会简化我们的代码,至少在这个例子中不是这样):

常规查询:

SELECT C.CustomerID, C.CompanyName, COUNT(O.OrderID) AS TotalOrders
FROM Customers C LEFT OUTER JOIN Orders O ON
       C.CustomerID = O.CustomerID AND YEAR(O.OrderDate) = 1996
GROUP BY C.CustomerID, C.CompanyName
Run Code Online (Sandbox Code Playgroud)

使用派生表进行查询:

SELECT C.CustomerID, C.CompanyName, COUNT(dOrders.OrderID) AS TotalOrders
FROM Customers C LEFT OUTER JOIN
        (SELECT * FROM Orders WHERE YEAR(Orders.OrderDate) = 1996) AS dOrders
     ON
        C.CustomerID = dOrders.CustomerID
GROUP BY C.CustomerID, C.CompanyName
Run Code Online (Sandbox Code Playgroud)

也许这只是一个很好的例子,你能告诉我一个例子,派生表的好处更明显吗?

感谢名单

回复GBN:

在这种情况下,如果客户和产品之间没有关系,则无法捕获产品和订单聚合.

你能详细说明一下你的意思吗?以下查询不会生成与查询相同的结果集:

SELECT 
     C.CustomerID, C.CompanyName,
     COUNT(O.OrderID) AS TotalOrders,
     COUNT(DISTINCT P.ProductID) AS DifferentProducts 
FROM Customers C LEFT OUTER JOIN Orders O ON
       C.CustomerID = O.CustomerID AND YEAR(O.OrderDate) = 1996
   LEFT OUTER JOIN Products P ON 
       O.somethingID = P.somethingID  
GROUP BY C.CustomerID, C.CompanyName
Run Code Online (Sandbox Code Playgroud)

回复CADE ROUX:

此外,如果表达式用于从具有大量共享中间计算的派生列派生列,则一组嵌套派生表或堆叠CTE是唯一的方法:

SELECT x, y, z1, z2
FROM (
    SELECT *
           ,x + y AS z1
           ,x - y AS z2
    FROM (
        SELECT x * 2 AS y
        FROM A
    ) AS A
) AS A
Run Code Online (Sandbox Code Playgroud)

以下查询不会产生与上述查询相同的结果:

SELECT x, x * 2 AS y, x + x*2 AS z1, x - x*2 AS z2
FROM A
Run Code Online (Sandbox Code Playgroud)

Jus*_*ant 5

我通常使用派生表(或CTE,它有时是SQL 2005/2008中派生查询的高级替代)来简化读取和构建查询,或者在SQL不允许我执行特定操作的情况下.

例如,如果没有派生表或CTE,则无法执行的操作之一是在WHERE子句中放置聚合函数.这不起作用:

SELECT  name, city, joindate
FROM    members 
        INNER JOIN cities ON cities.cityid = derived.cityid
WHERE   ROW_NUMBER() OVER (PARTITION BY cityid ORDER BY joindate) = 1
Run Code Online (Sandbox Code Playgroud)

但这会奏效:

SELECT  name, city, joindate
FROM    
( 
    SELECT  name, 
            cityid,
            joindate,
            ROW_NUMBER() OVER (PARTITION BY cityid ORDER BY joindate) AS rownum 
    FROM    members 
) derived INNER JOIN cities ON cities.cityid = derived.cityid
WHERE   rn = 1
Run Code Online (Sandbox Code Playgroud)

高级警告,特别是对于大规模分析

如果您正在处理相对较小的数据集(非千兆字节),您可能会在这里停止阅读.如果您正在使用千兆字节或数TB的数据并使用派生表,请继续阅读...

对于非常大规模的数据操作,有时最好是创建临时表而不是使用派生查询.如果SQL的统计信息表明派生的查询将返回比查询实际返回的行多得多的行,则可能会发生这种情况,这种情况比您想象的更频繁.主查询与非递归CTE自联接的查询也存在问题.

衍生表也可能会生成意外的查询计划.例如,即使您在派生表中放置了严格的WHERE子句以使该查询具有选择性,SQL Server也可能会重新排序您的查询计划,以便在查询计划中评估您的WHERE子句.有关此问题的讨论和解决方法,请参阅此Microsoft Connect反馈.

因此,对于性能密集型查询(尤其是100GB +表上的数据仓库查询),我总是喜欢对临时表解决方案进行原型设计,以确定您是否获得了比从派生表或CTE获得的更好的性能.这似乎是违反直觉的,因为您执行的I/O比理想的单一查询解决方案更多,但使用临时表可以完全控制所使用的查询计划以及每个子查询的评估顺序.有时,这可以将性能提高10倍或更多.

在我必须使用查询提示来强制SQL执行我想要的操作的情况下,我也倾向于更喜欢临时表 - 如果SQL优化器已经"行为不端",临时表通常是一种更明确的方式来强制它们采取行动的方式你要.

我不是说这是一个常见的情况 - 大多数情况下临时表解决方案至少会更糟糕,有时查询提示是唯一的追索权.但是,不要假设CTE或派生查询解决方案也是您最快的选择.测试,测试,测试!


Cad*_*oux 5

在您的示例中,派生表不是严格必需的.在许多情况下,您可能需要加入聚合或类似,并且派生表实际上是处理它的唯一方法:

SELECT *
FROM A
LEFT JOIN (
    SELECT x, SUM(y)
    FROM B
    GROUP BY x
) AS B
    ON B.x = A.x
Run Code Online (Sandbox Code Playgroud)

此外,如果表达式用于从具有大量共享中间计算的派生列派生列,则一组嵌套派生表或堆叠CTE是唯一的方法:

SELECT x, y, z1, z2
FROM (
    SELECT *
           ,x + y AS z1
           ,x - y AS z2
    FROM (
        SELECT x * 2 AS y
        FROM A
    ) AS A
) AS A
Run Code Online (Sandbox Code Playgroud)

就可维护性而言,使用堆叠的CTE或派生表(它们基本上是等效的)并且可以提供更易读和可维护的代码,以及促进剪切和粘贴重用和重构.优化器通常可以非常容易地变平.

我通常使用堆叠CTE而不是嵌套以获得更好的可读性(相同的两个示例):

WITH B AS (
    SELECT x, SUM(y)
    FROM B
    GROUP BY x
)
SELECT *
FROM A
LEFT JOIN B
    ON B.x = A.x

WITH A1 AS (
    SELECT x * 2 AS y
    FROM A
)
,A2 AS (
    SELECT *
           ,x + y AS z1
           ,x - y AS z2
    FROM A1
)
SELECT x, y, z1, z2
FROM A2
Run Code Online (Sandbox Code Playgroud)

关于你的问题:

SELECT x, x * 2 AS y, x + x*2 AS z1, x - x*2 AS z2 
FROM A 
Run Code Online (Sandbox Code Playgroud)

这有x*2代码重复3次.如果需要更改此业务规则,则必须在3个位置进行更改 - 注入缺陷的方法.只要您有中间计算需要保持一致并且只在一个地方定义,这就会变得复杂.

如果可以内联SQL Server的标量用户定义函数(或者如果它们执行得可行),这不会是一个问题,您可以简单地构建UDF来堆叠结果,优化器将消除冗余调用.不幸的是,SQL Server的标量UDF实现无法很好地处理大型行集.