web-dev-qa-db-ja.com

挿入された単一の仮想テーブル行のみを使用した挿入トリガーの後

別のテーブルに挿入された行のテーブルの複数の列を更新するトリガーがあります。

FuelTransactionInfo列に挿入されたレコードは、「情報コード」に基づいてFuelTransactionsの列を更新し、fuelTransactionIdを使用して正しい行を見つけます。レポートを簡単にするために、一部のデータを非正規化しています。

行は、複数の値を使用してfuelTransactionInfo列に挿入できます。例えば。、

INSERT INTO fuelTransactionInfo
(...cols)
VALUES
(...values),
(...values),
(...values);

私の問題は、最初の値のセットのみがトリガーで使用されていることです。行ではなくステートメントでトリガーが起動されることを理解しています。トリガーコードがそれを説明していると思います。しかし、それは単に機能していません。

これが私のトリガーコードです:

ALTER TRIGGER [dbo].[fuelTransactionInfoDev_DenormalizeFuelTransactions]
   ON  [ERPDev].[dbo].[fuelTransactionInfo]
   AFTER INSERT
AS 
BEGIN
    SET NOCOUNT ON

    UPDATE t SET
        t.employeeNumber = CASE
            WHEN fuelInfoCodes.efsCode = 'DRID' THEN
                inserted.value
            ELSE t.employeeNumber
        END,
        t.unitNumber = CASE
            WHEN fuelInfoCodes.efsCode = 'UNIT' THEN
                inserted.value
            ELSE t.unitNumber
        END,
        t.contractId = CASE
            WHEN fuelInfoCodes.efsCode = 'CNTN' THEN
                (SELECT c.id
                 FROM pinContract p
                 JOIN contracts c
                    ON c.contractNumber = p.ContractNumber
                 WHERE pin = inserted.value)
            ELSE t.contractId
        END,
        t.contract = CASE
            WHEN fuelInfoCodes.efsCode = 'CNTN' THEN
                (SELECT c.contractNumber
                 FROM pinContract p
                 JOIN contracts c
                    ON c.contractNumber = p.ContractNumber
                 WHERE pin = inserted.value)
            ELSE t.contract
        END,
        t.driverName = CASE
            WHEN fuelInfoCodes.efsCode = 'NAME' THEN
                inserted.value
            ELSE t.driverName
        END,
        t.subFleet = CASE
            WHEN fuelInfoCodes.efsCode = 'SSUB' THEN
                inserted.value
            ELSE t.subFleet
        END
    FROM fuelTransactions t
    JOIN inserted
        ON inserted.fuelTransactionid = t.id
    JOIN fuelInfoCodes
        ON fuelInfoCodes.id = inserted.fuelInfoCodeId;
END

トリガーに次のステートメントを追加して、少しテストを行いました。

SELECT * FROM INSERTED INTO insertedTest

InsertedTestテーブルに期待されるすべての行が表示されます。 UPDATEステートメントを同等の選択(「挿入された」ではなく、挿入されたテストテーブルに基づく)に変換してテストしましたが、すべての結合は適切です。

「UNIT」ケースの列のみが実際にfuelTransactionsで更新されます。これは、トリガーを起動するすべての挿入の最初の行です。

何が悪いのですか?さらに情報が必要な場合はお知らせください。

3
mwalt

それが機能しない理由は、結合によって各行が複数回更新されるためですが、各更新では元の値のみが表示され、以前の更新の累積値は表示されません。そのため、更新は実質的に以前の更新をキャンセルしています。 (または、SQLはステートメントごとに1つの行の更新のみを許可します。どちらの方法でも、リソースの使用方法は異なりますが、結果は同じです。)

以下は、迅速かつ適切にフォーマットされていない再現です。

declare @insertedexample table (id INT, codeid int, value varchar(10))

INSERT INTO @insertedexample VALUES
(1,1,'newvalue1'),
(1,2,'newvalue2')


declare @targetexample table (id INT, codeid1value varchar(10), codeid2value varchar(10))

INSERT INTO @targetexample VALUES
(1, 'origvalue1', 'origvalue2')


SELECT * FROM @targetexample

UPDATE t
SET 
t.codeid1value = CASE WHEN i.codeid=1 THEN i.value ELSE t.codeid1value END,
t.codeid2value = CASE WHEN i.codeid=2 THEN i.value ELSE t.codeid2value END
FROM @targetexample t
JOIN @insertedexample i on i.id = t.id

SELECT * FROM @targetexample

これを処理するために現時点で私が考えることができる唯一の方法は、動的クエリを作成することです。

アーロンのビューの提案は確かに優れています。ただし、ユーザーがfuelTransactions.idによってのみフィルタリングするか、ピボット後のフィルタリングを処理するためにデータが十分に小さい場合に限られます。 (編集:後でベーステーブルでのインデックスのサポートに依存して、効率的な追加のフィルタリングを可能にするビューまたはビューのセット、または確かに関数を作成することも可能であることに後で気付きました。)

更新:

興味のあるefsCodeコードと同様に、fuelTransactionsテーブルの列が固定されているため、実際にはおそらく動的クエリは必要ありません。

醜いサブクエリの束を使用する必要がある場合でも、更新されていない列の現在の値を組み込んだ、何らかの形式のピボットを使用できる場合があります。
実験する時間があるかもしれませんが、約束することはできません。

1
T.H.