sql >> Databasteknik >  >> RDS >> Sqlserver

SQL Server Triggers – Del 2 DDL &LOGON Triggers

I SQL Server är utlösare databasobjekt som kommer att exekveras när en utlösande händelse inträffar på databasen eller servern. Triggers spelar en nyckelroll för att uppnå affärskrav som att varna riktade personer baserat på ett uppnått tillstånd, att starta ett jobb eller annan verksamhet. I den tidigare artikeln om DML-triggers pratade vi om triggers, typer av triggers och olika triggeralternativ som finns tillgängliga för DML-triggers. I den här artikeln kommer vi att utforska SQL DDL- och LOGON-utlösare.

DDL-utlösare

DDL-utlösare kan aktiveras för en mängd olika server- eller databasomfattande händelser inklusive både DDL- och DCL-kommandon. DDL står för Data Definition Language som används för att CREATE, ALTER, DROP alla objekt och DCL står för Data Control Language-satser som GRANT, DENY och REVOKE-kommandon. Nedan är egenskaperna hos SQL DDL-utlösare.

  1. DDL-utlösare kan skapas på databasnivå eller serverinstansnivå som täcker en mängd olika DDL-operationer eller DDL-liknande operationer, t.ex. DCL-kommandon.
  2. DDL-utlösare kan endast anropas eller aktiveras som en FOR- eller EFTER-utlösartyp. SQL Server stöder inte I STÄLLET FÖR DDL Trigger och vi kan se hur man förhindrar vissa DDL-operationer via DDL Triggers.
  3. SQL-servern har inbyggda funktioner som EVENTDATA() och IS_MEMBER() för användning inom DDL-utlösare för att få mer information relaterad till triggerhändelserna.
    1. EVENTDATA()-funktionen returnerar fullständig information om databas- eller serveromfattade händelser i XML-format inom ramen för databas- eller serveromfångade DDL-utlösare eller inloggningsutlösare. Funktionen EVENTDATA() returnerar alla händelsedetaljer för den session som utför DDL- eller inloggningsaktiviteterna. EVENTDATA() returnerar nedanstående detaljer
      • EventType – Typ av händelse som aktiverar DDL-utlösaren som finns i tabellen sys.trigger_event_types.
      • PostTime – Tid då händelsen triggades eller lades upp.
      • SPID – Sessions-ID för händelsen.
      • Servernamn – SQL Server-instansnamn där händelsen utlöstes.
      • LoginName – SQL Server Inloggningsnamn som utförde händelsen.
      • Användarnamn – Användarnamn för inloggningen som kommer att vara dbo som standard.
      • Databasnamn – Databasnamn under vilket DDL-utlösaren aktiverades.
      • SchemaName – Schema Namn på objektet som påverkades.
      • Objektnamn – Objektnamn som påverkades.
      • ObjectType – Typ av SQL Server-objekt som tabell, vy, lagrad procedur.
      • TSQLCommand – T-SQL-skript som kördes av en användare som anropade DDL-utlösaren.
      • SetOptions – SET-alternativ som används av användare eller klient som SSMS medan TSQLCommand kördes.
      • CommandText – Faktiska DDL- eller DCL-satser med DDL-händelsen specificerad i tabellen sys.trigger_event_types.
    2. IS_MEMBER()-funktionen returnerar om den aktuella användaren är medlem i Windows-gruppen eller SQL Server Database-rollen eller inte.
  4. System DMV sys.triggers lagrar listan över alla utlösare med databasomfattning. Vi kan använda frågan nedan för att hämta information om alla databasomfattade DDL-utlösare.
SELECT * 
FROM sys.triggers
WHERE type = 'TR';
  1. System DMV sys.server_triggers lagrar listan över alla Server scoped triggers och vi kan använda nedanstående fråga för att hämta information om alla Server scoped DDL triggers.
SELECT * 
FROM sys.server_triggers;
  1. DDL-utlösardefinitioner kan visas om utlösaren inte är krypterad genom att använda något av alternativen nedan från sys.sql_modules eller använda funktionen OBJECT_DEFINITION() eller använda sp_helptext lagrad procedur.
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. Alla möjliga DDL-händelser är tillgängliga i tabellen sys.trigger_event_types och kan ses med hjälp av frågan nedan.
SELECT *
FROM sys.trigger_event_types;

Syntaxen för en DDL-utlösare är:

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

Skapa DDL-utlösare för en databas

Låt oss skapa en utlösare för databasomfattning för att spåra alla tabellskapande och logga in i en loggningstabell med namnet Track_DDL_Changes med skriptet nedan.

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

