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

SQL Serverトリガー–パート2DDLおよびLOGONトリガー

    SQL Serverでは、トリガーはデータベースオブジェクトであり、データベースまたはサーバーでトリガーイベントが発生するたびに実行されます。トリガーは、達成された条件に基づいて対象となる人々に警告する、仕事を開始する、またはその他の操作などのビジネス要件を達成する上で重要な役割を果たします。 DMLトリガーに関する前回の記事では、トリガー、トリガーの種類、およびDMLトリガーで使用できるさまざまなトリガーオプションについて説明しました。この記事では、SQLDDLおよびLOGONトリガーについて説明します。

    DDLトリガー

    DDLトリガーは、DDLコマンドとDCLコマンドの両方を含む、さまざまなサーバーまたはデータベーススコープのイベントに対して起動できます。 DDLは、任意のオブジェクトのCREATE、ALTER、DROPに使用されるデータ定義言語を表し、DCLは、GRANT、DENY、REVOKEコマンドなどのデータ制御言語ステートメントを表します。以下は、SQLDDLトリガーの特性です。

    1. DDLトリガーは、データベースレベルまたはサーバーインスタンスレベルで作成でき、さまざまなDDL操作またはDDLに類似した操作をカバーします。 DCLコマンド。
    2. DDLトリガーは、FORまたはAFTERトリガータイプとしてのみ呼び出しまたは起動できます。 SQLServerはINSTEADOFDDLトリガーをサポートしておらず、DDLトリガーを介して一部のDDL操作を防止する方法を確認できます。
    3. SQL Serverには、トリガーイベントに関連する詳細情報を取得するためのDDLトリガー内で使用するEVENTDATA()やIS_MEMBER()などの組み込み関数があります。
      1. EVENTDATA()関数は、データベースまたはサーバースコープのDDLトリガーまたはログオントリガーのスコープ内で、データベースまたはサーバースコープのイベントに関する完全な詳細をXML形式で返します。 EVENTDATA()関数は、DDLまたはログオンアクティビティを実行するセッションの完全なイベント詳細を返します。 EVENTDATA()は以下の詳細を返します
        • EventType –sys.trigger_event_typesテーブルで使用可能なDDLトリガーを起動するイベントのタイプ。
        • PostTime –イベントがトリガーまたは投稿された時刻。
        • SPID –イベントのセッションID。
        • ServerName –イベントがトリガーされたSQLServerインスタンス名。
        • LoginName –イベントを実行したSQLServerログイン名。
        • UserName –デフォルトでdboになるログインのユーザー名。
        • DatabaseName –DDLトリガーが起動されたデータベース名。
        • SchemaName –影響を受けたオブジェクトのスキーマ名。
        • ObjectName –影響を受けたオブジェクト名。
        • ObjectType –テーブル、ビュー、ストアドプロシージャなどのSQLServerオブジェクトのタイプ。
        • TSQLCommand –DDLトリガーを呼び出したユーザーによって実行されたT-SQLスクリプト。
        • SetOptions –TSQLCommandの実行中にユーザーまたはSSMSなどのクライアントによって使用されるSETオプション。
        • CommandText –sys.trigger_event_typesテーブルで指定されたDDLイベントを含む実際のDDLまたはDCLステートメント。
      2. IS_MEMBER()関数は、現在のユーザーがWindowsグループまたはSQLServerデータベースの役割のメンバーであるかどうかを返します。
    4. システムDMVsys.triggersは、すべてのデータベーススコープトリガーのリストを格納します。以下のクエリを使用して、データベーススコープのすべてのDDLトリガーの詳細を取得できます。
    SELECT * 
    FROM sys.triggers
    WHERE type = 'TR';
    1. システムDMVsys.server_triggersは、すべてのサーバースコープのトリガーのリストを格納します。以下のクエリを使用して、すべてのサーバースコープのDDLトリガーに関する詳細を取得できます。
    SELECT * 
    FROM sys.server_triggers;
    1. sys.sql_modulesの以下のオプションのいずれかを使用するか、OBJECT_DEFINITION()関数を使用するか、sp_helptextストアドプロシージャを使用してトリガーが暗号化されていない場合、DDLトリガー定義を表示できます。
    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>);   
    
    SELECT OBJECT_DEFINITION (OBJECT_ID(<trigger_name>)) AS ObjectDefinition; 
    
    EXEC sp_helptext '<trigger_name>';
    
    1. すべての可能なDDLイベントはsys.trigger_event_typesテーブルで利用可能であり、以下のクエリを使用して表示できます。
    SELECT *
    FROM sys.trigger_event_types;

    DDLトリガーの構文は次のとおりです。

    CREATE TRIGGER <trigger_name>
    ON < ALL SERVER | DATABASE > 
    [ WITH <DDL_trigger_option> [ ,...n ] ]  
    { FOR | AFTER } <event_type>
    AS { sql_statement | EXTERNAL NAME <method specifier> }  

    データベーススコープのDDLトリガーの作成

    以下のスクリプトを使用して、すべてのテーブル作成を追跡し、Track_DDL_Changesという名前のログテーブルにログインするデータベーススコープトリガーを作成しましょう。

    CREATE TABLE Track_DDL_Changes (EventData xml, PostDtm datetime)
    GO
    CREATE TRIGGER TR_D_CREATETABLE
    ON DATABASE
    FOR CREATE_TABLE
    AS
    BEGIN
    	INSERT INTO Track_DDL_Changes
    	SELECT EVENTDATA(),GETDATE()
    END
    GO

    以下のスクリプトを使用して、trigger_testという名前の新しいテーブルを作成し、CREATETABLEイベントが監査されたかどうかを確認しましょう。

    CREATE TABLE Trigger_Test ( a int, b datetime);

    Track_DDL_Changesテーブルからデータを選択すると、以下に示すように、上記のCREATE_TABLEイベントが正常にキャプチャされたことが示されます。

    EventData値をクリックすると、以下に示すように、新しいウィンドウでXML EVENTDATA()値が開きます。

    EVENTDATA()関数を介してトリガーイベントに関する完全な詳細を確認できるため、EVENTDATA()関数はすべてのDDLまたはLOGONトリガーに対して重要な役割を果たします。

    EVENTDATA()関数とXML解析を使用して、DDLトリガーをさらに強化し、以下のスクリプトを使用してテストデータベースにテーブルを作成できないようにすることができます。

    CREATE TRIGGER TR_D_PREVENT_CREATETABLE
    ON DATABASE
    FOR CREATE_TABLE
    AS
    BEGIN
       SELECT EVENTDATA().value  
            ('(/EVENT_INSTANCE/TSQLCommand/CommandText)[1]','nvarchar(max)')  
       RAISERROR ('Creation of New tables restricted in this database, Kindly contact DBA.', 16, 1)   
       ROLLBACK  
    END
    GO
    

    データベーススコープトリガーの作成が正常に完了しました。以下のスクリプトを使用して別のテーブルを作成して確認しましょう。

    CREATE TABLE Trigger_Test1 (a int, b datetime);

    このトリガーにより、このデータベースに新しいテーブルを作成できなくなり、ユーザーにも意味のあるメッセージが残されました。要件に合わせて、他のDDLまたはサーバースコープのイベントも同様に処理できます。

    データベーススコープのDDLトリガーを削除するには、次の構文を使用する必要があります。

    DROP TRIGGER <trigger_name> ON DATABASE;

    そして、今作成したトリガーを削除するには、スクリプトは次のようになります

    DROP TRIGGER TR_D_PREVENT_CREATETABLE ON DATABASE;

    SSMSでデータベーススコープのDDLトリガーを表示するには、以下に示すように、[テストデータベース]->[プログラマビリティ]->[データベーストリガー]を展開します。

    SQL DMLトリガーと同様に、以下に示すように、トリガー名を右クリックするだけで、DDLトリガーを削除、無効化、または有効化できます。

    T-SQLを介して、以下の構文を使用して、データベーススコープのDDLトリガーを削除または無効化または有効化できます。

    -- DROP Database scoped DDL Trigger
    DROP TRIGGER <trigger_name> ON DATABASE;
    -- Enable Database scoped DDL Trigger
    ENABLE TRIGGER <trigger_name> ON DATABASE;
    -- Disable Database scoped DDL Trigger
    DISABLE TRIGGER <trigger_name> ON DATABASE;

    作成したトリガーを無効にするには、以下のスクリプトを使用する必要がある場合があります。

    -- DROP Database scoped DDL Trigger
    DROP TRIGGER TR_D_PREVENT_CREATETABLE ON DATABASE;
    -- Enable Database scoped DDL Trigger
    ENABLE TRIGGER TR_D_PREVENT_CREATETABLE ON DATABASE;
    -- Disable Database scoped DDL Trigger
    DISABLE TRIGGER TR_D_PREVENT_CREATETABLE ON DATABASE;

    サーバースコープのDDLトリガーの作成

    サーバースコープのDDLトリガーは、イベントがサーバースコープに基づくことを除いて、データベーススコープのDDLトリガーと同じ構文に従います。

    以下のスクリプトを使用して、ユーザーがこのサーバーインスタンスに新しいデータベースを作成できないように、サーバースコープのDDLトリガーを作成してみましょう。

    CREATE TRIGGER TR_S_PREVENT_CREATEDATABASE
    ON ALL SERVER
    FOR CREATE_DATABASE
    AS
    BEGIN
       SELECT EVENTDATA().value  
            ('(/EVENT_INSTANCE/TSQLCommand/CommandText)[1]','nvarchar(max)')  
       RAISERROR ('Creation of New Databases restricted in this Instance, Kindly contact DBA.', 16, 1)   
       ROLLBACK  
    END
    GO
    

    以下のコマンドを使用して新しいデータベースを作成しようとすると、以下に示すようなエラーが発生します。

    CREATE DATABASE DATABASE_TEST;

    SSMSでは、以下に示すように、[サーバーオブジェクト]セクションの[トリガー]の下にあるサーバースコープのDDLトリガー。

    以下に示すように、サーバースコープのDDLトリガーを右クリックするだけで、サーバースコープのDDLトリガーを削除、無効化、または有効化できます。

    T-SQLを介して、以下のコマンドを使用して、ドロップ、無効化、または有効化できます。

    -- DROP Server scoped DDL Trigger
    DROP TRIGGER TR_S_PREVENT_CREATEDATABASE ON ALL SERVER;
    -- Disable Server scoped DDL Trigger
    DISABLE TRIGGER TR_S_PREVENT_CREATEDATABASE ON ALL SERVER;
    -- Enable Server scoped DDL Trigger
    ENABLE TRIGGER TR_S_PREVENT_CREATEDATABASE ON ALL SERVER;

    DDLトリガーの目的

    1. データベースまたはサーバーレベルで発生するDDLイベントを監査するため。
    2. データベースまたはサーバーレベルでDDLイベントが発生しないようにするため。
    3. データベースまたはサーバーレベルでDDLイベントが発生したときにアラートを送信します。

    ログオントリガー

    名前が示すように、ログオントリガーはSQLServerのLOGONイベントに対して実行されます。 Loginイベントの認証フェーズが完了すると、Loginアクティビティに加えてLOGONTriggerスクリプトが実行されます。ログインが正常に認証されない場合、LOGONトリガーは起動されません。ログオントリガーは、サーバーオブジェクトの[トリガー]セクションのSSMSに一覧表示されます。 LOGONトリガーの構文は次のとおりです。

    CREATE TRIGGER <schema_name.trigger_name>
    ON ALL SERVER
    { FOR| AFTER } LOGON    
    AS { sql_statement  [ ; ] [ ,...n ] | EXTERNAL NAME < method specifier >  [ ; ] }  

    トリガーを作成する

    以下に示すように、EVENTDATA()関数からLOGONイベントに関する詳細情報を取得するための単純なLOGONトリガーを作成しましょう。

    CREATE TABLE Track_LOGON_EVENTS (EventData xml, PostDtm datetime)
    GO
    CREATE TRIGGER TR_LOGON
    ON ALL SERVER
    FOR LOGON
    AS
    BEGIN
    	INSERT INTO Track_LOGON_EVENTS
    	SELECT EVENTDATA(),GETDATE();
    END
    GO

    上記のLOGONトリガーは、DDLトリガーでEVENTDATA()関数を使用しているときに気付いたのと同様のログインアクティビティに関するすべての詳細をキャプチャします。 LOGONトリガーの使用を計画する際は、トリガー内に論理エラーがあるかのように注意する必要があります。これにより、だれもほとんどのユーザーがSQLServerのインスタンスに接続できなくなります。

    LOGONトリガーを削除、無効化、または有効化するには、以下のスクリプトを使用できます。

    -- DROP LOGON Trigger
    DROP TRIGGER TR_LOGON ON ALL SERVER;
    -- Disable LOGON Trigger
    DISABLE TRIGGER TR_LOGON ON ALL SERVER;
    -- Enable LOGON Trigger
    ENABLE TRIGGER TR_LOGON ON ALL SERVER;

    LOGONトリガーの目的

    1. サーバーで発生するLOGONイベントを監査するため。
    2. サーバーでLOGONイベントが発生しないようにするため
    3. サーバーでLOGONイベントが発生したときにアラートを送信します。

    トリガープロパティ

    sp_settriggerorder

    sp_settriggerorderは、最初と最後のトリガーに対してのみトリガーの実行順序を定義するために使用されます。テーブルに3つ以上のDMLトリガーがある場合(たとえば5つのDMLトリガー)、最初のDMLトリガーと最後のDMLトリガーを定義できますが、中央の3つのトリガーの順序を定義することはできません。

    注: FIRSTまたはLASTオプションの設定は、DMLトリガーの特定のイベントカテゴリに固有です。たとえば、3つのINSERTトリガーを持つテーブルでは、どのINSERTトリガーがFIRSTで、どのINSERTトリガーがLASTであるかを定義できます。 INSERT、UPDATE、DELETEのようにテーブルに3つのトリガーがある場合、トリガーの順序条件を設定する必要はありません。

    トリガーの順序を設定するための構文は次のようになります。

    exec sp_settriggerorder @triggername = '<trigger_schema_name.trigger_name>' 
        , @order = 'FIRST' | 'LAST'   
        , @stmttype = '<trigger event type>'   
        , @namespace = 'DATABASE' | 'SERVER' | 'NULL'

    DDLトリガーの場合、最初と最後のサーバースコープトリガーを定義してから、最初と最後のデータベーススコープトリガーを定義できます。たとえば、サーバースコープのトリガーが5つ、データベーススコープのトリガーが5つある場合、順序は次のように定義できます。

    1. サーバースコープのDDLトリガーの最初のトリガー
    2. 3つのサーバースコープのDDLトリガーをランダムな順序で
    3. サーバースコープのDDLトリガーの最後のトリガー。
    4. データベーススコープのDDLトリガーの最初のトリガー(データベースごとに1つ)
    5. 3つのデータベーススコープのDDLトリガーをランダムな順序で
    6. データベーススコープのDDLトリガーの最後のトリガー。

    最初または最後のオプションの設定に関しては、データベーススコープのDDLトリガーをデータベース内で順序付けでき、サーバースコープのDDLトリガーをインスタンスレベルで並べ替えることができます。

    SQL Serverではテーブル上に多数のトリガーを作成できますが、メンテナンスとトラブルシューティングを改善するために、トリガーの要件を注意深く分析することをお勧めします。

    再帰トリガー

    SQL Serverは、DMLトリガーの再帰的なトリガーの呼び出しもサポートしています。再帰トリガーは、以下に示すように、直接または間接に分類できます。

    直接再帰トリガー –ユーザーまたはアプリケーションがテーブルAのレコードを更新します。テーブルAのUPDATEトリガーAが起動され、テーブルAが再度更新されます。表Aのレコードはトリガーを介して更新されたため、再度UPDATEトリガーAが呼び出され、これが再帰的に発生します。

    次のスクリプトを使用して、Salesテーブルに直接再帰トリガーを作成しましょう。

    CREATE TRIGGER TR_UPD_Recursive_Sales ON Sales
    FOR UPDATE 
    AS
    BEGIN
      UPDATE Sales 
      SET SalesDate = GETDATE() 
      WHERE SalesId = (SELECT SalesId FROM Inserted)
    END
    GO

    以下のスクリプトを実行します:

    UPDATE Sales 
    SET SalesDate = GETDATE() 
    WHERE SalesId = 3;

    間接再帰トリガー –ユーザーまたはアプリケーションがテーブルAのレコードを更新します。テーブルAのUPDATEトリガーAが起動され、テーブルBのレコードが更新されます。テーブルBにレコードを更新してテーブルAに戻すUPDATEトリガーがある場合、再帰的に発生する表A。

    次のスクリプトを使用して、IDR_Test1テーブルとIDR_Test2テーブルに間接再帰トリガーを作成しましょう。

    DROP TABLE IDR_Test1
    DROP TABLE IDR_Test2
    
    CREATE TABLE IDR_Test1 (PK int NOT NULL);
    GO
    INSERT INTO IDR_Test1 
    values (10),(20)
    GO
    CREATE TABLE IDR_Test2 (PK int NOT NULL);
    GO
    INSERT INTO IDR_Test2
    values (10),(20)
    GO
    
    CREATE TRIGGER TR_IDR_Test1
    ON IDR_Test1
    FOR UPDATE 
    AS
    BEGIN
    	UPDATE IDR_Test2
    	SET PK = 30
    	WHERE PK IN (SELECT PK FROM inserted);
    END
    GO
     
    CREATE TRIGGER TR_Temp2
    ON IDR_Test2
    FOR UPDATE 
    AS
    BEGIN
    	UPDATE IDR_Test1
    	SET PK = 30
    	WHERE PK IN (SELECT PK FROM inserted);
    END
    GO

    以下のスクリプトを実行します:

    UPDATE IDR_Test1
    SET PK = 1
    WHERE PK = 10;

    データベースレベルでのこれらの種類の再帰的トリガーの呼び出しを回避するために、SQL Serverには、すべてのデータベースレベルでRECURSIVE_TRIGGERSと呼ばれるオプションがあり、再帰的トリガーのトリガーを解除します。デフォルトでは、データベースの再帰トリガーオプションはFalseに設定されています。パフォーマンスへの影響や関連するデータの変更を慎重に検討した後、必要に応じてのみ有効にしてください。

    SSMSで、テストデータベースを右クリックし、[プロパティ]を選択します。 ->オプションをクリックします 下にスクロールして、[再帰トリガー]オプションが有効になっているかどうかを確認します。テストデータベースの場合、FalseがRecursive Triggersオプションのデフォルト値であるため、Falseに設定されます。特定のデータベースの[再帰トリガー]オプションをオンにするには、ドロップダウン値をクリックしてTrueに変更し、[OK]をクリックします。

    以下に示すように、T-SQLを介して、sys.databases DMVのis_recursive_triggers_on列を確認することにより、テストデータベースの再帰トリガーオプションを確認できます。

    select name, is_recursive_triggers_on
    from sys.databases
    where name = 'test'

    データベースの再帰トリガーオプションを変更するには(私の例ではテスト)、以下のスクリプトを実行できます。

    ALTER DATABASE [Test] SET RECURSIVE_TRIGGERS ON WITH NO_WAIT
    GO

    データベースのfalseステータス(デフォルトステータス)に戻すには(私の例ではテスト)、以下のスクリプトを実行します。

    ALTER DATABASE [Test] SET RECURSIVE_TRIGGERS OFF WITH NO_WAIT
    GO
    

    ネストされたトリガー

    再帰的トリガーはネストされたトリガーの典型的な例ですが、複数のトリガーのネストをもたらす他のケースはほとんどありません。 SQL Serverでは、最大32レベルまでのトリガーのネストが許可されており、そのネストレベルを超えるトリガーはSQLServerによってキャンセルされます。 SQL Serverには、ネストされたトリガーオプションを無効にするインスタンス全体の構成があります。 CLRコードまたはマネージコードを使用したSQLServerトリガーのネストは、SQL Serverの範囲外であるため、32レベルの制限に該当しないことに注意してください。デフォルトでは、ネストされたトリガーオプションはすべてのSQL Serverインスタンスで有効になり、必要に応じて無効にできます。

    次の手順に従って、ネストされたトリガーオプションがSSMSのインスタンスレベルで有効になっているかどうかを確認できます。

    サーバーを右クリックします ->プロパティを選択します ->詳細をクリックします

    ネストされたトリガーオプションを無効またはオフにするには、ドロップダウンをクリックしてFalseに変更し、 OKをクリックします。

    T-SQLを介して、ネストされたトリガーの構成名についてsys.configurations DMVのvalue_in_use列を確認することにより、ネストされたトリガーオプションが有効になっているかどうかを確認できます。

    このオプションを無効にするには、以下に示すように、sp_configureシステムストアドプロシージャを使用する必要があります。

    EXEC sp_configure 'nested triggers', 0;  
    GO  
    RECONFIGURE;  
    GO  
    

    DMLまたはDDLトリガー内で、ネストの現在のレベルを見つけるために、SQL Serverには、トリガーを起動した現在のステートメントに対して実行されたトリガーの数を返すTRIGGER_NESTLEVELという名前の組み込み関数があります。 TRIGGER_NESTLEVEL関数の構文は次のようになります。

    SELECT TRIGGER_NESTLEVEL ( object_id, <trigger_type> , <trigger_event_category> )

    object_idがトリガーのオブジェクトIDである場合、trigger_typeはAFTERトリガーの場合はAFTER、INSTEAD OFトリガーの場合はIOTになり、trigger_event_categoryはDMLまたはDDLのいずれかになります。

    たとえば、ネストレベルのみを10まで許可し、10レベル以降はエラーを発生させる必要がある場合は、次のようにテストトリガーで実行できます。

    IF ((SELECT TRIGGER_NESTLEVEL(OBJECT_ID('test_trigger'), 'AFTER’, 'DML’)) > 10)  
       RAISERROR ('Trigger test_trigger nested more than 10 levels.',16, -1)   

    暗号化

    トリガーロジックまたは定義を暗号化するために、WITH ENCRYPTIONオプションを、他のすべてのSQLServerオブジェクトと同様にトリガー定義で使用できます。

    EXECUTEAS句

    特定のセキュリティコンテキストを使用してトリガーを実行するには、トリガー定義でEXECUTEAS句を使用できます。

    複製不可

    レプリケーションの変更によって実行されているときにDMLトリガーが呼び出されないようにするために、サブスクライバデータベース内のすべてのオブジェクトにNOTFORREPLICATIONプロパティが設定されます。

    結論

    DDLトリガーとログオントリガーの目的、これらのトリガーを作成または削除、無効化、または有効化する方法、およびEVENTDATA()関数を使用する方法を理解した、DDLトリガーとログオントリガーに関する強力な記事を読んでいただきありがとうございます。 DDLまたはログオンアクティビティの追跡。それに加えて、複数のSQL DMLまたはDDLトリガーの実行順序を、再帰トリガーおよびネストトリガーとともに詳細に設定する方法と、再帰トリガーまたはネストトリガーを慎重に処理する方法を学習しました。


    1. OracleDBAのMySQLバックアップを実行および管理する方法

    2. node-oracledbを使用してLDAPサーバーに接続するにはどうすればよいですか?

    3. AndroidのSQLiteデータベースにJSONデータを挿入します

    4. 関数内のSELECTまたはINSERTは競合状態になりやすいですか?