SQL Server:具有不同长度的分析窗口函数

Posted

技术标签:

【中文标题】SQL Server:具有不同长度的分析窗口函数【英文标题】:SQL Server: Analytic window function with varying lengths 【发布时间】:2020-12-16 12:14:56 【问题描述】:

我想在几个不同长度的窗口上编写一个带有聚合函数的分析函数。假设我有一张股票收盘价表,如下所示:

Ticker | TradeDate | ClosePrice | 
----------------------------------------

A1      20201209       1.1       
A1      20201208       1.2
A1      20201207       1.6
.......
A1      20191209       1.1       
A1      20191208       1.2
A1      20191207       1.6

A2      20201209       2.1       
A2      20201208       2.2
A2      20201207       2.6
.......
A2      20191209       2.1       
A2      20191208       2.2
A2      20191207       2.6

而现在我想得到这样的结果(返回的行数和原表一样):

Ticker | TradeDate | ClosePrice | Past3DaysAverage | Past1MonthAverage | Past1YearAverage

所以前三列与原始表相同。第四列,我作为参考,表示过去 3 个交易日的平均价格,直到同一股票的 TradeDate(因此有一个partition by 子句)。到目前为止,我可以编写如下查询:

select 
t.Ticker, t.TradeDate, t.ClosePrice, 
avg(t.ClosePrice) over (partition by t.Ticker order by TradeDate rows between 2 preceding and current row) as Past3DaysAverage
from PriceTable t

最后两列计算同一代码在过去 1 个月和 1 年直到 TradeDate 的平均价格。现在它很麻烦,因为我不知道如何指定不同的窗口长度(如果可能的话),因为直到不同日期的一年(或一个月)的交易日数不同,所以我不能使用类似的例程的ROWS(或RANGE)。谁能帮我解决这个问题?

非常感谢!

【问题讨论】:

【参考方案1】:

很遗憾,SQL Server 不支持 range 框架到窗口函数。最简单的方法可能是横向连接:

select t.*, t1.*, t2.*, t3.*
from pricetable t
cross apply (
    select avg(t1.closeprice) as past_3days_average
    from pricetable t1
    where 
        t1.ticker = t.ticker 
        and t1.tradedate >= dateadd(day, -3, t.tradedate)
        and t1.tradedate <= t.tradedate
) as t1
cross apply (
    select avg(t1.closeprice) as past_1month_average
    from pricetable t1
    where 
        t1.ticker = t.ticker 
        and t1.tradedate >= dateadd(month, -1, t.tradedate)
        and t1.tradedate <= t.tradedate
) t2
cross apply (
    select avg(t1.closeprice) as past_1year_average
    from pricetable t1
    where 
        t1.ticker = t.ticker 
        and t1.tradedate >= dateadd(year, -1, t.tradedate)
        and t1.tradedate <= t.tradedate
) t3

另一种方法只使用一个横向连接和条件聚合:

select t.*, t1.*
from pricetable t
cross apply (
    select 
        avg(case when t1.trade_date >= dateadd(day,   -3, t.tradedate) then closeprice end) as past_3days_average,
        avg(case when t1.trade_date >= dateadd(month, -1, t.tradedate) then closeprice end) as past_1month_average,
        avg(t1.closeprice) as past_1year_average
    from pricetable t1
    where 
        t1.ticker = t.ticker 
        and t1.tradedate >= dateadd(year, -1, t.tradedate)
        and t1.tradedate <= t.tradedate
) t1

【讨论】:

感谢您的热情回复。我尝试了您的第一个解决方案并且它有效(在第二个解决方案中出现了一些我无法弄清楚的错误)。但后来我意识到我在最初的问题中错误地简化了一些:不仅我想要过去 1 个月和 1 年的平均价格(实际上也是最大值和最小值),而且我还需要得到 percentile TradeDate 过去 1 个月和 1 年的收盘价。我认为窗口函数AVG 和“PERCENT_RANK”可以类似地应用。但CROSS APPLY 似乎并非如此?

以上是关于SQL Server:具有不同长度的分析窗口函数的主要内容,如果未能解决你的问题,请参考以下文章

SQL Server:使用具有相同 OVER 子句的多个聚合/分析函数?

SQL Server 中的窗口函数(2012 新函数)

SQL Server2008窗口计算

创建“部分”窗口函数以更新 SQL Server 中的数据

SQL Server窗口框架——ROWSRANGE

SQL Server 2008 中用户定义的排名/分析函数