Låt oss skapa en ny tabell med namnet trigger_test och verifiera om CREATE TABLE-händelsen granskades eller inte genom att använda skriptet nedan.

CREATE TABLE Trigger_Test ( a int, b datetime);

Att välja data från Track_DDL_Changes-tabellen visar att ovanstående CREATE_TABLE-händelse registrerades framgångsrikt enligt nedan:

Om du klickar på EventData-värdet öppnas XML EVENTDATA()-värdet i ett nytt fönster som visas nedan.

Vi kan verifiera den fullständiga informationen om den utlösande händelsen via funktionen EVENTDATA() och därför skulle funktionen EVENTDATA() spela en viktig roll för alla DDL- eller LOGON-utlösare.

Vi kan ytterligare förbättra vår DDL-utlösare med hjälp av funktionen EVENTDATA() och XML-tolkning och förhindra att någon skapar någon tabell i testdatabasen med skriptet nedan:

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

Skapandet av utlösaren med databasomfattning slutfördes och låt oss verifiera genom att skapa en annan tabell med skriptet nedan.

CREATE TABLE Trigger_Test1 (a int, b datetime);

Utlösaren har hindrat oss från att skapa nya tabeller i denna databas och lämnat ett meningsfullt meddelande till användarna också. Vi kan på liknande sätt hantera alla andra DDL- eller serveromfattade händelser för att matcha kraven.

För att släppa DDL-utlösaren med databasomfattning måste vi använda syntaxen nedan:

DROP TRIGGER <trigger_name> ON DATABASE;

Och för att släppa triggern som vi har skapat just nu, skulle skriptet vara

DROP TRIGGER TR_D_PREVENT_CREATETABLE ON DATABASE;

För att se de databasomfattade DDL-utlösarna i SSMS, expandera Testdatabasen -> Programmerbarhet -> Databasutlösare som visas nedan.

I likhet med SQL DML-utlösare kan DDL-utlösare släppas, inaktiveras eller aktiveras genom att bara högerklicka på utlösarens namn som visas nedan.

Via T-SQL kan vi släppa eller inaktivera eller aktivera DDL-utlösare med databasomfattning med hjälp av syntaxen nedan:

-- 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;

För att inaktivera triggern vi har skapat kan vi behöva använda skriptet nedan.

-- 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;

Skapa DDL-utlösare med serveromfattning

Serveromfångad DDL-utlösare följer samma syntax som liknar DDL-utlösaren för databasomfång, förutom att händelserna kommer att baseras på serveromfånget.

Låt oss försöka skapa en DDL-utlösare med serveromfattning för att förhindra att någon användare skapar en ny databas på den här serverinstansen med hjälp av skriptet nedan.

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

När vi försöker skapa en ny databas med kommandot nedan kommer vi att få ett felmeddelande som visas nedan.

CREATE DATABASE DATABASE_TEST;

I SSMS omfångade servern DDL-utlösare under utlösare i avsnittet Serverobjekt som visas nedan.

Vi kan ta bort, inaktivera eller aktivera den serveromfattade DDL-utlösaren genom att helt enkelt högerklicka på serveromfångad DDL-utlösare som visas nedan.

Via T-SQL kan vi släppa eller inaktivera eller aktivera med kommandot nedan.

-- 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;

Syftet med DDL-utlösare

  1. För att granska eventuella DDL-händelser som händer på databas- eller servernivå.
  2. För att förhindra att DDL-händelser inträffar på databas- eller servernivå.
  3. För att varna när DDL-händelser inträffar på databas- eller servernivå.

Inloggningsutlösare

Inloggningsutlösare som namnet indikerar exekveras för LOGON-händelser i SQL Server. När autentiseringsfasen är klar för en inloggningshändelse kommer LOGON Trigger-skriptet att köras utöver inloggningsaktiviteten. Om inloggningen inte har autentiserats framgångsrikt kommer LOGON-utlösare inte att aktiveras. Inloggningsutlösare kommer att listas i SSMS under avsnittet Utlösare i Serverobjekt. Syntaxen för en LOGON Trigger är följande:

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

Skapa utlösare

Låt oss skapa en enkel LOGON-utlösare för att fånga mer information om LOGON-händelsen från funktionen EVENTDATA() som visas nedan.

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

