【问题标题】:recursive common table expression in SQL ServerSQL Server 中的递归公用表表达式
【发布时间】:2017-12-11 11:59:01
【问题描述】:

我有一个表格,其中包含两条信息 BusinessDateDailyPerf,其中 DailyPerf 显示我正在跟踪的价格值的百分比变化。前几行数据是这样的

BusinessDate   DailyPerf 
Jan 3, 2017     -0.0356%
Jan 4, 2017     -0.4325%
Jan 5, 2017     -0.3953%
Jan 6, 2017     -0.8469%
Jan 9, 2017     -0.5050%

我要做的是计算另一列 YearFac,它显示了我剩余的初始值的当前百分比

在 Excel 中完成后,数据将如下所示:

BusinessDate DailyPerf  YearFac
Jan 2, 2017  NULL       100%
Jan 3, 2017  -0.0356%   99.9644%
Jan 4, 2017  -0.4325%   99.5321%
Jan 5, 2017  -0.3953%   99.1386%
Jan 6, 2017  -0.8469%   98.2989%
Jan 9, 2017  -0.5050%   97.8025%

因此,我需要使用 100% 的 YearFac 来初始化我的查询,然后递归地计算因子。需要注意的是,日期不一定是连续的(周末或节假日没有条目) - 所以我不能假设 next day = this day + 1 只是第二天是下一个较大的日期

我尝试了以下

WITH   cte
AS     (
        SELECT cast(1 as int) RowCnt,
               cast('3 jan 2017' as date) BusinessDate, 
               cast(1.0 as float) YearFac -- anchor member
        UNION ALL
        select  cast(ROW_NUMBER() OVER(ORDER BY p.BusinessDate ASC) + 1 as int) as RowCnt,
                p.BusinessDate,                                                       -- recursive member
                cast(cte.YearFac*(1.0 + p.DailyPerc) as float) YearFac
        from cte
        inner join dbo.MsfsDailyPnl p 
        on 
        cte.RowCnt = ROW_NUMBER() OVER(ORDER BY p.BusinessDate ASC) + 1
        where p.BusinessDate < sysutcdatetime()
       )
SELECT RowCnt,BusinessDate, YearFac
FROM   cte

但是,当然,这会失败,因为我无法引用联接中的行号。任何人都可以提出修改以使该查询正常工作吗?

【问题讨论】:

    标签: sql sql-server recursion common-table-expression


    【解决方案1】:

    使用累积和:

    select BusinessDate, DailyPerf,
           1 - exp(sum(log(1 + DailyPerf)) over (order by BusinessDate desc)
    from (select cast('2017-01-03' as date) as BusinessDate, cast(0 as float) as DailyPerf
          union all
          select BusinessDate, DailyPerf from dbo.MsfsDailyPnl
         ) p;
    

    【讨论】:

    • 我猜有一些更容易完成任务的函数:) 比如窗口函数:)
    【解决方案2】:

    使用窗口函数

    select *, sum(DailyPerf) over (order by BusinessDate) + 100 YearFac 
    from (
         select dateadd(day, -1, min(BusinessDate)) BusinessDate, 0  DailyPerf from data
         union all 
         select * from data) t
    

    dbfiddle demo

    结果

    BusinessDate        DailyPerf   YearFac
    -------------------------------------------
    02/01/2017 00:00:00 0.0000      100.0000
    03/01/2017 00:00:00 -0.0356     99.9644
    04/01/2017 00:00:00 -0.4325     99.5319
    05/01/2017 00:00:00 -0.3953     99.1366
    06/01/2017 00:00:00 -0.8469     98.2897
    09/01/2017 00:00:00 -0.5050     97.7847
    

    【讨论】:

    • 对每日价值求和与复利不同,但如果我对 log(1+return) 求和,您的答案是最简洁的
    【解决方案3】:

    以下是您可能想要尝试的方法。我还附上了表格的定义,因此您知道它的外观以及可以轻松完成任务的方法。

    declare @x table(BusinessDate date, DailyPerf float)
    insert into @x values
    ('Jan 2, 2017',     NULL),
    ('Jan 3, 2017',     -0.0356),
    ('Jan 4, 2017',     -0.4325),
    ('Jan 5, 2017',     -0.3953),
    ('Jan 6, 2017',     -0.8469),
    ('Jan 9, 2017',     -0.5050)
    
    select *,
        100 + SUM(isnull(DailyPerf,0)) over (partition by (select null) order by 
            BusinessDate rows between unbounded preceding and current row)
    from @x
    

    【讨论】:

      猜你喜欢
      • 2012-09-15
      • 1970-01-01
      • 1970-01-01
      • 2019-11-04
      • 1970-01-01
      • 2015-03-30
      • 1970-01-01
      • 2011-10-05
      • 1970-01-01
      相关资源
      最近更新 更多