3

考虑到分配给每个单元的一组特定“退休率”,我需要计算多年来在不同国家具有不同“环境”的不同放置/出货量的单元的安装基数。放置、曲线定义和曲线分配存储在不同的数据库表中(下面有 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一系列. 当以较小的批次完成并将公式转换为值时,它不会崩溃,但计算时间仍然超过一分钟。VLOOKUPsVLOOKUPsVLOOKUPs

问题:

是否有一些数学或编程构造可以帮助我通过 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'
4

2 回答 2

1

回应以下声明:

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

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

于 2012-08-17T21:25:00.120 回答
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
于 2012-08-19T03:26:04.903 回答