Ovanstående LOGON-utlösare kommer att fånga all information om en inloggningsaktivitet som liknar vad vi har märkt när vi använder funktionen EVENTDATA() i DDL Trigger. Vi bör vara försiktiga när vi planerar att använda LOGON-utlösare som om det finns några logiska fel inuti utlösaren, det skulle inte tillåta någon eller de flesta användare att ansluta till instansen av SQL Server.

För att släppa, inaktivera eller aktivera LOGON-utlösare kan vi använda skriptet nedan.

-- 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;

Syftet med LOGON-utlösare

  1. För att granska eventuella LOGON-händelser som händer på servern.
  2. För att förhindra att LOGON-händelser inträffar på servern
  3. För att varna varje gång LOGON-händelser händer på servern.

Triggeregenskaper

sp_settriggerorder

sp_settriggerorder används för att definiera ordningen för triggerexekvering endast för den första och sista triggern. Om det finns fler än 2 DML-utlösare i en tabell, låt oss säga 5 DML-utlösare, kan vi definiera den första DML-utlösaren och den sista DML-utlösaren men kan inte definiera ordningen på de tre mittersta utlösarna.

Obs! Att ställa in FIRST eller LAST-alternativet är specifikt för en viss händelsekategori för DML-utlösare. Till exempel i en tabell med 3 INSERT-triggers, kan vi definiera vilken INSERT-trigger som är FIRST och vilken INSERT-trigger som är LAST. Om du har tre triggers på en tabell som INSERT, UPDATE och DELETE, behöver du inte ställa in triggerordningen.

Syntaxen för att ställa in triggerordningen skulle vara så här:

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

För DDL-utlösare kan vi definiera den första och sista Server Scoped-utlösaren och sedan definiera den första och den sista Databas Scoped-utlösaren. Till exempel, om vi har 5 utlösare med serveromfattning och fem utlösare med databasomfattning, kan ordningen definieras så här:

  1. Första trigger för Server Scoped DDL-utlösare
  2. 3 andra DDL-utlösare med serveromfattning i slumpmässig ordning
  3. Sista trigger för Server Scoped DDL-utlösare.
  4. Första trigger för DDL-utlösare med databasomfattning (en per databas)
  5. 3 andra DDL-utlösare med databasomfattning i slumpmässig ordning
  6. Sista utlösare för DDL-utlösare med databas.

När det gäller att ställa in det första eller det sista alternativet, kan de databasomfattade DDL-utlösarna beställas inom databasen och de serveromfångade DDL-utlösarna på instansnivå.

Även om SQL Server tillåter oss att skapa många triggers på ett bord, rekommenderas det att analysera utlösarens krav noggrant för bättre underhåll och felsökning.

Rekursiva utlösare

SQL Server stöder också att anropa utlösare rekursivt för DML-utlösare. Rekursiva triggers kan klassificeras som direkta eller indirekta som visas nedan.

Direkt rekursiva utlösare – Användare eller applikation uppdaterar en post i tabell A. UPPDATERING Trigger A i tabell A avfyras och uppdaterar tabell A igen. Eftersom posten i Tabell A uppdaterades via Trigger, kommer den att anropa UPPDATERING Trigger A igen och detta kommer att ske rekursivt.

Låt oss skapa en direkt rekursiv utlösare i försäljningstabellen med hjälp av skriptet nedan:

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

Kör skriptet nedan:

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

Indirekta rekursiva utlösare – Användare eller applikation uppdaterar en post i tabell A. UPDATE-triggern A i tabell A aktiveras och uppdaterar en post i tabell B. Om tabell B har en UPDATE-trigger för att uppdatera poster tillbaka till tabell A, kommer den att anropa UPDATE-triggern i Tabell A som kommer att ske rekursivt.

Låt oss skapa en indirekt rekursiv utlösare på IDR_Test1- och IDR_Test2-tabeller med skriptet nedan:

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

Kör skriptet nedan:

UPDATE IDR_Test1
SET PK = 1
WHERE PK = 10;

För att undvika dessa typer av anrop av rekursiva utlösare på databasnivå har SQL Server ett alternativ som heter RECURSIVE_TRIGGERS på varje databasnivå för att bryta utlösandet av rekursiva utlösare. Som standard är alternativet Rekursiv trigger inställt på False för en databas. Aktivera endast vid behov efter noggrant övervägande av prestandapåverkan eller dataändringar.

I SSMS, högerklicka på vår testdatabas -> Välj Egenskaper -> Klicka på Alternativ och scrolla ner för att se alternativet Rekursiva utlösare är aktiverat eller inte som visas nedan. För testdatabas är den inställd på False eftersom False är standardvärdet för alternativet Rekursiva utlösare. För att aktivera alternativet Rekursiva utlösare för en specifik databas, klicka bara på rullgardinsmenyn, ändra det till True och klicka på OK.

