web-dev-qa-db-ja.com

SQL Serverで条件が満たされた場合にのみ起動するトリガー

これが、SQLの人々にとって十分に単純な質問であることを願っています...

システム構成データを保持するテーブルがあり、これはトリガーを介して履歴テーブルに関連付けられているため、誰が何をいつ変更したかを追跡できます。

このテーブルに別の値を追加する必要がありますが、コードから頻繁に変更されるものであり、その履歴を追跡しないという要件があります(何千ものテーブルを詰まらせたくない1日あたりの更新。

現在、私たちのトリガーはこのようなものです...

CREATE TRIGGER 
    [dbo].[SystemParameterInsertUpdate]
ON 
    [dbo].[SystemParameter]
FOR INSERT, UPDATE 
AS
  BEGIN
    SET NOCOUNT ON
      INSERT INTO SystemParameterHistory 
      (
        Attribute,
        ParameterValue,
        ParameterDescription,
        ChangeDate
      )
    SELECT
      Attribute,
      ParameterValue,
      ParameterDescription,
      ChangeDate
    FROM Inserted AS I
END

属性列の値に特定の文字列(「NoHist_」など)が付いている場合、レコードの作成を停止するロジックを追加できるようにしたい

トリガーを使用した経験がほとんどないことを考えると、これを実装するのが最善かどうか疑問に思っていました...次のようなwhere句を試しました

where I.Attribute NOT LIKE 'NoHist_%'

しかし、うまくいかないようです。値は引き続き履歴テーブルにコピーされます。

あなたが提供できるどんな助けもありがたいです。


OK-Cade Rouxが予測したように、これは複数の更新で見事に失敗します。これに対して新しいアプローチをとる必要があります。誰か他の提案がありますか?


みんな-ここで教えてください...なぜこのシナリオでLEFT()がLIKEよりも望ましいのでしょうか?私は答えを受け入れましたが、私自身の教育について知りたいです。

26
ZombieSheep

WHERE句が機能しなかった場合、おそらく次のようになります。

CREATE TRIGGER 
    [dbo].[SystemParameterInsertUpdate]
ON 
    [dbo].[SystemParameter]
FOR INSERT, UPDATE 
AS
  BEGIN
    SET NOCOUNT ON

      If (SELECT Attribute FROM INSERTED) LIKE 'NoHist_%'
      Begin
          Return
      End

      INSERT INTO SystemParameterHistory 
      (
        Attribute,
        ParameterValue,
        ParameterDescription,
        ChangeDate
      )
    SELECT
      Attribute,
      ParameterValue,
      ParameterDescription,
      ChangeDate
    FROM Inserted AS I
END
40
Joel Coehoorn

これはどう?

CREATE TRIGGER 
[dbo].[SystemParameterInsertUpdate]
ON 
[dbo].[SystemParameter]
FOR INSERT, UPDATE 
AS
BEGIN
SET NOCOUNT ON
  IF (LEFT((SELECT Attribute FROM INSERTED), 7) <> 'NoHist_') 
  BEGIN
      INSERT INTO SystemParameterHistory 
      (
        Attribute,
        ParameterValue,
        ParameterDescription,
        ChangeDate
      )
    SELECT
      Attribute,
      ParameterValue,
      ParameterDescription,
      ChangeDate
   FROM Inserted AS I
END
END
7
Matty

あなたのwhere句は機能するはずです。なぜそうしなかったのか、私は途方に暮れています。 where句の問題を将来どのように解決できるかを理解する方法を説明します。

トリガーを作成するときは、テーブルのすべての列で#inserted(または#deleted)という一時テーブルを作成して、クエリウィンドウから開始します。次に、典型的な値を入力します(常に複数のレコードがあり、値のテストケースをヒットしようとします)

その後、トリガーロジックを記述し、実際にトリガーに入らずにテストできます。あなたのwhere節のようなケースが期待したことをしていない場合、挿入がコメントアウトされてselectが何を返しているかを簡単にテストできます。そうすれば、問題が何であるかを簡単に確認できるでしょう。正しく書かれていれば、クラスのどこでトリガーが機能するかを保証します。

コードがすべての場合に適切に機能することを確認したら、#insertedをinsertにグローバルに置き換え、テストトリガーであるvoilaの周りにcreateトリガーコードを追加します。

コメントで述べたように、複数のレコードの挿入または更新では、選択したソリューションが適切に機能しないことが心配です。トリガーは、発生するかどうか、いつ発生するかを予測できないため、常に説明する必要があります(最終的にはほとんどすべてのテーブルで発生します)。

5
HLGEM

_文字もワイルドカード、BTWですが、なぜこれがうまくいかなかったのかはわかりません。

CREATE TRIGGER 
    [dbo].[SystemParameterInsertUpdate]
ON 
    [dbo].[SystemParameter]
FOR INSERT, UPDATE 
AS
  BEGIN
    SET NOCOUNT ON
      INSERT INTO SystemParameterHistory 
      (
        Attribute,
        ParameterValue,
        ParameterDescription,
        ChangeDate
      )
    SELECT
      I.Attribute,
      I.ParameterValue,
      I.ParameterDescription,
      I.ChangeDate
    FROM Inserted AS I
    WHERE I.Attribute NOT LIKE 'NoHist[_]%'
END
5
Cade Roux
CREATE TRIGGER
    [dbo].[SystemParameterInsertUpdate]
ON 
    [dbo].[SystemParameter]
FOR INSERT, UPDATE 
AS
  BEGIN
    SET NOCOUNT ON 

    DECLARE @StartRow int
    DECLARE @EndRow int
    DECLARE @CurrentRow int

    SET @StartRow = 1
    SET @EndRow = (SELECT count(*) FROM inserted)
    SET @CurrentRow = @StartRow

    WHILE @CurrentRow <= @EndRow BEGIN

        IF (SELECT Attribute FROM (SELECT ROW_NUMBER() OVER (ORDER BY Attribute ASC) AS 'RowNum', Attribute FROM inserted) AS INS WHERE RowNum = @CurrentRow) LIKE 'NoHist_%' BEGIN

            INSERT INTO SystemParameterHistory(
                Attribute,
                ParameterValue,
                ParameterDescription,
                ChangeDate)
            SELECT
                I.Attribute,
                I.ParameterValue,
                I.ParameterDescription,
                I.ChangeDate
            FROM
                (SELECT Attribute, ParameterValue, ParameterDescription, ChangeDate FROM (
                                                                                            SELECT ROW_NUMBER() OVER (ORDER BY Attribute ASC) AS 'RowNum', * 
                                                                                            FROM inserted)
                                                                                    AS I 
            WHERE RowNum = @CurrentRow

        END --END IF

    SET @CurrentRow = @CurrentRow + 1

    END --END WHILE
END --END TRIGGER