3

各ユニットに割り当てられた特定の「引退率」のセットを前提として、長年にわたってさまざまな「環境」を持つさまざまな国でさまざまな配置/出荷のユニットの設置ベースを計算する必要があります。配置、曲線の定義、および曲線の割り当ては、さまざまなデータベーステーブルに保存されます(以下のDDLおよびサンプルデータとともに、SQLFiddle.comにもあります)。設置ベースの計算式は次のとおりです。

ここに画像の説明を入力してください ここで、1990年は配置データを取得した最初の年です。

問題:

ユニット/国/環境/年の配置の組み合わせが300万から1600万行のデータセットを使用してこれらの計算を行うと、目標の負荷/計算時間である30秒から1分よりもはるかに時間がかかります。

SQLServerのアプローチ

PIVOT毎年独自の列になるように編集すると、100,000から40万の生データ(プレースメント+レート)の返される行が取得されます。これには約8〜15秒かかります。ただし、以下に示すようにSQLステートメントを使用してこれを手動で計算する場合は、少なくとも10分かかります。

また、プレースメントまたはレートが変更されるたびにインストールベースを更新するSQLトリガーソリューションを試しましたが、バッチ更新ではデータベースの更新が不当に遅くなり、信頼性も低くなりました。これが本当に最良の選択肢であるならば、これはさらに調査する価値があると思います。

Excel-VSTOアプローチ(これまでのところ、最速のアプローチ):

このデータは最終的にC#VSTOを利用したExcelワークブックになり、一連のデータを介して計算されますが、VLOOKUPs6年間で150,000プレースメントをVLOOKUPsセルあたり約20(約2,000万VLOOKUPs)ロードすると、Excelがクラッシュします。小さなバッチで実行され、VLOOKUPs数式が値に変換される場合、クラッシュは発生しませんが、計算には1分以上かかります。

質問:

これまでよりも効率的に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 Service Brokerについて聞いたことがありますか? これが非常にうまく機能することの 1 つは、非同期処理のためにデータをキューに入れることができることです。トリガー自体が遅すぎる場合は、トリガーを使用して、非同期処理のためにレコードをキューに入れることができます。

于 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 に答える