3

我正在尝试使用窗口和 CTE 来使用“Quirky Update”的替代方法来计算“运行性能”的复杂性。运行性能的快速计算是 ((1 + Running) * (1 + Daily)) - 1。此运行需要为当前行更新,然后用于下一行计算。当 AssetID 更改时,它会重置为 0。我发现的唯一替代方法是使用 exp(sum(log)) (显示在代码示例中),虽然数字在现实世界中是正确的,但使用起来太慢了,使用 Quirky Update 几乎是瞬时的。在我的示例中,我用窗口代替了每日计算,但是因为运行需要更新当前行并将其反映在下一行计算中,所以我想不出使用窗口和 CTE 来处理运行计算的方法。所以我的尝试是在类似于 Quirky Update 的更新语句中使用变量,它们是错误的。也许有一种类似于我每天计算的方式?在此先感谢,罗伯

if OBJECT_ID('tempdb..#DMVRunningPerformance') is not null
    drop table #DMVRunningPerformance

create table #DMVRunningPerformance (
    LongPosition_bt bit null,
    AssetID_in int null,
    Symbol_vc varchar(255),
    DayID_in int,
    Status_vc varchar(255) null,
    MV float,
    DailyPerf float null,
    RunningPerf float null)

    CREATE CLUSTERED INDEX [indexdown] ON #DMVRunningPerformance 
        (
            [LongPosition_bt] ASC,
            [AssetID_in] ASC,
            [DayID_in] DESC
        )
    WITH (SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF) ON [PRIMARY]

insert into #DMVRunningPerformance  
 values (1,100,'IBM',75006,NULL,201048.8987,NULL,NULL)
insert into #DMVRunningPerformance  
 values (1,100,'IBM',75005,NULL,200841.5658,NULL,NULL)
insert into #DMVRunningPerformance  
 values (1,100,'IBM',75004,NULL,200321.7043,NULL,NULL)
insert into #DMVRunningPerformance  
 values (1,100,'IBM',75003,NULL,201120.0467,NULL,NULL)
insert into #DMVRunningPerformance  
 values (1,100,'IBM',75002,NULL,201779.8805,NULL,NULL)
insert into #DMVRunningPerformance  
 values (1,100,'IBM',75001,NULL,201651.3917,NULL,NULL)
insert into #DMVRunningPerformance  
 values (1,100,'IBM',75000,NULL,201101.0320,NULL,NULL)
insert into #DMVRunningPerformance  
 values (1,200,'MSFT',75006,NULL,805048.8987,NULL,NULL)
insert into #DMVRunningPerformance  
 values (1,200,'MSFT',75005,NULL,801841.5658,NULL,NULL)
insert into #DMVRunningPerformance  
 values (1,200,'MSFT',75004,NULL,804321.7043,NULL,NULL)
insert into #DMVRunningPerformance  
 values (1,200,'MSFT',75003,NULL,809120.0467,NULL,NULL)
insert into #DMVRunningPerformance  
 values (1,200,'MSFT',75002,NULL,801779.8805,NULL,NULL)
insert into #DMVRunningPerformance  
 values (1,200,'MSFT',75001,NULL,801151.3917,NULL,NULL)
insert into #DMVRunningPerformance  
 values (1,200,'MSFT',75000,NULL,801901.0320,NULL,NULL)

-------------------  BEGIN Quirky Update   -------------------------------------    
declare @RunningPerformance as float=0
declare @AssetID_in int=-1,
        @DayID_in int=0,
        @LongPosition_bt bit=0,
        @Status_vc varchar(255)=NULL,
        @NewGroup_bt bit=1,
        @DailyPerformance float,
        @NDMarketValue float,
        @PDMarketValue float=0,
        @NDSymbol_vc varchar(255)='XXXXXXXXXX',
        @PDSymbol_vc varchar(255)='XXXXXXXXXX',
        @NDDayID_in int,
        @PDDayID_in int,
        @NDAssetID_in int=-1,
        @PDAssetID_in int=-1

