Sql server 2012 SQL Server 2012-使用OVER的滑动函数

Sql server 2012 SQL Server 2012-使用OVER的滑动函数,sql-server-2012,Sql Server 2012,假设我有以下模式: -- Create the dbo.Transaction table CREATE TABLE [dbo].[Transaction] ( [TransactionId] INT NOT NULL IDENTITY, [AccountId] INT NOT NULL, [TransactionDate] DateTime2(7) NOT NULL, [Amount] decimal(9,3) NOT NULL CONSTRAINT [

假设我有以下模式:

-- Create the dbo.Transaction table
CREATE TABLE [dbo].[Transaction] (
    [TransactionId] INT NOT NULL IDENTITY,
    [AccountId] INT NOT NULL,
    [TransactionDate] DateTime2(7) NOT NULL,
    [Amount] decimal(9,3) NOT NULL
    CONSTRAINT [PK_Transaction] PRIMARY KEY ([TransactionId])
);
以及以下查询:

Select
    AccountId,
    TransactionDate,
    Amount,
    AverageAmount       = Avg(Amount)   Over (Partition By AccountId Order By TransactionDate ROWS BETWEEN 2 PRECEDING AND CURRENT ROW),
    TransactionCount    = Count(Amount) Over (Partition By AccountId Order By TransactionDate ROWS 2 PRECEDING),
    MinimumAmount       = Min(Amount)   Over (Partition By AccountId Order By TransactionDate ROWS 2 PRECEDING),
    MaximumAmount       = Max(Amount)   Over (Partition By AccountId Order By TransactionDate ROWS 2 PRECEDING),
    SumAmount           = Sum(Amount)   Over (Partition By AccountId Order By TransactionDate ROWS 2 PRECEDING)
From dbo.[Transaction]
Order By AccountId, TransactionDate

如果查询包含在UDF或存储过程中,并且滑动间隔(在本例中为2)直到运行时才作为参数传递给UDF/存储过程,我将如何执行该查询?SQL 2012似乎不允许在此处使用变量。

如您所述,SQL Server只支持前面和后面in-OVER子句的整数文本

有两个选项可供选择:动态sql和重新编写查询以不使用前面的语句

动态sql是最简单的,但我会小心地将其放在UDF中

set @sql = N'Select AccountId, ... ROWS ' 
          + cast(@sz as varchar(10)) + N' PRECEDING) ...'
exec sp_executesql @sql
然而,窗口函数只是花哨的语法。您可以在不使用它们的情况下重新编写查询:

DECLARE @sz INT
SET @sz = 2

;
WITH    q AS ( SELECT   AccountId ,
                        TransactionDate ,
                        Amount ,
                        ROW_NUMBER() OVER ( PARTITION BY AccountId 
                                            ORDER BY TransactionDate ) rw
               FROM     [Transaction]
             )
    SELECT  accountID ,
            TransactionDate ,
            Amount ,
            ( SELECT    AVG(q1.Amount) FROM  q q1
              WHERE     q1.accountid = q.accountid
                        AND q1.rw BETWEEN q.rw - @sz AND q.rw
            ) AverageAmount,
            ( SELECT    COUNT(q1.Amount) FROM  q q1
              WHERE     q1.accountid = q.accountid
                        AND q1.rw BETWEEN q.rw - @sz AND q.rw
            ) TransactionAmount
            -- etc.
            FROM q
            ORDER BY AccountID, TransactionDate
这里还有另一种重新编写查询的方法:

DECLARE @sz INT
SET @sz = 2;
WITH    q AS ( SELECT   AccountId ,
                        TransactionDate ,
                        Amount ,
                        ROW_NUMBER() OVER ( PARTITION BY AccountId 
                                            ORDER BY TransactionDate ) rw
               FROM     [Transaction]
             )
    SELECT  q.accountID ,
            q.TransactionDate ,
            q.Amount ,
            AVG(q1.Amount) AverageAmount ,
            COUNT(q1.Amount) TransactionAmount ,
            MAX(q1.Amount) MaxAmount ,
            MIN(q1.Amount) MinAmount
                -- etc.
    FROM    q
            INNER JOIN q q1 ON q1.accountid = q.accountid
                               AND q1.rw BETWEEN q.rw - @sz AND q.rw
    GROUP BY q.accountid ,
            q.transactiondate ,
            q.amount

聪明的解决方法,但调用窗口函数来实现“奇特的语法”有点不公平。对于windows函数,查询使用一个
windows spool
来执行移动窗口的工作,并且由于over子句对于所有聚合都是相同的,因此只有一个表扫描(如果有覆盖索引,则为索引扫描)。您的解决方案将有6个表/索引扫描。巨大的区别。我将前面提到的语法称为花哨的语法的观点是,不使用语法就可以编写逻辑上等价的查询。我还添加了一种额外的方法来重新编写查询,以减少扫描次数。是的,当使用上一个查询时,Sql可能会提供更好的物理计划,但逻辑上查询是相同的。解决方法很好,但由于额外的开销,仍然很痛苦。我想知道这是否可能与一个可变的窗口规范。。。可能不会。但在某些情况下,例如年初至今的计算,这样的滑动窗口规范将非常受欢迎。