2

列と、そのフラグがいつ設定されたかを追跡bitする対応する列を持つテーブルがあります。datetime2

CREATE TABLE MyTable
(
    Id int primary key identity,
    Processed bit not null,
    DateTimeProcessed datetime2
)

次のようにチェック制約を追加しました。

ALTER TABLE MyTable 
  ADD CHECK ((Processed = 0 AND DateTimeProcessed IS NULL) 
             OR (Processed = 1 AND DateTimeProcessed IS NOT NULL))

トリガーDateTimeProcessedを使用して列の設定を制御しようとしました:AFTER UPDATE

CREATE TRIGGER tr_MyTable_AfterUpdate ON MyTable
AFTER UPDATE
AS
BEGIN
    IF(UPDATE(Processed))
    BEGIN
        UPDATE MyTable
        SET DateTimeProcessed = CASE
            WHEN tab.Processed = 1 THEN GETDATE()
            ELSE NULL
            END
        FROM MyTable tab
        JOIN INSERTED ins
        ON ins.Id = tab.Id
    END
END

これに関する問題は、トリガーが実行される前にチェック制約が適用されるため、列が更新されるAFTER UPDATEと制約に違反することです。Processed

私がここでやろうとしていることを達成するための最良の方法は何でしょうか?

4

1 に答える 1

2

さて、 CREATE TABLEのMSDNページによると:

テーブルに FOREIGN KEY または CHECK CONSTRAINTS とトリガーがある場合、トリガーが実行される前に制約条件が評価されます。

これにより、「INSTEAD OF」トリガーを使用する可能性も排除されます。

AFTER トリガー自体がルールと同じ強制を提供できるため、最終的には必要ないため、CHECK CONSTRAINT を削除する必要があります。

  • BIT フィールドが 1 に設定されているときに、日付フィールドが設定されていることを既に確認しています。
  • CASE ステートメントは、日付フィールドを NULL にすることによって、BIT フィールドが 0 に設定されていることを既に処理しています。
  • チェックする別のブロックを用意IF UPDATE(DateTimeProcessed)して、テーブルにあったものに戻すかDELETED、エラーをスローすることができます。

    • 更新して元の値に戻す場合は、再帰トリガー呼び出しをテストし、再帰呼び出しである場合は終了する必要があります。
    • エラーをスローしたい場合は、次の行に沿って何かを使用してください。

      IF(UPDATE(DateTimeProcessed))
      BEGIN
         RAISERROR('Update of [DateTimeProcessed] field is not allowed.', 16, 1);
         ROLLBACK; -- cancel the UPDATE statement
         RETURN;
      END;
      

      このUPDATE()関数は、フィールドが UPDATE ステートメントに含まれていたことのみを示していることに注意してください。値の変化を示すものではありません。したがって、UPDATE を実行SET DateTimeProcessed = DateTimeProcessedすると、明らかに値は変更されませんが、UPDATE(DateTimeProcessed)「true」が返されます。

    • 列レベルのDENYを使用して、トリガーの外部で「ルール」のこの部分を処理することもできます。

      DENY UPDATE ON MyTable (DateTimeProcessed) TO {User and/or Role};

于 2014-11-05T20:29:15.063 に答える