Via T-SQL kan vi verifiera alternativet Rekursiv utlösare i testdatabasen genom att markera kolumnen is_recursive_triggers_on från sys.databases DMV som visas nedan.

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

För att ändra alternativet Rekursiva utlösare för en databas (Testa i mitt exempel), kan vi köra skriptet nedan.

ALTER DATABASE [Test] SET RECURSIVE_TRIGGERS ON WITH NO_WAIT
GO

För att inaktivera den tillbaka till falsk status (standardstatus) för en databas (testa i mitt exempel), kör följande skript.

ALTER DATABASE [Test] SET RECURSIVE_TRIGGERS OFF WITH NO_WAIT
GO

Inkapslade utlösare

Rekursiva utlösare är ett klassiskt exempel på kapslade utlösare, men det kan finnas få andra fall som leder till kapsling av flera utlösare. SQL Server tillåter kapsling av utlösare ner till maximalt 32 nivåer och alla utlösare som överskrider den kapslingsnivån kommer att avbrytas av SQL Server. SQL Server har en instansomfattande konfiguration för att inaktivera alternativet Kapslade utlösare. Observera att inkapsling av SQL Server-utlösare med CLR-kod eller hanterad kod inte faller under gränsen på 32 nivåer eftersom det ligger utanför SQL-serverns omfattning. Som standard kommer alternativet för kapslade utlösare att vara aktiverat i alla SQL Server-instanser och vi kan inaktivera det efter behov.

Vi kan verifiera om alternativet för kapslade utlösare är aktiverat på instansnivå i SSMS genom att följa stegen nedan:

Högerklicka på Server -> Välj Egenskaper -> Klicka på Avancerat

Om du vill inaktivera eller stänga av alternativet för kapslade utlösare klickar du på rullgardinsmenyn och ändrar den till Falskt och klickar på OK .

Via T-SQL kan vi verifiera om alternativet Nested triggers är aktiverat genom att kontrollera kolumnen value_in_use i sys.configurations DMV för kapslade triggers konfigurationsnamn.

För att inaktivera det här alternativet måste vi använda sp_configure den systemlagrade proceduren som visas nedan:

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

Inom alla DML- eller DDL-utlösare, för att hitta den aktuella nivån av kapsling, har SQL Server en inbyggd funktion som heter TRIGGER_NESTLEVEL för att returnera antalet triggers som exekveras för den aktuella satsen som aktiverade utlösaren inklusive sig själv. Syntaxen för TRIGGER_NESTLEVEL-funktionen skulle vara:

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

Där object_id är objekt-id för utlösaren, kommer trigger_type att vara AFTER för AFTER trigger och IOT för INSTEAD OF trigger och trigger_event_category kommer att vara antingen DML eller DDL.

Om vi ​​till exempel bara behöver tillåta kapslingsnivå till 10 och höja felet efter 10 nivåer, kan vi göra det på testtrigger som här:

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

KRYPTERING

För att kryptera triggerlogiken eller definitionen, kan WITH ENCRYPTION-alternativet användas i triggerdefinition som liknar alla andra SQL Server-objekt.

EXEKUT SOM-klausul

För att exekvera triggern med ett specifikt säkerhetskontext kan EXECUTE AS-satsen användas i triggerdefinitionen.

INTE FÖR REPLIKATION

För att identifiera att DML-utlösaren inte ska anropas när den körs via replikeringsändringar, kommer egenskapen NOT FOR REPICATION att ställas in för alla objekt i abonnentdatabasen.

Slutsats

Tack för att du gick igenom den kraftfulla artikeln om DDL-utlösare och inloggningsutlösare där vi har förstått syftet med DDL- och inloggningsutlösare, hur man skapar eller släpper, inaktiverar eller aktiverar dessa utlösare tillsammans med hur man använder EVENTDATA()-funktionen för spåra DDL- eller inloggningsaktiviteter. Utöver det har vi lärt oss hur man ställer in exekveringsordningen för flera SQL DML- eller DDL-utlösare tillsammans med rekursiva och kapslade utlösare i detalj och hur man hanterar rekursiva eller kapslade utlösare noggrant.


  1. PostgreSQL:Vilken datatyp ska användas för valuta?

  2. Hur CRC32 fungerar i MariaDB

  3. ) Operatör för nybörjare

  4. 60 miljoner poster, välj poster från en viss månad. Hur optimerar man databasen?