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

SQL Serverトリガー:理解と代替案

    SQL Serverトリガーは、特定のデータベースサーバーでイベントが発生したときに自動的に実行される特殊なタイプのストアドプロシージャです。 SQL Serverには、主に2つのタイプのトリガーが用意されています。 DML トリガーとDDL トリガー。 DDLトリガーは、CREATE、ALTER、DROP、GRANT、DENY、およびREVOKE T-SQLステートメントの実行など、さまざまなデータ定義言語(DDL)イベントに応答して起動されます。 DDLトリガーは、これらの変更がデータベースに影響を与えないようにすることでDDLアクションに応答したり、これらのDDLアクションに応答して別のアクションを実行したり、データベースに対して実行されたこれらの変更を記録したりできます。

    SQL Server DMLトリガーは、INSERT、UPDATE、DELETEなどのデータ操作言語(DML)イベントが発生したときに、トリガーがアタッチされているデータベーステーブルに対して一連のアクションを実行するように設計された特殊なタイプのストアドプロシージャです。アクションは、テーブルの行が影響を受けるかどうかに関係なく、データベースのテーブルまたはビューのコンテンツを変更するために発生します。トリガーは、事前定義されたデータ変更が発生したときにトリガーが自動的に起動されるという点で、ストアドプロシージャとは異なります。 DMLトリガーは、監査プロセスやその他のDML後のアクションを実行することにより、データの整合性を維持し、テーブルチェックや外部キー制約機能と同じように会社のビジネスルールを適用するために使用できます。 DMLトリガーを使用して、他のテーブルを照会し、複雑なT-SQL照会を実行できます。

    トリガーが発生すると、挿入と呼ばれる特別なタイプの仮想テーブルが発生します および削除済み テーブルは、変更の前後のデータ値を保持するために使用されます。トリガーステートメントは、そのトリガーを起動するのと同じトランザクションのスコープの下で機能します。これは、トリガーステートメントが正常に完了するまでトランザクションが完全にコミットされないことを意味します。一方、トリガーステートメントが失敗した場合、トランザクションはロールバックされます。

    DMLトリガーには2つのタイプがあります: AFTER またはFOR トリガーとINSTEADOF 引き金。 AFTERトリガーは、それを正常に起動するINSERT、UPDATE、またはDELETEアクションを実行した後に起動および実行されます。また、参照カスケードアクションと制約チェックは、トリガーを起動する前に成功する必要があります。 AFTERトリガーは、ビューで定義する可能性なしに、テーブルレベルでのみ定義できます。 INSTEAD OFトリガーは、トリガーを起動するアクションのステートメントをトリガーで提供されるステートメントでオーバーライドするために使用され、誰かが更新などの特定のポリシーに違反するアクションを実行しようとしたときにエラーが発生した後、そのステートメントをロールバックします重要な財務列、または変更を実行する前に監査テーブルに変更を書き込む。 INSTEAD OFトリガーを使用すると、複数のテーブルのデータを参照するビューからデータをINSERT、UPDATE、またはDELETEすることができます。さらに、バッチクエリの一部を拒否して、そのバッチの別の部分を正常に実行することもできます。 INSTEAD OFトリガーは、WITH CHECK OPTIONを持つ更新可能なビュー、およびDELETEまたはUPDATEでのカスケードアクションを指定する参照関係を持つテーブルでは使用できません。

    トリガーを理論的に議論した後、実際に議論することを示し始めます。今後のデモでは、SQLServerトリガーを活用できるさまざまな状況を紹介します。

    AFTER…DMLトリガー

    特定のテーブルで実行されるDMLアクションを追跡し、これらのログを履歴テーブルに書き込む必要があると想定します。履歴テーブルでは、挿入、更新、または削除されたレコードのIDと、実行されるアクションが履歴テーブルに書き込まれます。以下のCREATETABLET-SQLステートメントを使用して、ソーステーブルと履歴テーブルの両方を作成できます。

    CREATE TABLE TriggerDemo_Parent
    (
       ID INT IDENTITY (1,1) PRIMARY KEY,
       Emp_First_name VARCHAR (50),
       Emp_Last_name VARCHAR (50),
       Emp_Salary INT 
      )
    GO
    
    CREATE TABLE TriggerDemo_History
    (
       ID INT IDENTITY (1,1) PRIMARY KEY,
       ParentID INT,
       PerformedAction VARCHAR (50),
      )
    GO
    

    INSERT操作を追跡するために、親テーブルでINSERT操作を実行した後に起動されるDMLトリガーを作成します。このトリガーは、以下のCREATE TRIGGER T-SQLステートメントのように、仮想挿入テーブルからその親テーブルに最後に挿入されたID値を取得します。

    CREATE TRIGGER AfterInsertTrigger
    ON TriggerDemo_Parent
    AFTER INSERT
    AS
    INSERT INTO TriggerDemo_History VALUES ((SELECT TOP 1  inserted.ID FROM inserted), 'Insert')
    GO
    

    DELETE操作の追跡は、親テーブルでDELETE操作を実行した後に起動されるDMLトリガーを作成することで実現できます。この場合も、トリガーは、以下のCREATE TRIGGER T-SQLステートメントのように、仮想削除テーブルからその親テーブルから最後に削除されたレコードのID値を取得します。

    CREATE TRIGGER AfterDeleteTrigger
    ON TriggerDemo_Parent
    AFTER DELETE
    AS
    INSERT INTO TriggerDemo_History VALUES ((SELECT TOP 1  deleted.ID FROM deleted), 'Delete')
    GO
    

    最後に、親テーブルでUPDATE操作を実行した後に起動されるDMLトリガーを作成することにより、UPDATE操作も追跡します。このトリガー内で、CREATE TRIGGERのように、レコードを削除し、更新された値で新しいレコードを挿入することによってUPDATEプロセスが実行されることを考慮して、仮想挿入テーブルからその親テーブルから最後に更新されたID値を取得します。以下のT-SQLステートメント:

    CREATE TRIGGER AfterUPDATETrigger
    ON TriggerDemo_Parent
    AFTER UPDATE
    AS
    INSERT INTO TriggerDemo_History VALUES ((SELECT TOP 1  inserted.ID FROM inserted), 'UPDATE')
    GO
    
    

    これで、テーブルとトリガーをテストする準備が整いました。以下のINSERTINTOT-SQLステートメントを使用して、親テーブルに新しいレコードを挿入しようとした場合:

    INSERT INTO TriggerDemo_Parent VALUES ('AAA','BBB',500)

    次に、前のINSERTステートメントを実行して生成された実行プランを確認すると、2つの挿入操作が実行され、2つのテーブルに影響することがわかります。以下の実行プランに示すように、INSERTステートメントで指定された値を持つ親テーブルとAFTERINSERTトリガーの起動による履歴テーブル:

    以下のSELECTステートメントを使用して、親テーブルと履歴テーブルの両方に挿入されたデータを確認する場合も明らかです。

    SELECT * FROM TriggerDemo_Parent
    GO
    SELECT * FROM TriggerDemo_History
    

    以下の結果に示すように、INSERTステートメントで指定された値が親テーブルに挿入され、挿入されたレコードのIDと実行された操作を含む挿入ログが履歴テーブルに挿入されます。

    >

    ここで、以下のUPDATE T-SQLステートメントを使用して、親テーブルの既存のレコードを更新しようとすると、次のようになります。

    UPDATE TriggerDemo_Parent SET Emp_Salary=550 WHERE ID=1

    また、前のUPDATEステートメントを実行して生成された実行プランを確認すると、更新操作の後に2つの異なるテーブルに影響を与える挿入操作が続くことがわかります。以下の実行プランに示すように、AFTER UPDATEトリガーを起動すると、UPDATEステートメントで指定された値と履歴テーブルへの挿入操作で親テーブルが更新されます。

    以下のSELECTステートメントを使用して、親レコードと履歴テーブルレコードの両方を確認します。

    SELECT * FROM TriggerDemo_Parent
    GO
    SELECT * FROM TriggerDemo_History
    

    updateステートメントが親テーブルのEmp_Salary値を、UPDATEステートメントで指定された値で変更し、更新されたレコードのIDと実行された操作を含む更新ログが履歴テーブルに挿入されることがわかります。以下の結果に示されています:

    AFTER DMLトリガーの最後のシナリオでは、以下のDELETE T-SQLステートメントを使用して、親テーブルからの既存のレコードの削除を追跡します。

    DELETE FROM  TriggerDemo_Parent WHERE ID=1

    次に、前のDELETEステートメントを実行して生成された実行プランを確認します。DELETE操作の後に挿入操作が続き、2つの異なるテーブルに影響することがわかります。以下の実行プランに示すように、DELETEステートメントのWHERE句で指定されたIDを持つレコードが削除される親テーブルと、AFTERDELETEトリガーの起動による履歴テーブルへの挿入操作:

    以下のSELECTステートメントを使用して、親レコードと履歴テーブルレコードの両方をチェックする場合:

    SELECT * FROM TriggerDemo_Parent
    GO
    SELECT * FROM TriggerDemo_History
    

    ID値が1のレコードが、DELETEステートメントで提供される親テーブルから削除されたことがわかります。削除されたレコードのIDと実行された操作を含む削除ログが履歴テーブルに挿入されます。 、以下の結果に示すように:

    INSTEADOF…DMLトリガー

    2番目のタイプのDMLトリガーは、INSTEADOFDMLトリガーです。前述のように、INSTEAD OFトリガーは、トリガーで提供されたステートメントでトリガーを起動するアクションのステートメントをオーバーライドします。ユーザーが特定のテーブルで実行しようとしているDMLアクションを、そのアクションの実行を許可せずにログに記録する必要があると想定します。以下のCREATETABLET-SQLステートメントを使用して、ソーステーブルと代替テーブルの両方を作成できます。

    CREATE TABLE TriggerDemo_NewParent
    (
       ID INT IDENTITY (1,1) PRIMARY KEY,
       Emp_First_name VARCHAR (50),
       Emp_Last_name VARCHAR (50),
       Emp_Salary INT 
      )
    GO
    
    
    CREATE TABLE TriggerDemo_InsteadParent
    (
       ID INT IDENTITY (1,1) PRIMARY KEY,
       ParentID INT,
       PerformedAction VARCHAR (50),
      )
    GO
    

    2つのテーブルを作成した後、以下のINSERT INTOステートメントを使用して、デモのソーステーブルに1つのレコードを挿入します。

    INSERT INTO TriggerDemo_NewParent VALUES ('AA','BB', 500)
    
    

    このデモでは、INSERT、UPDATE、およびDELETE操作をオーバーライドする3つのトリガーを作成します。最初のトリガーは、親テーブルおよび代替テーブルに変更されるログに対する挿入操作を防ぐために使用されます。トリガーは、以下のCREATETRIGGERT-SQLステートメントを使用して作成されます。

    CREATE TRIGGER InsteadOfInsertTrigger
    ON TriggerDemo_NewParent
    INSTEAD OF INSERT
    AS
    INSERT INTO TriggerDemo_InsteadParent VALUES ((SELECT TOP 1  inserted.ID FROM inserted), 'Trying to Insert new ID')
    GO
    

    2番目のトリガーは、親テーブルおよび代替テーブルに変更されるログに対する更新操作を防ぐために使用されます。このトリガーは次のように作成されます:

    CREATE TRIGGER InsteadOfUpdateTrigger
    ON TriggerDemo_NewParent
    INSTEAD OF UPDATE
    AS
    INSERT INTO TriggerDemo_InsteadParent VALUES ((SELECT TOP 1  inserted.ID FROM inserted), 'Trying to Update an existing ID')
    GO
    
    

    最後のトリガーは、親テーブルおよび代替テーブルに変更されるログに対する削除操作を防ぐために使用されます。このトリガーは次のように作成されます:

    CREATE TRIGGER InsteadOfDeleteTrigger
    ON TriggerDemo_NewParent
    INSTEAD OF DELETE
    AS
    INSERT INTO TriggerDemo_InsteadParent VALUES ((SELECT TOP 1  inserted.ID FROM inserted), 'Trying to Delete an existing ID')
    GO
    

    これで、2つのテーブルと3つのトリガーの準備が整いました。以下のINSERTINTOT-SQLステートメントを使用して、親テーブルに新しい値を挿入しようとした場合:

    INSERT INTO TriggerDemo_NewParent VALUES ('CCC','DDD',500)

    次に、以下のSELECTステートメントを使用して、親テーブルレコードと代替テーブルレコードの両方を確認します。

    SELECT * FROM TriggerDemo_NewParent
    GO
    SELECT * FROM TriggerDemo_InsteadParent
    

    親テーブルにINSTEADOFINSERTトリガーがあるため、結果から、次のように、新しいレコードが親テーブルに挿入されず、挿入操作のログが代替テーブルに挿入されていることがわかります。以下の結果:

    以下のUPDATET-SQLステートメントを使用して、親テーブルの既存のレコードを更新しようとしています:

    UPDATE TriggerDemo_NewParent SET Emp_Salary=550 WHERE ID=1

    次に、以下のSELECTステートメントを使用して、親テーブルレコードと代替テーブルレコードの両方をチェックします。

    SELECT * FROM TriggerDemo_NewParent
    GO
    SELECT * FROM TriggerDemo_InsteadParent
    

    結果から、親テーブルのID値が1のレコードのEmp_Salary値は変更されず、INSTEAD OF UPDATEトリガーがあるため、更新操作のログが代替テーブルに挿入されることがわかります。以下の結果に示すように、親テーブルで:

    最後に、以下のDELETE T-SQLステートメントを使用して、親テーブルから既存のレコードを削除しようとすると、次のようになります。

    DELETE FROM  TriggerDemo_NewParent  WHERE ID=1

    そして、以下のSELECTステートメントを使用して、親テーブルレコードと代替テーブルレコードの両方を確認します。

    SELECT * FROM TriggerDemo_NewParent
    GO
    SELECT * FROM TriggerDemo_InsteadParent
    

    結果から、親テーブルのID値が1のレコードは削除されず、親にINSTEAD OF DELETEトリガーがあるため、削除操作のログが代替テーブルに挿入されることが明らかになります。以下の結果に示すように、テーブル:

    AFTER…メッセージを使用したDMLトリガー

    AFTERトリガーを使用して、ユーザーに警告メッセージを表示することもできます。この場合、クエリは、そのトリガーを起動するステートメントの実行を妨げない情報メッセージになります。以前に作成したINSTEADOFUPDATEトリガーを削除し、以下のDROP / CREATE TRIGGERT-SQLステートメントを使用して更新操作を実行した後に警告エラーを発生させる別のAFTERUPDATEトリガーに置き換えてみましょう。

    DROP TRIGGER InsteadOfUpdateTrigger
    CREATE TRIGGER ReminderTrigger  
    ON TriggerDemo_NewParent  
    AFTER  UPDATE   
    AS RAISERROR ('An Update is performed on the TriggerDemo_NewParent table', 16, 10);  
    GO  
    

    以下のUDPATEステートメントを使用して、従業員のEmp_Salary値を1に等しいID値で更新しようとした場合:

    UPDATE TriggerDemo_NewParent SET Emp_Salary=550 WHERE ID=1

    メッセージでエラーメッセージが表示されます 以下に示すように、作成されたトリガーで提供されるメッセージを含むタブ:

    以下のSELECTステートメントを使用して親テーブルデータを確認します:

    SELECT * FROM TriggerDemo_NewParent

    結果から、以下に示すように、Emp_Salaryが正常に更新されたことがわかります。

    エラーメッセージが表示された後に更新操作を停止するためにAFTERUPDATEトリガーが必要な場合は、 ROLLBACK ステートメントをトリガーに追加して、そのトリガーを起動した更新操作をロールバックし、トリガーとトリガーを起動するステートメントが同じトランザクションで実行されることを思い出すことができます。これは、ALTERTRIGGERT-SQLステートメントを使用して実現できます。

    を参照してください。
    ALTER TRIGGER ReminderTrigger  
    ON TriggerDemo_NewParent  
    AFTER  UPDATE   
    AS RAISERROR ('An Update is performed on the TriggerDemo_NewParent table', 16, 10);  
    ROLLBACK
    GO  
    

    以下のUPDATEステートメントを使用して、IDが1の従業員のEmp_Salary値を更新しようとした場合:

    UPDATE TriggerDemo_NewParent SET Emp_Salary=700 WHERE ID=1

    この場合も、メッセージにエラーメッセージが表示されます タブですが、今回は、以下のエラーメッセージに示すように、更新操作が完全にロールバックされます。

    以下のSELECTステートメントを使用してソーステーブルの値を確認します。

    SELECT * FROM TriggerDemo_NewParent

    以下の表の結果に示すように、AFTER UPDATEトリガーがエラーメッセージを表示した後、トランザクション全体をロールバックしたため、Emp_Salary値が変更されていないことがわかります。

    トリガーのデメリット

    SQL Serverトリガーの前述のすべての利点により、トリガーはデータベースの複雑さを増します。トリガーの設計が不適切であるか、使いすぎていると、トランザクションの寿命が長くなるためにセッションがブロックされるなどの主要なパフォーマンスの問題が発生し、INSERT、UPDATE、またはDELETEアクションが実行されるか、データ損失の問題が発生する可能性があります。また、データベーストリガーを表示および追跡することは簡単ではありません。特に、開発者やアプリケーションには表示されないため、データベーストリガーに関するドキュメントがない場合はそうです。

    トリガーの選択肢…整合性を強化する

    トリガーがSQLServerインスタンスのパフォーマンスに悪影響を及ぼしていることが判明した場合は、それらを他のソリューションに置き換える必要があります。たとえば、トリガーを使用してエンティティの整合性を強制するのではなく、PRIMARYKEYおよびUNIQUE制約を使用して最下位レベルで強制する必要があります。同じことが、CHECK制約を介して強制される必要があるドメイン整合性、およびFOREIGNKEY制約を介して強制される必要がある参照整合性にも適用されます。 DMLトリガーを使用できるのは、特定の制約でサポートされている機能がアプリケーションの要件を満たせない場合のみです。

    DMLトリガーを使用してドメインの整合性を強制することとCHECK制約を使用することを比較してみましょう。 Emp_Salary列にのみ正の値を挿入するように強制する必要があると想定します。以下のCREATETABLET-SQLステートメントを使用して単純なテーブルを作成することから始めます。

    CREATE TABLE EmployeeSalaryTrigger
    (
       ID INT IDENTITY (1,1) PRIMARY KEY,
       Emp_First_name VARCHAR (50),
       Emp_Last_name VARCHAR (50),
       Emp_Salary INT 
      )
    GO
    

    次に、以下のCREATE TRIGGER T-SQLステートメントを使用して、ユーザーが負の給与値を挿入した場合にトランザクションをロールバックすることにより、Emp_Salary列に正の値を挿入することを保証するAFTERINSERTDMLトリガーを定義します。

    CREATE TRIGGER TRGR_EmployeeSalary ON EmployeeSalaryTrigger 
    AFTER INSERT 
    AS
    DECLARE @EmpSal AS INT
    SET @EmpSal = (SELECT TOP 1 inserted.Emp_Salary FROM inserted)
    IF @EmpSal<0
    BEGIN 
     RAISERROR  ('Cannot insert negative salary',16,10);
      ROLLBACK
    END
    

    比較のために、同じスキーマを使用して別の単純なテーブルを作成し、CREATE TABLEステートメント内にCHECK制約を定義して、以下に示すように、Emp_Salary列の正の値のみを受け入れます。

    CREATE TABLE EmployeeSalaryConstraint
    (
       ID INT IDENTITY (1,1) PRIMARY KEY,
       Emp_First_name VARCHAR (50),
       Emp_Last_name VARCHAR (50),
       Emp_Salary INT CONSTRAINT EmpSal CHECK (Emp_Salary >=0)
      )
    GO
    

    以下のINSERTINTOステートメントを使用して、負のEmp_Salary値を含む以下のレコードを、事前定義されたトリガーを持つ最初のテーブルに挿入しようとした場合:

    INSERT INTO EmployeeSalaryTrigger VALUES('Ali', 'Fadi',-4)
    GO
    

    以下のエラーメッセージに示すように、INSERTステートメントは失敗し、Emp_Salary列に負の値を挿入できず、AFTERINSERTトリガーがあるためにトランザクション全体をロールバックできないことを示すエラーメッセージが表示されます。

    また、以下のINSERT INTOステートメントを使用して、負のEmp_Salary値を含む同じレコードを、事前定義されたCHECK制約を持つ2番目のテーブルに挿入しようとした場合:

    INSERT INTO EmployeeSalaryConstraint VALUES ('Ali', 'Fadi',-4)

    以下のエラーメッセージに示すように、INSERTステートメントは再び失敗し、CHECK制約条件と競合する値を挿入しようとしていることを示します。

    前の結果から、トリガーメソッドとCHECK制約メソッドの両方が、負のEmp_Salary値を挿入しないようにすることで目標を達成していることがわかります。しかし、どちらが良いですか?それぞれの実行プランの重みを確認して、2つのメソッドのパフォーマンスを比較してみましょう。 2つのクエリを実行した後に生成された実行計画から、トリガーメソッドの重みが3倍であることがわかります。 以下の実行プランの比較に示すように、CHECK制約メソッドの重み:

    また、それぞれが消費する実行時間を比較するために、以下のT-SQLステートメントを使用してそれぞれを1000回実行してみましょう。

    INSERT INTO EmployeeSalaryTrigger VALUES('Ali', 'Fadi',-4)
    GO 10000  
    INSERT INTO EmployeeSalaryConstraint VALUES ('Ali', 'Fadi',-4)
    GO 10000 
    

    トリガーを使用する最初のメソッドには、約31ミリ秒かかることがわかります。 完全に実行されます。この場合、CHECK制約を使用する2番目のメソッドは17msしかかかりません。 、これは約0.5時間です トリガーを使用するメソッドで必要です。これは、トリガーがトランザクションの寿命を延ばし、整合性違反が見つかったときにトリガーを実行した後にトリガーを起動するクエリをロールバックし、ロールバックプロセスによるパフォーマンスの低下を引き起こすためです。 CHECK制約を使用する場合はケースが異なります。この場合、データに変更を加える前に制約がその役割を果たし、違反が発生した場合にロールバックする必要はありません。

    トリガーの選択肢…監査

    前述したように、トリガーを使用して、特定のテーブルで実行された変更を監査および追跡することもできます。この監査方法によってSQLServerインスタンスのパフォーマンスが低下する場合は、 OUTPUTに簡単に置き換えることができます。 句。 OUTPUT句は、INSERT、UPDATE、またはDELETE操作の影響を受ける各行に関する情報を、確認メッセージまたは履歴テーブルに挿入できる値の形式で返します。 OUTPUT句メソッドは、常に実行されるトリガーとは逆に、いつでもデータの挿入、変更、または削除ステートメント自体に追加されるため、実行されるコードをより細かく制御できます。

    DMLトリガーとOUTPUT句を使用して、データの挿入と変更を履歴テーブルに記録することを比較してみましょう。以下のCREATETABLET-SQLステートメントを使用して、以下の本番テーブルと履歴テーブルを作成することから始めます。

    CREATE TABLE TriggerDemo_Prod
    (
       ID INT IDENTITY (1,1) PRIMARY KEY,
       Emp_First_name VARCHAR (50),
       Emp_Last_name VARCHAR (50),
       Emp_Salary INT 
      )
    GO
    
    
    CREATE TABLE TriggerDemo_ProdHistory
    (
       ID INT IDENTITY (1,1) PRIMARY KEY,
       ProdID INT,
       ProdSalary INT,
       TS DATETIME,
      )
    GO
    

    両方のテーブルが正常に作成されたら、AFTER INSERT、UPDATE DMLトリガーを作成します。このトリガーは、新しい行が本番テーブルに挿入された場合、または既存のレコードがCREATE TRIGGER T-SQLステートメントを使用して変更された場合に、履歴テーブルにレコードを書き込みます。以下:

    CREATE TRIGGER ProdHistory
    ON TriggerDemo_Prod
    AFTER INSERT, UPDATE
    AS
    INSERT INTO TriggerDemo_ProdHistory  VALUES ( (SELECT TOP 1  inserted.ID FROM inserted),(SELECT TOP 1  inserted.Emp_Salary FROM inserted), GETDATE())
    GO
    

    トリガーメソッドとOUTPUT句を使用して変更のログを比較するには、前の2つのテーブルと同じスキーマを使用して、本番テーブルと履歴テーブルの2つの新しい単純なテーブルを作成する必要がありますが、今回はトリガーを定義せずに、以下のCREATETABLET-SQLステートメント:

    CREATE TABLE OutputDemo_Prod
    (
       ID INT IDENTITY (1,1) PRIMARY KEY,
       Emp_First_name VARCHAR (50),
       Emp_Last_name VARCHAR (50),
       Emp_Salary INT 
      )
    GO
    
    
    CREATE TABLE OutputDemo_ProdHistory
    (
       ID INT IDENTITY (1,1) PRIMARY KEY,
       ProdID INT,
       ProdSalary INT,
       TS DATETIME,
      )
      
    GO
    

    これで、4つのテーブルをテストする準備が整いました。以下のINSERTINTOT-SQLステートメントを使用して、トリガーを持つ最初の本番テーブルに1つのレコードを挿入します。

    INSERT INTO TriggerDemo_Prod values('AA','BB', 750)
    GO 
    
    

    次に、OUTPUT句を使用して同じレコードを2番目の本番テーブルに挿入します。以下のINSERTINTOステートメントは、2つの挿入ステートメントとして機能します。最初のレコードは同じレコードを本番テーブルに挿入し、OUTPUT句の横にある2番目の挿入ステートメントは挿入ログを履歴テーブルに挿入します。

    INSERT INTO OutputDemo_Prod  OUTPUT inserted.ID, inserted.Emp_Salary, GETDATE() 
    INTO OutputDemo_ProdHistory	values('AA','BB', 750)
    GO 
    

    4つの本番テーブルと履歴テーブルに挿入されたデータを確認すると、以下の結果に示すように、トリガーメソッドとOUTPUTメソッドの両方が同じログを履歴テーブルに正常に同じ方法で書き込むことがわかります。

    2つのクエリを実行した後に生成された実行計画から、トリガーメソッドの重みが約(21%+ 36%) 57%であることがわかります。 全体の重みの、ここでOUTPUTメソッドの重みは約 43% 、以下の実行計画の比較に示すように、わずかな重みの違いがあります:

    各メソッドで消費される実行時間を比較すると、パフォーマンスの違いは明らかです。トリガーメソッドを使用して変更をログに記録すると、(114 + 125)239ミリ秒が消費されます。 完全に実行され、OUTPUT句メソッドを使用しているメソッドは5msしか消費しません 、これは 2% 以下の時間統計から明確に示されているように、トリガーメソッドで使用された時間の割合:

    以前の結果から、変更の監査にトリガーを使用するよりもOUTPUTメソッドを使用する方が優れていることがわかります。

    便利なリンク:

    • CREATE TRIGGER(Transact-SQL)https://docs.microsoft.com/en-us/sql/t-sql/statements/create-trigger-transact-sql
    • DMLトリガーhttps://docs.microsoft.com/en-us/sql/relational-databases/triggers/dml-triggers
    • DDLトリガーhttps://docs.microsoft.com/en-us/sql/relational-databases/triggers/ddl-triggers
    • 出力句(Transact-SQL)https://docs.microsoft.com/en-us/sql/t-sql/queries/output-clause-transact-sql

    1. Postgres:CASTエラーのデフォルト値を定義しますか?

    2. UTC_TIMEの例– MySQL

    3. SQL Server Compactの制限は何ですか? (または-MSプラットフォームで使用するデータベースをどのように選択しますか?)

    4. MySQLで行ジェネレータを作成するにはどうすればよいですか?