2

当使用带有窗口聚合函数的 RANGE 选项时,我在 SQL Server 2012 中遇到了一些有趣的行为,并且不确定这是 SQL Server 2012 的错误还是“功能”。我有一个定义如下的表:

CREATE TABLE [Test].[Trades](
    [ID] [int] IDENTITY(1,1) NOT NULL,
    [Member] [varchar](20) NOT NULL,
    [TradeDate] [date] NOT NULL,
    [Fund] [varchar](4) NOT NULL,
    [Units] [decimal](28, 8) NOT NULL,
    PRIMARY KEY CLUSTERED 
    (
        [ID] ASC
    )
);

此表存储成员在特定交易日期在基金中进行的交易。会员可以在给定日期对给定基金进行 >1 次交易。除了聚集索引之外,我还有一个非聚集索引,定义如下:

CREATE NONCLUSTERED INDEX [Ix_TradesIndex] ON [Test].[Trades]
(
    [Member] ASC,
    [Fund] ASC,
    [TradeDate] ASC
)
INCLUDE ([Units]);

如果我想查询数据集以提供每个成员在每个基金中的运行总单位数,那么使用 SQL Server 2012 中窗口聚合的扩展,我可以回答如下问题:

SELECT T.Member, T.Fund, T.TradeDate, 
SUM(T.Units) OVER(PARTITION BY T.Member, T.Fund
            ORDER BY T.TradeDate
            RANGE BETWEEN UNBOUNDED PRECEDING 
            AND CURRENT ROW) AS TotalShares
FROM Test.Trades AS T;

这将给我一个类似于下面的数据集(示例显示在 2005-02-03 在 Fund2 进行 >1 交易的成员):

……

Member1, Fund1, 2005-03-31, 0.00

会员1,基金2,2005-02-03,3256.50

会员1,基金2,2005-02-03,3256.50

……

RANGE 选项确保在排序条款不是唯一的(即,给定成员在特定交易日期对给定基金进行了多次交易)的情况下,窗口包括范围顶部的所有重复行。这按预期正常工作。但是,如果我想说“现在只给我这个集合中不同的行”(即去掉重复的条目),提出这个问题的一种方法如下:

SELECT DISTINCT T.Member, T.Fund, T.TradeDate, T.TotalShares
FROM
(
    SELECT T.Member, T.Fund, T.TradeDate, 
        SUM(T.Units) OVER(PARTITION BY T.Member, T.Fund
                ORDER BY T.TradeDate
                RANGE BETWEEN UNBOUNDED PRECEDING 
                AND CURRENT ROW) AS TotalShares
    FROM Test.Trades AS T 
) AS T;

这里的事情变得有趣:我看到的是,如果计划并行,那么结果集是不确定的(即查询给出错误的答案,查询返回的行数可以在后续运行中改变)查询)。 如果计划不并行(我显然可以通过指定 OPTION(MAXDOP 1) 来强制执行),那么查询总是返回相同数量的行,如果结果集“正确”,则返回结果集。对我来说,这感觉就像 SQL Server 2012 中的一个错误。

我的问题是'<strong>是否有人对此行为有其他解释,或者这是一个错误?'</p>

4

1 回答 1

1

因此RANGE使用磁盘工作表进行假脱机,同时ROWS使用内存(如果可能)。我会尝试换掉,RANGE然后ROWS看看是否:

  1. 语义仍然相同(例如,您得到预期的结果)
  2. 计划不会以同样的方式改变(例如,你总是得到预期的结果)

对于某些查询,它们可以给出相同的语义,而对于其他查询,它实际上可以改变潜在的含义,所以我再次强调你应该测试这个改变。如果语义相同,我几乎可以保证性能会提高(并且您的计划偏差的可能性会下降)。

我不是在争论您发现的行为是否是错误,只是建议您可能能够解决它的方法。它可能会阻止计划并行执行,因为与磁盘和内存的交互不同。

我很想进一步研究这个问题,你可以在某处发布数据填充脚本或备份(显然没有任何专有/私有数据)来更深入地研究这个吗?

于 2012-03-01T22:42:35.020 回答