什么是计算安装基数最有效的方法?

我有一个要求,在不同的“环境”多年的不同的国家/地区,通过分配给每个单位的一定的“退休率”来计算不同的出货单位的出货量。 展示位置,曲线定义和曲线分配存储在不同的数据库表中(下面的DDL和示例数据也在SQLFiddle.com上 )。 计算安装基数的公式如下:

在这里输入图像说明 其中1990年是我们拥有数据的第一年。

问题:

使用3到1600万行单位/国家/环境/年份布置组合的数据集进行这些计算需要比30秒到1分钟的目标加载/计算时间多得多的时间。

Sql Server的方法

PIVOT每年成为自己的专栏时,我会从100,000到400,000个返回的原始数据(展示位置+费率)中获得任何地方,大约需要8-15秒。 但是,如果我要通过下面的SQL语句手动计算,至less需要10分钟。

我们还尝试了一种SQL触发器解决scheme,在每次修改展示位置或费率时更新已安装的基础,但是这会使批量更新的数据库更新过于缓慢,而且也不可靠。 如果这真的是最好的select,我想这可能值得更多的调查。

Excel-VSTO方法(到目前为止,最快的方法):

这些数据最终以C#VSTO驱动的Excel工作簿结束,其中的数据是通过一系列VLOOKUPs计算的,但是当6年VLOOKUPs每个单元大约20个VLOOKUPs加载150,000个位置(大约2000万个VLOOKUPs )时,Excel崩溃。 当VLOOKUPs以较小批量完成并且公式被转换成值时,它不会崩溃,但仍然需要比一分钟计算更长的时间。

问题是:

是否有一些math或程序结构可以帮助我通过C#或SQL更有效地计算这些数据? 蛮力迭代也太慢了,所以这也不是一个选项。

 DECLARE @Placements TABLE ( UnitId int not null, Environment varchar(50) not null, Country varchar(100) not null, YearColumn smallint not null, Placement decimal(18,2) not null, PRIMARY KEY (UnitId, Environment, Country, YearColumn) ) DECLARE @CurveAssignments TABLE ( UnitId int not null, Environment varchar(50) not null, Country varchar(100) not null, YearColumn smallint not null, RateId int not null, PRIMARY KEY (UnitId, Environment, Country, YearColumn) ) DECLARE @CurveDefinitions TABLE ( RateId int not null, YearOffset int not null, Rate decimal(18,2) not null, PRIMARY KEY (RateId, YearOffset) ) INSERT INTO @Placements ( UnitId, Country, YearColumn, Environment, Placement ) VALUES ( 1, 'United States', 1991, 'Windows', 100 ), ( 1, 'United States', 1990, 'Windows', 100 ) INSERT INTO @CurveAssignments ( UnitId, Country, YearColumn, Environment, RateId ) VALUES ( 1, 'United States', 1991, 'Windows', 1 ) INSERT INTO @CurveDefinitions ( RateId, YearOffset, Rate ) VALUES ( 1, 0, 1 ), ( 1, 1, 0.5 ) SELECT P.UnitId, P.Country, P.YearColumn, P.Placement * ( SELECT Rate FROM @CurveDefinitions CD INNER JOIN @CurveAssignments CA ON CD.RateId = CA.RateId WHERE CA.UnitId = P.UnitId AND CA.Environment = P.Environment AND CA.Country = P.Country AND CA.YearColumn = P.YearColumn - 0 AND CD.YearOffset = 0 ) + ( SELECT Placement FROM @Placements PP WHERE PP.UnitId = P.UnitId AND PP.Environment = P.Environment AND PP.Country = P.Country AND PP.YearColumn = P.YearColumn - 1 ) * ( SELECT Rate FROM @CurveDefinitions CD INNER JOIN @CurveAssignments CA ON CD.RateId = CA.RateId WHERE CA.UnitId = P.UnitId AND CA.Environment = P.Environment AND CA.Country = P.Country AND CA.YearColumn = P.YearColumn AND CD.YearOffset = 1 ) [Installed Base - 1993] FROM @Placements P WHERE P.UnitId = 1 AND P.Country = 'United States' AND P.YearColumn = 1991 AND P.Environment = 'Windows' 

作出以下回应:

我们还尝试了一种SQL触发器解决scheme,在每次修改展示位置或费率时更新已安装的基础,但是这会使批量更新的数据库更新过于缓慢,而且也不可靠。 如果这真的是最好的select,我想这可能值得更多的调查。

你听说过SQL Service Broker吗? 它确实做得很好的一件事就是允许你为asynchronous处理排队数据。 如果触发器本身速度太慢,则可以使用触发器将logging排队以进行asynchronous处理。

看起来这可能会成为一个问题的问题导致正确的答案。 事实certificate,答案主要在于我上面给出的查询,这是完全没有效率的。 我已经能够获得我正在寻找的附近加载时间只是优化查询如下。

 SELECT P.UnitId, P.Country, P.YearColumn, P.Environment, P.Placement, sum(IBP.Placement * FRR.Rate) InstalledBase FROM @Placements P INNER JOIN @Placements IBP ON P.UnitId = IBP.UnitId AND P.Country = IBP.Country AND P.Environment = IBP.Environment AND P.YearColumn >= IBP.YearColumn INNER JOIN @CurveAssignments RR ON IBP.UnitId = RR.UnitId AND IBP.Country = RR.Country AND IBP.Environment = RR.Environment AND IBP.YearColumn = RR.YearColumn INNER JOIN @CurveDefinitions FRR ON Rr.RateId = FRR.RateId AND P.YearColumn - IBP.YearColumn = FRR.YearOffset GROUP BY P.UnitId, P.YearColumn, P.Country, P.Environment, P.Placement