【问题标题】:What is the most efficient way to calculate installed base?计算安装基数的最有效方法是什么?
【发布时间】: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


【解决方案1】:

回应以下声明:

我们还尝试了更新已安装的 SQL 触发器解决方案 每次修改展示位置或费率时的基础,但这使得 数据库更新在批量更新时异常缓慢,并且 不可靠。我想这可能值得更多调查,如果这是 真的是最好的选择。

你听说过SQL Service Broker吗?它做得很好的一件事是允许您将数据排队以进行异步处理。如果触发器本身太慢,您可以使用触发器将记录排队以进行异步处理。

【讨论】:

  • 不,我没有!异步处理可能是要走的路。我会调查一下,然后告诉你结果如何。
【解决方案2】:

看起来这可能是一个提出问题会导致正确答案的情况。事实证明,答案主要在于我上面给出的查询,这完全是低效的。通过如下优化查询,我已经能够获得我正在寻找的附近的加载时间。

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

【讨论】:

    猜你喜欢
    • 2011-01-26
    • 1970-01-01
    • 2011-07-13
    • 1970-01-01
    • 2013-01-23
    • 1970-01-01
    • 2014-11-08
    • 2011-03-10
    • 2013-12-03
    相关资源
    最近更新 更多