sql >> Databáze >  >> RDS >> Sqlserver

Spouštěče SQL Server – Část 2 Spouštěče DDL &LOGON

Na serveru SQL Server jsou spouštěče databázové objekty, které se spouštějí vždy, když na databázi nebo serveru dojde ke spouštěcí události. Spouštěče hrají klíčovou roli při dosahování obchodních požadavků, jako je upozornění cílených lidí na základě dosaženého stavu, zahájení práce nebo jiných operací. V předchozím článku o spouštěčích DML jsme hovořili o spouštěčích, typech spouštěčů a různých možnostech spouštění dostupných pro spouštěče DML. V tomto článku prozkoumáme spouštěče SQL DDL a LOGON.

Spouštěče DDL

Spouštěče DDL lze spouštět pro různé události v rozsahu serveru nebo databáze, včetně příkazů DDL i DCL. DDL je zkratka pro Data Definition Language, která se používá k CREATE, ALTER, DROP jakékoli objekty a DCL je zkratka pro příkazy Data Control Language, jako jsou příkazy GRANT, DENY a REVOKE. Níže jsou uvedeny charakteristiky spouštěčů SQL DDL.

  1. Spouštěče DDL lze vytvářet na úrovni databáze nebo na úrovni instance serveru, které pokrývají širokou škálu operací DDL nebo podobných operací DDL, např. příkazy DCL.
  2. Spouštěče DDL lze vyvolat nebo spustit pouze jako typ spouštěče FOR nebo AFTER. SQL Server MÍSTO DDL Trigger nepodporuje a my vidíme, jak zabránit některým DDL operacím pomocí DDL Triggers.
  3. SQL Server má vestavěné funkce jako EVENTDATA() a IS_MEMBER() pro použití v rámci spouštěčů DDL k získání dalších informací souvisejících s událostmi spouštění.
    1. Funkce EVENTDATA() vrací úplné podrobnosti o událostech v rozsahu databáze nebo serveru ve formátu XML v rozsahu spouštěče DDL v rozsahu databáze nebo serveru nebo také spouštěče přihlášení. Funkce EVENTDATA() vrací úplné podrobnosti o události pro relaci, která provádí aktivity DDL nebo přihlášení. EVENTDATA() vrátí níže uvedené podrobnosti
      • EventType – Typ události, která spustí spouštěč DDL dostupný v tabulce sys.trigger_event_types.
      • PostTime – čas, kdy byla událost spuštěna nebo odeslána.
      • SPID – ID relace události.
      • ServerName – název instance SQL Server, ve které byla událost spuštěna.
      • LoginName – Přihlašovací jméno serveru SQL, které provedlo událost.
      • UserName – uživatelské jméno pro přihlášení, které bude ve výchozím nastavení dbo.
      • DatabaseName – Název databáze, pod kterým byl spuštěn DDL Trigger.
      • SchemaName – Název schématu objektu, který byl ovlivněn.
      • ObjectName – Název objektu, který byl ovlivněn.
      • ObjectType – Typ objektu SQL Server, jako je tabulka, zobrazení, uložená procedura.
      • TSQLCommand – T-SQL skript, který byl spuštěn uživatelem, který vyvolal DDL Trigger.
      • SetOptions – možnosti SET používané uživatelem nebo klientem, jako je SSMS při provádění příkazu TSQLCommand.
      • CommandText – Aktuální příkazy DDL nebo DCL s událostí DDL specifikovanou v tabulce sys.trigger_event_types.
    2. Funkce IS_MEMBER() vrací, zda je aktuální uživatel členem skupiny Windows nebo role databáze SQL Server či nikoli.
  4. Systém DMV sys.triggers ukládá seznam všech spouštěčů v rozsahu databáze. Níže uvedený dotaz můžeme použít k načtení podrobností všech spouštěčů DDL v rozsahu databáze.
