sql >> データベース >  >> RDS >> Sqlserver

SQL Serverトリガー:DMLトリガー

    SQL Serverでは、トリガーはデータベースオブジェクトであり、データベースまたはサーバーでトリガーイベントが発生するたびに実行されます。

    トリガーは、対象となる人々への警告、仕事の開始、またはその他の操作などのビジネス要件を達成する上で重要な役割を果たします。トリガーはそのような操作の多くを処理できるため、パフォーマンスへの影響を回避するために注意して定義する必要があります。

    この記事では、トリガー、トリガーのタイプ、および使用可能なさまざまなトリガーオプションについて説明します。また、DMLトリガーを使用する際に必要な注意事項についても説明します。

    トリガー SQLで

    トリガーは、トリガー本体で定義されたスクリプトを実行して、定義されたイベントで実行される特殊なタイプのストアドプロシージャです。トリガーにはいくつかの種類があります:

    • DMLトリガー –テーブルでINSERT、UPDATE、DELETEコマンドなどのDML操作を実行するため。
    • DDLトリガー –データベースまたはサーバー上の任意のオブジェクトに対してCREATE、ALTER、DROPコマンドなどのDDL操作を実行するため。
    • ログオントリガー –LOGONイベント中にSQLServerのインスタンスにログインしようとした場合。

    SQLServerのDMLトリガー

    DMLトリガーは、テーブルまたはビューでDMLコマンド(INSERT、UPDATE、またはDELETE)によって起動されるトリガーです。これらのテーブルまたはビューには、データが存在する場所でのみこのようなトリガーを作成して、DMLコマンドを受け入れることができます。

    起動/呼び出しの時間に基づいて、DMLトリガーは次のタイプになります。

    • FOR または トリガータイプ–トリガーは、テーブルまたはビューでDMLステートメントが正常に完了した後に呼び出されます。注:AFTERトリガーは、ビューではなく、テーブルでのみ作成できます。
    • INSTEAD OF トリガータイプ–トリガーは、テーブルまたはビューで実行されるDMLスクリプト(INSTEAD OF)の前に呼び出されます。

    SQL Serverは、 INSERTEDという名前の2つの特別なテーブルまたは論理テーブルを作成します および更新 DMLトリガーがテーブルまたはビュー全体で作成されるときはいつでも。これらの論理テーブルは、INSERT / UPDATE/DELETE操作を介して発生するレコードの変更を識別するのに役立ちます。このようにして、DMLトリガーが効果的に機能することを保証します。

    • 挿入済み 論理テーブルには、INSERTおよびUPDATE操作中に変更されたレコードの新しいレコードのコピーが格納されます。新しいレコードが実際のテーブルに追加されると、そのレコードもINSERTEDテーブルに追加されます。同様に、UPDATEステートメントを介して既存のレコードを変更すると、最新の値がINSERTEDテーブルに移動し、古い値がDELETED論理テーブルに移動します。
    • 削除済み 論理テーブルには、UPDATEおよびDELETE操作中の古い値のコピーが格納されます。レコードが更新されるたびに、古い値がDELETEDテーブルにコピーされます。レコードが実際のテーブルから削除されるたびに、レコードはDELETEDテーブルに挿入されます。

    SQLServerには組み込み関数COLUMN_UPDATED()があります およびUPDATE() 特定の列でのINSERTまたはUPDATE操作の存在を識別するため。

    • COLUMN_UPDATED() INSERTまたはUPDATE操作の影響を受けた列の変数値を返します。
    • UPDATE() 列名を入力パラメーターとして受け入れ、その列にINSERTまたはUPDATE操作の一部としてデータ変更があるかどうかの情報を返します。

    複製不可 プロパティをDMLトリガーで使用して、レプリケーションプロセスを介して行われる変更に対してそれらが起動されないようにすることができます。

    DMLトリガーは、.Net Framework共通言語ランタイム(CLR)を使用して作成することもできます。

    システムDMV sys.triggers データベーススコープのすべてのトリガーのリストを格納します。以下のクエリを使用して、データベース内のすべてのDMLトリガーの詳細を取得できます。

    SELECT * 
    FROM sys.triggers
    WHERE type = 'TR';
    

    トリガーが暗号化されていない場合は、DMLトリガー定義を表示できます。以下のオプションのいずれかを使用します:

    sys.sql_modules

    SELECT OBJECT_SCHEMA_NAME(object_id, db_id()) Schema_name, OBJECT_NAME(object_id) Trigger_Name, definition
    FROM sys.sql_modules  
    WHERE object_id = OBJECT_ID(<trigger_name>);   
    

    OBJECT_DEFINITION() 関数

    SELECT OBJECT_DEFINITION (OBJECT_ID(<trigger_name>)) AS ObjectDefinition; 

    sp_helptext ストアドプロシージャ

    EXEC sp_helptext '<trigger_name>';

    可能なすべてのDMLイベントは、 sys.eventsで利用できます。 テーブル。以下のクエリを使用してそれらを表示できます:

    SELECT * 
    FROM sys.events;
    

    DMLトリガーの構文

    CREATE TRIGGER <trigger_name>
    ON <schema_name.table_name | schema_name.view_name > 
    [ WITH <DML_trigger_option> [ ,...n ] ]  
    { FOR | AFTER | INSTEAD OF} <event_type>
    AS { sql_statement | EXTERNAL NAME <method specifier> }  
    

    デモの目的で、 Salesという名前の2つのテーブルを作成しました およびSalesHistory テストデータベースにいくつかの列がある場合:

    CREATE TABLE Sales (SalesId int IDENTITY NOT NULL, SalesDate datetime, Itemcount int, price money);
    CREATE TABLE SalesHistory (SalesId int NOT NULL, SalesDate datetime, Itemcount int, price money, ChangeType varchar(10), ChangeDate datetime DEFAULT GETDATE(), ChangedUser varchar(100) DEFAULT SUSER_NAME());
    GO
    

    ご覧のとおり、 SalesHistory テーブルには、変更を呼び出した変更日とユーザー名を追跡するための3つの追加列があります。必要に応じて、ID列をもう1つ作成できます。 定義して、それも主キーにします。

    挿入トリガー

    Salesで単純なINSERTトリガーを作成します SalesHistoryへの新しいレコードの変更を挿入するテーブル テーブル。以下のスクリプトを使用してください:

    CREATE TRIGGER TR_INS_Sales ON Sales
    FOR INSERT 
    AS
    BEGIN
      INSERT INTO SalesHistory(SalesId,SalesDate,Itemcount,price,ChangeType)
      SELECT SalesId
        	,SalesDate
    	,Itemcount
    	,price
    	,'INSERT'
      FROM inserted
    END
    GO
    

    トリガーの構文を説明するために、 TR_INS_Salesという名前のDMLトリガーを作成しました。 販売 テーブル。 INSERT操作のトリガーのみを起動する必要があります– SalesHistoryにレコードを挿入します 挿入されたテーブルからのテーブル。

    ご存知のとおり、挿入 ソーステーブル( Sales )で発生する変更をキャプチャする論理テーブルです。 この場合はテーブル)。

    削除されたため、UPDATEトリガーで削除された別の特別な論理テーブルを確認できます。 テーブルはINSERTトリガーには適用されません。

    新しいレコードを追加して、レコードが SalesHistoryに挿入されているかどうかを確認しましょう テーブルは自動的に。

    INSERT INTO Sales(SalesDate,Itemcount,price)
    VALUES ('2021-01-01', 5, 100);

    Salesにレコードを1つだけ挿入しましたが 表では、1行が影響を受けるの2行を取得します メッセージ。 2番目のレコードは、 SalesのINSERTアクティビティによって呼び出されたトリガーの一部としてのINSERT操作のために表示されます。 テーブル– SalesHistoryへのレコードの挿入 テーブル。

    両方の販売の記録を確認しましょう およびSalesHistory テーブル:

    SELECT * 
    FROM Sales
    
    SELECT * 
    FROM SalesHistory

    ChangeDate およびChangedUser 自動的に入力されます。 歴史を設計したからです デフォルト値がGETDATE()のテーブル およびSUSER_NAME()

    エンドユーザーは、トリガーまたはその他の方法でINSERTが影響を受けた追加の1行を介して監査されたことを確認できます。 メッセージ。ユーザーに通知せずに変更を監視する場合は、 SET ROWCOUNT ONを適用する必要があります 指図。トリガー内で発生するDML操作に対して表示される結果を抑制します。

    SET ROWCOUNT ON のスクリプトを使用して、トリガーを変更しましょう オプションを選択して、実際の動作を確認してください:

    ALTER TRIGGER TR_INS_Sales ON Sales
    FOR INSERT 
    AS
    BEGIN
    SET NOCOUNT ON
      INSERT INTO SalesHistory(SalesId,SalesDate,Itemcount,price,ChangeType)
      SELECT SalesId
        ,SalesDate
    	,Itemcount
    	,price
    	,'INSERT'
      FROM inserted
    END
    GO
    

    次に、別のレコードを Salesに挿入します。 テーブル:

    INSERT INTO Sales(SalesDate,Itemcount,price)
    VALUES ('2021-02-01', 1, 50);
    

    影響を受ける1行は1つだけです。 メッセージ。したがって、ターゲットオーディエンスは、自分のアクションが監視されていることをまったく通知されない可能性があります。

    Sales を確認して、INSERTトリガーが呼び出されたかどうかを確認しましょう。 およびSalesHistory テーブル。

    はい、 SalesのINSERTイベント テーブルが正常にトリガーされました。レコードはSalesHistoryに挿入されました ユーザーに通知せずにテーブル。

    したがって、監査目的でトリガーを作成する場合は、 SET NOCOUNT ON 必要です。誰にも警告せずに監査できます。

    更新トリガー

    販売で実際のUPDATEトリガーを作成する前 テーブルについて、論理的に挿入および削除された特別なテーブルをもう一度参照してみましょう。 販売でサンプルのUPDATEトリガーを作成します テーブル:

    CREATE TRIGGER TR_UPD_Sales ON Sales
    FOR UPDATE 
    AS
    BEGIN
    SELECT * FROM inserted
    SELECT * FROM deleted
    END
    GO
    

    UPDATEトリガーが正常に作成されました。それでは、新しいレコードを誤って挿入しましょう。後で更新して、UPDATEトリガーが動作していることを確認します:

    INSERT INTO Sales(SalesDate,Itemcount,price)
    VALUES ('2021-02-01', 1, 50);
    

    売上全体で以下の記録があります およびSalesHistory テーブル:

    SalesId =3を更新しましょう 販売 新しい値を持つテーブル。挿入されたテーブルと削除されたテーブルのデータが表示されます:

    UPDATE Sales
    SET SalesDate = '2021-03-01'
    	, Itemcount = 3
    	, price = 500
    WHERE SalesId = 3
    

    UPDATE操作が実行されると、すべての新しい値または変更された値が挿入されたテーブルで使用可能になり、古い値が削除されたテーブルで使用可能になります。

    それでは、以下のスクリプトを使用してUPDATEトリガーを変更し、動作を確認してみましょう。

    ALTER TRIGGER TR_UPD_Sales ON Sales
    FOR UPDATE 
    AS
    BEGIN
      INSERT INTO SalesHistory(SalesId,SalesDate,Itemcount,price,ChangeType)
      SELECT SalesId
        ,SalesDate
    	,Itemcount
    	,price
    	,'UPDATE'
      FROM inserted
    END
    GO
    

    UPDATEトリガーは正常に変更され、同じUPDATEスクリプトを再度実行できます:

    これで、影響を受ける1行を確認できます。 メッセージを2回。 SalesでのUPDATE操作の実行を示します。 テーブルとSalesHistoryでのINSERT操作 テーブル。両方のテーブルから選択して、これを確認しましょう:

    UPDATEアクティビティは、 SalesHistoryで追跡されました。 新しいレコードとしてのテーブル。そのレコードの前に、レコードが最初に挿入された日時を示す別のレコードがあります。

    削除トリガー

    これまで、 FORをテストしました または INSERT操作またはUPDATE操作の両方のトリガーのタイプ。これで、 INSTEAD OFを使用してみることができます DELETE操作のDMLトリガーのタイプ。以下のスクリプトを使用してください:

    CREATE TRIGGER TR_DEL_Sales ON Sales
    INSTEAD OF DELETE 
    AS
    BEGIN
    	RAISERROR ('Notify Sales Team', 16, 10);  
    END
    GO
    

    DELETEトリガーが正常に作成されました。 Sales でDELETEコマンドを実行する代わりに、クライアントにエラーメッセージを送信します。 テーブル。

    レコードを削除してみましょうSalesID =3 販売から 以下のスクリプトを使用したテーブル:

    DELETE FROM Sales
    WHERE SalesId = 3

    ユーザーが販売からレコードを削除できないようにしました テーブル。トリガーによってエラーメッセージが発生しました。

    また、レコードが販売から削除されたかどうかを確認しましょう。 表と、 SalesHistoryに変更があったかどうか テーブル:

    INSTEAD OFトリガーを使用して実際のDELETEステートメントの前にトリガースクリプトを実行したため、SalesId=3でのDELETE操作はまったく成功しませんでした。したがって、両方の販売に変更は反映されませんでした およびSalesHistory テーブル。

    以下のスクリプトを使用してトリガーを変更し、テーブルでのDELETEの試行を識別してみましょう。

    ALTER TRIGGER TR_DEL_Sales ON Sales
    INSTEAD OF DELETE 
    AS
    BEGIN
      INSERT INTO SalesHistory(SalesId,SalesDate,Itemcount,price,ChangeType)
      SELECT SalesId
        ,SalesDate
    	,Itemcount
    	,price
    	,'DELETE ATP'
      FROM deleted 
    END
    GO
    

    トリガーは正常に変更されました。 SalesId =3を削除しましょう 販売からの記録 もう一度テーブル:

    DELETEステートメントを実行すると、影響を受ける1行が表示されます。 メッセージを2回。 売上全体の記録を確認しましょう およびSalesHistory そこで何が起こっているかを確認するための表:

    DELETEトリガーで使用されるロジックは、 Sales からレコードを実際に削除せずに、テーブルでのDELETEの試行をキャプチャすることでした。 INSTEAD OFを使用したテーブル 引き金。 販売からレコードが削除されていないことを確認できます テーブル、および新しいレコードが SalesHistory に挿入されました テーブル。

    INSERT、UPDATE、およびDELETE操作を処理するための単一のトリガー

    これまで、単一のテーブルでINSERT、UPDATE、およびDELETE操作を処理するための3つのトリガーを作成しました。複数のトリガーがある場合、特にそれらが適切に文書化されていない場合、それらを管理することは困難です。開発者が複数のトリガーにわたって矛盾するロジックを使用した場合、パフォーマンスの問題が発生する可能性があります。

    個人的には、潜在的なデータ損失やパフォーマンスの問題を回避するために、すべてのロジックを組み合わせた単一のトリガーを使用することをお勧めします。パフォーマンスを向上させるために、3つのトリガーを1つのトリガーに組み合わせてみることができます。ただし、その前に、既存のトリガーを削除する方法と、トリガーを無効または有効にする方法を調べてみましょう。

    トリガーをドロップ

    3つのトリガーを1つにマージするには、最初にこれら3つのトリガーをドロップする必要があります。 SSMSとT-SQLの両方のアプローチで可能です。

    SSMSで、テストを展開します データベース >テーブル >販売 表>トリガー

    これまでに作成された3つのトリガーを確認できます:

    トリガーをドロップするには、トリガーを右クリックします>削除 > OK

    T-SQLを使用する場合は、次の構文を参照してトリガーを削除してください。

    DROP TRIGGER <trigger_name>

    TR_INS_Salesがあります 販売で作成したトリガー テーブル。スクリプトは次のようになります:

    DROP TRIGGER TR_INS_Sales

    重要 :テーブルを削除すると、デフォルトですべてのトリガーが削除されます。

    トリガーの無効化と有効化

    トリガーをドロップする代わりに、無効で一時的に無効にすることができます トリガー SSMSまたはT-SQLによるオプション。

    SSMSで、トリガー名を右クリックします>無効にする 。一度無効にすると、再度有効にするまでトリガーは起動されません。

    トリガーが機能している間、有効 オプションは灰色で表示されます。無効にすると、有効 オプションが表示されてアクティブになります。

    T-SQLを使用する場合は、次のスクリプトを使用してトリガーを無効または有効にできます。

    -- To Disable all triggers on a specific table
    DISABLE TRIGGER ALL ON <table_name>;
    
    -- To Disable a specific trigger on a table
    DISABLE TRIGGER <trigger_name> ON <table_name>;
    
    -- To Enable all triggers on a specific table
    ENABLE TRIGGER ALL ON <table_name>;
    
    -- To Enable a specific trigger on a table
    ENABLE TRIGGER <trigger_name> ON <table_name>;
    

    特定のTR_INS_Salesを無効および有効にするには 販売でトリガー 表では、以下のスクリプトを使用します:

    -- To Disable TR_INS_Sales trigger on Sales table
    DISABLE TRIGGER TR_INS_Sales ON Sales;
    
    -- To Enable TR_INS_Sales trigger on Sales table
    ENABLE TRIGGER TR_INS_Sales ON Sales;
    

    このようにして、 DROPの方法を学びました。 無効 、および ENABLE トリガー。既存の3つのトリガーを削除し、以下のスクリプトを使用して、3つの操作すべて、または挿入、更新、削除をカバーする単一のトリガーを作成します。

    DROP TRIGGER TR_INS_Sales
    DROP TRIGGER TR_UPD_Sales
    DROP TRIGGER TR_DEL_Sales
    GO
    
    CREATE TRIGGER TR_INS_UPD_DEL_Sales ON Sales
    FOR INSERT, UPDATE, DELETE
    AS
    BEGIN
    IF (SELECT COUNT (*) FROM deleted) = 0
    BEGIN
      INSERT INTO SalesHistory(SalesId,SalesDate,Itemcount,price,ChangeType)
      SELECT SalesId
        ,SalesDate
    	,Itemcount
    	,price
    	,'INSERT'
      FROM inserted
    END
    ELSE IF (SELECT COUNT (*) FROM inserted) = 0
    BEGIN
      INSERT INTO SalesHistory(SalesId,SalesDate,Itemcount,price,ChangeType)
      SELECT SalesId
        ,SalesDate
    	,Itemcount
    	,price
    	,'DELETE'
      FROM deleted
    END
    ELSE IF (UPDATE (SalesDate) OR UPDATE (ItemCount) OR UPDATE (Price))
    BEGIN
      INSERT INTO SalesHistory(SalesId,SalesDate,Itemcount,price,ChangeType)
      SELECT SalesId
        ,SalesDate
    	,Itemcount
    	,price
    	,'UPDATE'
      FROM inserted
    END 
    END
    GO
    

    シングルトリガーの作成は成功しました。挿入および削除されたテーブルを使用して操作を識別するロジックを使用しました。

    INSERT操作の場合、削除されたテーブルは作成されません。 DELETE操作の場合、挿入されたテーブルは作成されません。これらの操作は簡単に識別できます。これらの2つの条件が一致しない場合、それはUPDATE操作であり、単純なELSEステートメントを使用できます。

    UPDATE()を使用しました それがどのように機能するかを示す関数。これらの列に更新があった場合、UPDATEトリガーアクションが発生します。 COLUMNS_UPDATED()を使用することもできます UPDATE操作も識別するために前に説明した関数。

    新しいレコードを挿入して、新しいトリガーをテストしてみましょう:

    INSERT INTO Sales(SalesDate,Itemcount,price)
    VALUES ('2021-04-01', 4, 400);
    

    販売全体の記録を確認する およびSalesHistory 表は以下のようなデータを示しています:

    SalesId =2を更新してみましょう 記録:

    UPDATE Sales
    SET price = 250
    WHERE SalesId = 2;
    

    SalesId =4 で、この手順を使用してDELETEスクリプトを試してみましょう。 記録:

    DELETE FROM Sales
    WHERE SalesId = 4;
    

    お気づきのとおり、 SalesId =4 販売から削除されました これはFORであるため、テーブル または トリガーし、DELETE操作を Salesで成功させます テーブルを作成し、レコードを SalesHistory に挿入します テーブル。

    DMLトリガーの目的

    DMLトリガーは、次のシナリオで効果的に機能します。

    1. 特定のテーブルでのINSERT、UPDATE、およびDELETE操作の履歴変更を追跡します。
    2. 監査アクティビティをユーザーに公開せずに、テーブルで発生するDMLイベントを監査します。
    3. INSTEAD OF を介して、DMLの変更がテーブルで発生しないようにします。 特定のエラーメッセージをトリガーしてユーザーに警告します。
    4. 事前定義された条件を達成したときに、対象となる人々に通知を送信します。
    5. 事前定義された条件を達成するたびに、SQLServerエージェントジョブまたはその他のプロセスを開始します。

    また、T-SQLステートメントで実装できる他のビジネスロジック要件に使用できます。

    結論

    DMLトリガー本体は、ストアドプロシージャに似ています。必要なビジネスロジックを実装できますが、潜在的な問題を回避するために、関連するロジックを作成する際には注意が必要です。

    SQL Serverは単一のテーブルでの複数のトリガーの作成をサポートしていますが、単一のトリガーに統合することをお勧めします。このようにして、トリガーを簡単に保守し、トラブルシューティングを迅速に行うことができます。監査目的でDMLトリガーを実装する場合は常に、 SET NOCOUNT ONを確認してください。 オプションが効果的に使用されます。

    次の記事では、DDLトリガーとログオントリガーについて説明します。


    1. MySQLサーバー起動エラー'サーバーはPIDファイルを更新せずに終了しました'

    2. SQLServer2012の拡張イベントに対する重要な変更

    3. SQLステートメントでの角かっこ[]の使用は何ですか?

    4. OracleのSEQUENCE.NEXTVALに相当するMySQL