3

假设我有以下架构:

-- 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 不允许在此处使用变量。

4

1 回答 1

6

正如您所提到的,SQL Server 仅支持 OVER 子句中的 PRECEDING 和 FOLLOWING 的整数文字。

有两个选项可供您使用:动态 sql 和重写查询以不使用 PRECEDING

动态 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
于 2013-01-26T06:25:27.923 回答