SELECT * 
FROM sys.triggers
WHERE type = 'TR';
  1. System DMV sys.server_triggers ukládá seznam všech spouštěčů v rozsahu serveru a my můžeme použít níže uvedený dotaz k načtení podrobností o všech spouštěčích DDL v rozsahu serveru.
SELECT * 
FROM sys.server_triggers;
  1. Definice spouštěče DDL lze zobrazit, pokud spouštěč není zašifrován pomocí některé z níže uvedených možností z sys.sql_modules nebo pomocí funkce OBJECT_DEFINITION() nebo pomocí uložené procedury sp_helptext.
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. Všechny možné události DDL jsou dostupné v tabulce sys.trigger_event_types a lze je zobrazit pomocí níže uvedeného dotazu.
SELECT *
FROM sys.trigger_event_types;

Syntaxe spouštěče DDL je:

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

Vytvoření spouštěče DDL v rozsahu databáze

Vytvořme spouštěč Database Scoped pro sledování všech vytvoření tabulek a pomocí níže uvedeného skriptu se přihlaste do protokolovací tabulky s názvem 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

Vytvořme novou tabulku s názvem trigger_test a pomocí níže uvedeného skriptu ověřte, zda byla událost CREATE TABLE auditována či nikoli.

CREATE TABLE Trigger_Test ( a int, b datetime);

Výběr dat z tabulky Track_DDL_Changes ukazuje, že výše uvedená událost CREATE_TABLE byla úspěšně zachycena, jak je uvedeno níže:

Kliknutím na hodnotu EventData otevřete hodnotu XML EVENTDATA() v novém okně, jak je uvedeno níže.

Jsme schopni ověřit úplné podrobnosti o spouštěcí události pomocí funkce EVENTDATA(), a proto by funkce EVENTDATA() hrála významnou roli pro všechny spouštěče DDL nebo LOGON.

Náš DDL Trigger můžeme dále vylepšit pomocí funkce EVENTDATA() a analýzy XML a zabránit komukoli ve vytváření jakékoli tabulky v testovací databázi pomocí skriptu uvedeného níže:

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

Vytvoření spouštěče Database Scoped bylo úspěšně dokončeno a ověřte vytvořením další tabulky pomocí skriptu níže.

CREATE TABLE Trigger_Test1 (a int, b datetime);

Spouštěč nám zabránil vytvořit nové tabulky v této databázi a zanechal smysluplnou zprávu i uživatelům. Podobně můžeme zpracovat jakékoli jiné události v rozsahu DDL nebo serveru, aby odpovídaly požadavkům.

Chcete-li zrušit spouštěč DDL v rozsahu databáze, musíme použít níže uvedenou syntaxi:

DROP TRIGGER <trigger_name> ON DATABASE;

A pro zrušení spouštěče, který jsme právě vytvořili, by skript byl

DROP TRIGGER TR_D_PREVENT_CREATETABLE ON DATABASE;

Chcete-li zobrazit spouštěče DDL v rozsahu databáze v SSMS, rozbalte Test databáze -> Programovatelnost -> Spouštěče databáze, jak je uvedeno níže.

Podobně jako u spouštěčů SQL DML lze spouštěče DDL zrušit, zakázat nebo povolit pouhým kliknutím pravým tlačítkem na název spouštěče, jak je znázorněno níže.

Prostřednictvím T-SQL můžeme zrušit nebo zakázat nebo povolit spouštění DDL v rozsahu databáze pomocí níže uvedené syntaxe:

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

K deaktivaci spouštěče, který jsme vytvořili, možná budeme muset použít níže uvedený skript.

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

Vytvoření spouštěče DDL v rozsahu serveru

Spouštěč DDL v rozsahu serveru se řídí stejnou syntaxí jako spouštěč DDL rozsahu databáze s tím rozdílem, že události budou založeny na rozsahu serveru.

Zkusme vytvořit spouštěč DDL v rozsahu serveru, který zabrání jakémukoli uživateli ve vytváření nové databáze na této instanci serveru pomocí níže uvedeného skriptu.

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

