考虑到为每个单元分配了一定的“退休率”,我需要计算多年来具有不同“环境”的不同国家/地区的不同展示位置/出货量的单位的安装基数。放置,曲线定义和曲线分配存储在不同的数据库表中(下面有DDL和样本数据,也在SQLFiddle.com上)。计算安装基数的公式如下:
1990年是我们有第一年的就业数据。
使用300万到1600万行单位/国家/环境/年放置组合的数据集进行这些计算需要比目标加载/计算时间30秒到1分钟更多的时间。
当PIVOT
编辑,以便每年成为自己的列时,我可以获得100,000 t0 400,000返回的原始数据行(展示位置+费率),大约需要8-15秒。但是,如果我通过下面包含的SQL语句手动计算,则至少 10分钟。
我们还尝试了一种SQL触发器解决方案,每次修改放置或速率时都会更新已安装的基础,但这会使批量更新时数据库更新速度过慢,并且也不可靠。如果这真的是最好的选择,我想这可能值得进一步调查。
此数据最终以C#VSTO驱动的Excel工作簿结束,其中数据是通过一系列VLOOKUPs
计算的,但是当在6年内加载150,000个展示位置时,每个单元格约20 VLOOKUPs
(约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'
答案 0 :(得分:1)
回应以下声明:
我们还尝试过更新已安装的SQL触发器解决方案 每次修改展示位置或费率时都会生成基数 数据库更新在批量更新时不合理地缓慢,也是 不可靠的。如果是这样的话,我认为值得进一步调查 真的是最好的选择。
您听说过SQL Service Broker吗?它做得非常好的一件事是允许您将数据排队以进行异步处理。如果触发器本身太慢,您可以使用触发器对记录进行排队以进行异步处理。
答案 1 :(得分:0)
看起来这可能会成为一个问题导致正确答案的情况。事实证明,答案主要在于我上面给出的查询,这完全是低效的。我已经能够通过优化查询来获得我正在寻找的附近的加载时间。
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