2013-01-24 73 views
3

滑動功能說我有以下模式:SQL Server 2012中 - 使用OVER

-- 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不允許在這裏使用變量。

回答

6

如上所述,SQL Server僅支持OVER子句中的PRECEDING和FOLLOWING的整數文字。

有提供給您兩種選擇:動態SQL和重寫查詢不使用前茬

動態SQL是simpliest,但我會小心把它在一個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 
+1

聰明的解決方法,但要求「看中語法」窗口的功能是有點不公平。使用windows函數,查詢使用'windows spool'來完成移動窗口的工作,並且由於所有聚合的over子句都是相同的,所以只會有一個表掃描(如果有覆蓋索引,則爲索引掃描)。您的解決方法將有6個表/索引掃描。巨大的差異。 –

+0

我認爲將PRECEDING當作花式語法的一點是,可以在不使用語法的情況下編寫一個邏輯上等效的查詢。我還添加了另外一種方法來重新編寫查詢以減少掃描次數。是的,在使用PRECEDING時,Sql可以提供更好的物理計劃,但邏輯上查詢是相同的。 – StrayCatDBA

+0

良好的解決方法,但仍然因爲額外的開銷而痛苦。我想知道這是否可能與一個可變的窗口規範...可能不是。但在某些情況下,例如年初至今的計算,這樣的滑動窗口規範將是非常受歡迎的。 –