Při pokusu o vytvoření nové databáze pomocí níže uvedeného příkazu se zobrazí chyba, jak je uvedeno níže.

CREATE DATABASE DATABASE_TEST;

V SSMS server v rozsahu Spouštěče DDL v části Spouštěče v části Objekty serveru, jak je uvedeno níže.

Můžeme zrušit, zakázat nebo povolit spouštěč DDL v rozsahu serveru jednoduchým kliknutím pravým tlačítkem na spouštěč DDL v rozsahu serveru, jak je znázorněno níže.

Prostřednictvím T-SQL můžeme pomocí níže uvedeného příkazu zahodit nebo zakázat nebo povolit.

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

Účel spouštěčů DDL

  1. Auditovat jakékoli události DDL probíhající na úrovni databáze nebo serveru.
  2. Aby se zabránilo jakýmkoli událostem DDL na úrovni databáze nebo serveru.
  3. Upozornit, kdykoli dojde k jakékoli události DDL na úrovni databáze nebo serveru.

Spouštěče přihlášení

Spouštěče přihlášení, jak naznačuje název, se spouštějí pro události LOGON na serveru SQL Server. Po dokončení fáze ověřování pro událost přihlášení se kromě aktivity přihlášení spustí také skript LOGON Trigger. Pokud přihlášení není úspěšně ověřeno, spouštěče LOGON se nespustí. Přihlašovací spouštěče budou uvedeny v SSMS v části Spouštěče v Serverových objektech. Syntaxe LOGON Trigger je následující:

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

Vytvořit spouštěče

Vytvořme jednoduchý spouštěč LOGON pro zachycení dalších informací o události LOGON z funkce EVENTDATA(), jak je uvedeno níže.

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

Výše uvedený spouštěč LOGON zachytí všechny podrobnosti o přihlašovací aktivitě podobné tomu, co jsme zaznamenali při používání funkce EVENTDATA() v DDL Trigger. Při plánování použití spouštěčů LOGON bychom měli být opatrní, protože pokud jsou uvnitř spouštěče nějaké logické chyby, nikomu nebo většině uživatelů by to neumožnilo připojit se k instanci SQL Server.

Chcete-li DROP, Disable nebo Enable LOGON triggery, můžeme použít níže uvedený skript.

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

Účel spouštěčů LOGON

  1. Pro audit všech událostí LOGON probíhajících na serveru.
  2. Chcete-li zabránit jakýmkoli událostem LOGON na serveru
  3. Upozornit, kdykoli na serveru dojde k jakékoli události LOGON.

Vlastnosti spouštěče

sp_settriggerorder

sp_settriggerorder se používá k definování pořadí spouštění spouště pouze pro první a poslední spouštěče. Pokud jsou v tabulce více než 2 spouštěče DML, řekněme 5 spouštěčů DML, pak můžeme definovat první spouštěč DML a poslední spouštěč DML, ale nemůžeme definovat pořadí prostředních 3 spouštěčů.

Poznámka: Nastavení možnosti FIRST nebo LAST je specifické pro konkrétní kategorii událostí pro spouštěče DML. Například v tabulce se 3 INSERT triggery můžeme definovat, který INSERT trigger je FIRST a který INSERT trigger je LAST. Pokud máte v tabulce 3 spouštěče, jako jsou INSERT, UPDATE a DELETE, pak není potřeba nastavovat podmínku Trigger order.

Syntaxe pro nastavení pořadí spouštění by byla tato:

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

Pro spouštěče DDL můžeme definovat první a poslední spouštěč v rozsahu serveru a poté definovat první a poslední spouštěč v rozsahu databáze. Máme-li například 5 spouštěčů v rozsahu serveru a 5 spouštěčů v rozsahu databáze, lze pořadí definovat takto:

  1. První spouštěč pro spouštěč DDL v rozsahu serveru
  2. 3 další spouštěče DDL s rozsahem serveru v náhodném pořadí
  3. Poslední spouštěč pro spouštěč DDL v rozsahu serveru.
  4. První spouštěč pro spouštěč DDL v rozsahu databáze (jeden na databázi)
  5. 3 další spouštěče DDL s rozsahem databáze v náhodném pořadí
  6. Poslední spouštěč pro spouštěč DDL s rozsahem databáze.

