【发布时间】:2012-08-14 08:08:02
【问题描述】:
我需要计算多年来在不同“环境”的不同国家/地区具有不同放置/出货量的设备的安装基数,给每个设备分配了一组特定的“退役率”。放置、曲线定义和曲线分配存储在不同的数据库表中(下面有 DDL 和示例数据,也在SQLFiddle.com 上)。安装基数计算公式如下:
其中 1990 年是我们获得展示位置数据的第一年。
问题:
使用包含 3 到 1600 万行单位/国家/地区/环境/年份放置组合的数据集进行这些计算所花费的时间比 30 秒到 1 分钟的目标加载/计算时间要长得多。
Sql Server 方法
当PIVOTed 使每一年成为自己的列时,我得到了从 100,000 到 400,000 行返回的原始数据(展示位置 + 费率),这大约需要 8-15 秒。但是,如果我通过如下所示的 SQL 语句手动计算,则需要至少 10 分钟。
我们还尝试了一种 SQL 触发器解决方案,该解决方案可以在每次修改展示位置或费率时更新安装基础,但这会导致数据库更新在批量更新时异常缓慢,而且也不可靠。如果这真的是最好的选择,我想这可能值得更多调查。
Excel-VSTO 方法(目前最快的方法):
这些数据最终会出现在 C# VSTO 驱动的 Excel 工作簿中,其中数据是通过一系列 VLOOKUPs 计算得出的,但是当在 6 年内加载 150,000 个展示位置时,每个单元格大约有 20 个 VLOOKUPs(约 2000 万个 @987654326 @),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'
【问题讨论】:
-
这听起来像是我通常保存在单独表格中的那种汇总数据。该表可以每天更新,也可以在其他合适的时间间隔内使用计划的作业进行更新。
-
这些数据是否需要“实时”,或者计算有一些延迟是否可以接受?
-
当 PIVOTed 使每一年成为自己的列时,我得到了从 100,000 到 400,000 返回的原始数据行(展示位置 + 费率),这大约需要 8-15 秒。 i> 你这是什么意思?这是否意味着您使用了 PIVOT 子句?
-
@HABO:感谢您的评论。我在最后一次迭代中这样做了。我禁用了表更新的触发,因为用户最终想要一个实时的解决方案,而更新时间太长了。它最终可能会再次出现这种情况。
-
@dana:感谢您的评论,类似于 HABO 的评论。这让我意识到这可能会促使与用户就“实时”数据与“实时”负载的优缺点进行诚实的对话,因为如果两者相等(10 分钟加载/秒更新一次场景和相反的场景),两者都是有效的“实时”。
标签: c# sql excel optimization sql-server-2008-r2