C# 计算安装基数的最有效方法是什么?
我有一项要求,即根据分配给每个装置的一组特定“退役率”,计算多年来在不同“环境”的不同国家/地区进行不同放置/装运的装置的安装基数。放置、曲线定义和曲线指定存储在不同的数据库表中(下面的DDL和示例数据也处于打开状态)。装机基数计算公式如下: 其中,1990年是我们有安置数据的第一年 问题是: 使用300万到1600万行单位/国家/环境/年份布局组合的数据集进行这些计算所需的时间远远超过30秒到1分钟的目标负载/计算时间 Sql Server方法 当C# 计算安装基数的最有效方法是什么?,c#,sql,excel,optimization,sql-server-2008-r2,C#,Sql,Excel,Optimization,Sql Server 2008 R2,我有一项要求,即根据分配给每个装置的一组特定“退役率”,计算多年来在不同“环境”的不同国家/地区进行不同放置/装运的装置的安装基数。放置、曲线定义和曲线指定存储在不同的数据库表中(下面的DDL和示例数据也处于打开状态)。装机基数计算公式如下: 其中,1990年是我们有安置数据的第一年 问题是: 使用300万到1600万行单位/国家/环境/年份布局组合的数据集进行这些计算所需的时间远远超过30秒到1分钟的目标负载/计算时间 Sql Server方法 当PIVOTed使每年都成为自己的列时,我得到
PIVOT
ed使每年都成为自己的列时,我得到了从100000到400000行返回的原始数据(放置+速率),这大约需要8-15秒。然而,若我要通过下面包含的SQL语句手动计算,至少需要10分钟
我们还尝试了一种SQL触发器解决方案,该解决方案在每次修改位置或速率时都会更新安装基数,但这会使数据库更新在批更新时不合理地慢,而且也不可靠。如果这真的是最好的选择,我想这可能值得更多的调查
Excel VSTO方法(迄今为止,最快的方法):
这些数据最终会出现在一个以C#VSTO为动力的Excel工作簿中,在该工作簿中,数据是通过一系列的VLOOKUPs
计算出来的,但当在6年内以每个单元格20个VLOOKUPs
加载150000个位置时(约2000万VLOOKUPs
),Excel崩溃。当VLOOKUPs
以较小的批量完成,并将公式转换为值时,虽然不会崩溃,但计算时间仍要比一分钟长得多
问题是:
是否有一些数学或编程结构可以帮助我通过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触发器解决方案,该解决方案更新了已安装的 每次修改位置或速率时的基准,但这使 数据库更新在批处理更新时不合理地慢,并且 不可靠的。如果这是事实,我想这可能值得更多的调查 这确实是最好的选择
你听说过吗?它做得很好的一件事是允许您将数据排队以进行异步处理。如果触发器本身太慢,您可以使用触发器将记录排队以进行异步处理。看起来,在这种情况下,提问可能会得到正确的答案。事实证明,答案主要在于我上面给出的查询,这是完全没有效率的。通过如下优化查询,我已经能够获得我正在寻找的附近的加载时间
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
这听起来像是我通常保存在单独表格中的汇总数据。该表可以每天更新,也可以在其他适当的时间间隔内使用计划作业进行更新。这些数据需要“实时”吗?或者在计算中有一些延迟是可以接受的吗?当数据透视使每年都成为自己的列时,我可以得到100000到400000行返回的原始数据(放置+费率),大约需要8-15秒。你这是什么意思?这是否意味着你使用了PIVOT子句?@HABO:谢谢你的评论。我在上一次迭代中就是这么做的。我禁用了表更新的触发,因为用户最终想要一个实时解决方案,并且更新时间太长。最终可能会再次出现这种情况。@dana:谢谢你的评论,和HABO的评论类似。这让我意识到,这可能会促使我与用户就“实时”数据与“实时”加载的优缺点进行坦诚的对话,因为如果这两种情况相等(一种情况下加载10分钟/秒,另一种情况下更新10分钟/秒),那么这两种情况实际上都是“实时”的。不,我没有!异步处理可能是一种方法。我会调查一下,让你知道结果如何。