无法理解运行聚合的工作原理

时间:2016-11-10 09:29:53

标签: sql sql-server

首先让我向您介绍一个例子,之后我会问这个问题。 代码

SELECT
    orderyear
   ,qty
FROM
    Sales.OrderTotalsByYear;

给了我一个看起来像这样的表

orderyear   qty
----------- -----------
2007        25489
2008        16247
2006        9581

我需要每年返回多年来的订单年份,数量和运行总量。也就是说,对于每年,返回到该年份的数量之和。因此,对于视图(2006年)中记录的最早年份,运行总数等于该年份的数量。第二年(2007年),运行总数是第一年加第二年的总和,依此类推。 代码看起来像这样

SELECT
    orderyear
   ,qty
   ,(
        SELECT
            SUM(O2.qty)
        FROM
            Sales.OrderTotalsByYear AS O2
        WHERE
            O2.orderyear <= O1.orderyear
    )
    AS runqty
FROM
    Sales.OrderTotalsByYear AS O1
ORDER BY
    orderyear;

和一个表

orderyear   qty         runqty
----------- ----------- -----------
2006        9581        9581
2007        25489       35070
2008        16247       51317

现在,我明白了这段代码的作用,但我不明白 HOW 它做到了。我有程序和面向对象编程的经验,但这只会让我发疯。 如果查询以这样的方式进行

  
      
  1. FROM
  2.   
  3. WHERE
  4.   
  5. GROUP BY
  6.   
  7. HAVING
  8.   
  9. 选择
  10.   
  11. ORDER BY
  12.   

然后如何通过使用内部和外部SELECT之间的关系来设法将内部SELECT组合在外部SELECT中?外部SELECT首先运行,当它遇到表中的第一个元素时它会停止,然后内部SELECT开始运行 O2.orderyear&lt; = O1.orderyear 为真的元素吗?或者是否有一些完全不同的东西?

2 个答案:

答案 0 :(得分:1)

逻辑,对于系统生成的每一行(通过FROMWHEREGROUP BYHAVING),系统将评估SELECT子句。作为每行评估的一部分,系统将评估相关子查询:

(
    SELECT
        SUM(O2.qty)
    FROM
        Sales.OrderTotalsByYear AS O2
    WHERE
        O2.orderyear <= O1.orderyear
)

使用当前行的O1.orderyear值。

但是,从实用立场 1 ,系统可能能够优化其对此子查询的评估。足够聪明的优化器,如果统计数据表明它值得做,可能会决定以orderyear顺序评估外部查询,并创建OrderTotalsByYear表中的orderyear表的副本。 order(或使用已经表示此排序顺序的索引)。在这种情况下,系统将能够评估此子查询结果,而无需为外部查询的每一行重新扫描整个OrderTotalsByYear表。

优化程序所做的只能通过获取执行计划来确定,并且将取决于您的特定表 - 它们的结构,索引以及它们中包含的数据。

1 SQL是根据逻辑处理顺序定义的。实现可以按照与逻辑处理顺序不同的顺序执行操作,前提是它们产生的结果与逻辑处理顺序遵循 2 时获得的结果相同。通常,SQL也定义为处理集而不是指定逐行或从左到右的处理。

2 SQL Server在这里采取了比它应该更多的自由,并且可能产生错误,这些错误不会生成 它遵循逻辑处理顺序。哼哼。

答案 1 :(得分:0)

您可以通过两个步骤来考虑查询:select子句中的子查询是相关的,即它通过O2.orderyear <= O1.orderyear引用主查询。因此主要查询被执行,并且对于从表中读取的每个单个记录(别名为O1),它执行子查询以获得运行数量。

因此,当主查询读取2008的记录时,它会执行

SELECT SUM(O2.qty)
FROM Sales.OrderTotalsByYear AS O2
WHERE O2.orderyear <= 2008

有两个记录,其中一年&lt; = 2008:2007和2008.他们的数量正在被添加,因此您可以获得与2008年记录一起显示的运行总计。 2007年和2009年的记录也是如此。最后,您的行是按顺序排列的,以便按升序显示年份。