S ohledem na nastavení první nebo poslední možnosti lze spouštěče DDL s rozsahem databáze seřadit v rámci databáze a spouštěče DDL s rozsahem pro server na úrovni instance.

Přestože nám SQL Server umožňuje vytvářet velké množství spouštěčů na stole, doporučujeme pečlivě analyzovat požadavky spouštěče pro lepší údržbu a odstraňování problémů.

Rekurzivní spouštěče

SQL Server také podporuje rekurzivní vyvolání spouštěčů pro spouštěče DML. Rekurzivní spouštěče lze klasifikovat jako přímé nebo nepřímé, jak je uvedeno níže.

Přímé rekurzivní spouštěče – Uživatel nebo aplikace aktualizuje záznam v tabulce A. Aktivuje se spouštěč UPDATE A v tabulce A a znovu aktualizuje tabulku A. Vzhledem k tomu, že záznam v tabulce A byl aktualizován prostřednictvím Triggeru, znovu vyvolá UPDATE Trigger A a to se stane rekurzivně.

Pojďme vytvořit přímé rekurzivní spouštěče v tabulce Prodej pomocí níže uvedeného skriptu:

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

Spusťte níže uvedený skript:

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

Nepřímé rekurzivní spouštěče – Uživatel nebo aplikace aktualizuje záznam v tabulce A. Aktivuje se spouštěč UPDATE A v tabulce A a aktualizuje záznam v tabulce B. Pokud má tabulka B spouštěč UPDATE pro aktualizaci záznamů zpět na tabulku A, vyvolá spouštěč UPDATE v Tabulka A, která bude probíhat rekurzivně.

Vytvořme nepřímý rekurzivní spouštěč v tabulkách IDR_Test1 a IDR_Test2 pomocí níže uvedeného skriptu:

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

Spusťte níže uvedený skript:

UPDATE IDR_Test1
SET PK = 1
WHERE PK = 10;

Aby se předešlo těmto druhům vyvolání rekurzivních spouštěčů na úrovni databáze, má SQL Server možnost nazvanou RECURSIVE_TRIGGERS na každé úrovni databáze pro přerušení spouštění rekurzivního spouštění. Ve výchozím nastavení je možnost rekurzivního spouštěče pro databázi nastavena na hodnotu False. Aktivujte pouze podle potřeby po pečlivém zvážení dopadů na výkon nebo souvisejících změn dat.

V SSMS klikněte pravým tlačítkem na naši testovací databázi -> Vyberte Vlastnosti -> Klikněte na Možnosti a přejděte dolů, abyste viděli, zda je možnost Rekurzivní spouštění povolena nebo ne, jak je uvedeno níže. Pro testovací databázi je nastavena na False, protože False je výchozí hodnota pro možnost Rekurzivní spouštěče. Chcete-li zapnout možnost Rekurzivní spouštěče pro konkrétní databázi, stačí kliknout na rozbalovací hodnotu, změnit ji na True a kliknout na OK.

Prostřednictvím T-SQL můžeme ověřit možnost Rekurzivního spouštění v databázi Test kontrolou sloupce is_recursive_triggers_on ze sys.databases DMV, jak je uvedeno níže.

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

Chcete-li změnit možnost rekurzivních spouštěčů pro databázi (v mém příkladu Test), můžeme spustit níže uvedený skript.

ALTER DATABASE [Test] SET RECURSIVE_TRIGGERS ON WITH NO_WAIT
GO

Chcete-li ji deaktivovat zpět do false stavu (výchozí stav) pro databázi (v mém příkladu Test), spusťte níže uvedený skript.

