我正在尝试使用窗口和CTE来使用“Quirky Update”来替代“运行性能”的复杂计算。运行性能的快速数学运算是((1 +运行)*(1 +每日)) - 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
答案 0 :(得分: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