update #DMVRunningPerformance
    set 
        @LongPosition_bt=LongPosition_bt,
        @NewGroup_bt=CASE WHEN @AssetID_in<>AssetID_in or @LongPosition_bt<>LongPosition_bt THEN 1 ELSE 0 END,
        @DailyPerformance=CASE WHEN @NewGroup_bt=1 THEN 0 ELSE
                            (@NDMarketValue-MV)/MV END,
        @RunningPerformance=CASE WHEN @NewGroup_bt=1 THEN 0 ELSE
                            ((1 + @RunningPerformance) * (1 + @DailyPerformance)) - 1 END
        ,DailyPerf=@DailyPerformance
        ,RunningPerf=@RunningPerformance
        ,@AssetID_in=AssetID_in
        ,@NDAssetID_in=AssetID_in
        ,@NDSymbol_vc=Symbol_vc
        ,@NDDayID_in=DayID_in
        ,@NDMarketValue=MV
FROM #DMVRunningPerformance OPTION (MAXDOP 1)

select 'this data is correct'
select * from #DMVRunningPerformance
-------------------  END Quirky Update   -------------------------------------  

-- reset performance
update #DMVRunningPerformance set DailyPerf=null, RunningPerf=NULL

-------------------  BEGIN windowing and CTE   -------------------------------------    
-- update daily perf with CTE
;WITH dailyCTE as
    (
    select LongPosition_bt,AssetID_in,Symbol_vc,DayID_in,PreviousAssetID,
        MV,
        DailyPerformance = CASE WHEN YesterdayMV IS NULL THEN 0 
                            ELSE (YesterdayMV - MV) / MV END
    FROM
        (
            select p.LongPosition_bt,p.AssetID_in,p.Symbol_vc,p.DayID_in,
                p.MV,
                lag(p.MV,1) over (Partition by LongPosition_bt,AssetID_in
                                    order by DayID_in desc) as YesterdayMV,
                lag(p.AssetID_in,1) over (Partition by LongPosition_bt,AssetID_in
                                    order by DayID_in desc) as PreviousAssetID
                from #DMVRunningPerformance p
        ) p1
    )
    Update #DMVRunningPerformance 
        set DailyPerf=dailyCTE.DailyPerformance,
        @NewGroup_bt=CASE WHEN PreviousAssetID is null THEN 1 ELSE 0 END,
        @RunningPerformance=CASE WHEN PreviousAssetID is null THEN DailyPerformance ELSE
                            ((1 + @RunningPerformance) * (1 + DailyPerformance)) - 1 END,
        RunningPerf=@RunningPerformance
        from dailyCTE join #DMVRunningPerformance d
            on dailyCTE.LongPosition_bt=d.LongPosition_bt and dailyCTE.AssetID_in=d.AssetID_in and dailyCTE.DayID_in=d.DayID_in

select 'this data daily is correct, running is incorrect'

select * from #DMVRunningPerformance

-- alternate method for running perf
-- works but for realworld data takes orders of magnitude longer than quirky update
if 1=2
select 
    dr.AssetID_in,dr.LongPosition_bt,dr.DayID_in,dr.MV,dr.DailyPerf-1 as DailyPerformance,
    RunningPerformance_fl = exp((
                    select
                        sum(log(DailyPerf))
                    from
                        #DMVRunningPerformance
                    where
                        AssetID_in=dr.AssetID_in and LongPosition_bt=dr.LongPosition_bt and
                        DayID_in >= DR.DayID_in)) - 1
 from #DMVRunningPerformance dr
    order by
        dr.assetid_in,DR.DayID_in desc

-------------------  END windowing and CTE   -------------------------------------  



drop table #DMVRunningPerformance
4

1 回答 1

0

这很容易使用递归 CTE。下面的实现是一个快速而肮脏的版本,因此您可以删除一些额外/冗余的列。(DailyPerf2 和 RunningPerf2 是新结果。)

;with orderedRows as
(
    select *, row_number() over (partition by AssetId_in order by DayId_in desc) as rn
    from #DMVRunningPerformance
), grouped as
(
    select *, cast(0.0 as float) as DailyPerf2, cast(0.0 as float) as RunningPerf2
    from orderedRows
    where rn = 1

    union all

    select r.*
        , cast((prevR.MV - r.MV) / r.MV as float)
        , cast(((1 + prevR.RunningPerf2) * (1 + (prevR.MV - r.MV) / r.MV)) - 1 as float)
    from orderedRows r
    inner join grouped prevR on r.AssetId_in = prevR.AssetID_in and r.rn = prevR.rn + 1
)
select * 
from grouped
order by AssetID_in, DayID_in desc
于 2016-02-19T17:26:46.913 回答