ALTER DATABASE [Test] SET RECURSIVE_TRIGGERS OFF WITH NO_WAIT
GO

Vnořené spouštěče

Rekurzivní spouštěče jsou klasickým příkladem vnořených spouštěčů, ale může existovat několik dalších případů, které vedou k vnoření více spouštěčů. SQL Server umožňuje vnoření spouštěčů až do maximálně 32 úrovní a jakýkoli spouštěč překračující tuto úroveň vnoření bude SQL Serverem zrušen. SQL Server má konfiguraci pro celou instanci pro zakázání možnosti vnořených spouštěčů. Vezměte prosím na vědomí, že vnořování spouštěčů SQL Server pomocí kódu CLR nebo spravovaného kódu nespadá pod limit 32 úrovní, protože je mimo rozsah SQL Server. Ve výchozím nastavení bude možnost vnořených spouštěčů povolena ve všech instancích SQL Serveru a podle potřeby ji můžeme zakázat.

Zda je možnost vnořených spouštěčů povolena na úrovni instance v SSMS, můžeme ověřit pomocí následujících kroků:

Klikněte pravým tlačítkem na Server -> Vyberte Vlastnosti -> Klikněte na Upřesnit

Chcete-li zakázat nebo vypnout možnost vnořených spouštěčů, klikněte na rozevírací seznam a změňte jej na hodnotu False a klikněte na OK .

Prostřednictvím T-SQL můžeme ověřit, zda je povolena možnost Nested triggers kontrolou sloupce value_in_use v sys.configurations DMV pro název konfigurace vnořených triggerů.

Chcete-li tuto možnost zakázat, musíme použít sp_configure uloženou proceduru systému, jak je uvedeno níže:

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

V rámci všech spouštěčů DML nebo DDL má SQL Server k nalezení aktuální úrovně vnoření vestavěnou funkci s názvem TRIGGER_NESTLEVEL, která vrací počet spouštěčů provedených pro aktuální příkaz, který spustil aktivační událost včetně sebe sama. Syntaxe funkce TRIGGER_NESTLEVEL by byla:

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

Kde object_id je id objektu triggeru, trigger_type bude AFTER pro AFTER trigger a IOT pro INSTEAD OF trigger a trigger_event_category bude buď DML nebo DDL.

Pokud například potřebujeme povolit pouze úroveň vnoření do 10 a zvýšit chybu po 10 úrovních, můžeme to udělat na testovacím spouštěči jako zde:

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

ŠIFROVÁNÍ

Chcete-li zašifrovat logiku nebo definici spouštěče, lze v definici spouštěče použít možnost WITH ENCRYPTION podobně jako všechny ostatní objekty SQL Server.

PROVEĎTE JAKO doložku

Chcete-li spustit spouštěč pomocí specifického kontextu zabezpečení, lze v definici spouštěče použít klauzuli EXECUTE AS.

NENÍ K REPLIKÁCI

Aby bylo možné identifikovat, že spouštěč DML by neměl být vyvolán při provádění prostřednictvím změn replikace, bude pro všechny objekty v databázi odběratelů nastavena vlastnost NOT FOR REPLICATION.

Závěr

Děkujeme, že jste si prošli nabitý článek o DDL Triggers and Logon Triggers, kde jsme pochopili účel spouštěčů DDL a Logon, jak vytvořit nebo zrušit, zakázat nebo povolit tyto spouštěče a jak používat funkci EVENTDATA() pro sledování aktivit DDL nebo přihlášení. Kromě toho jsme se naučili, jak podrobně nastavit pořadí provádění více spouštěčů SQL DML nebo DDL spolu s rekurzivními a vnořenými spouštěči a jak opatrně zacházet s rekurzivními nebo vnořenými spouštěči.


  1. Jak interpretujete plán vysvětlení dotazu?

  2. Jak mohu porovnat čas na serveru SQL?

  3. Jak nahradit MySQL Perconou na Plesk CentOS 7

  4. Výkon Oracle:Bulk Collect