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

Spouštěče SQL Server:Spouštěče DML

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í, zahájení práce nebo jiné operace. Protože spouštěče zvládnou mnoho takových operací, měli bychom je definovat opatrně, abychom se vyhnuli dopadům na výkon.

V tomto článku prozkoumáme spouštěče, typy spouštěčů a různé dostupné možnosti spouštění. Také prozkoumáme nezbytná opatření při používání spouštěčů DML.

Spouštěče v SQL

Trigger je speciální typ uložené procedury, která se spouští po definovaných událostech a spouští skript definovaný v těle Trigger. Existuje několik typů spouštěčů:

  • Spouštěče DML – pro provádění operací DML, jako jsou příkazy INSERT, UPDATE a DELETE s tabulkami.
  • Spouštěče DDL – pro provádění operací DDL, jako jsou příkazy CREATE, ALTER a DROP napříč libovolnými objekty v databázi nebo na serveru.
  • Spouštěče přihlášení – pro pokus o přihlášení k instanci serveru SQL během události LOGON.

Spouštěče DML v SQL Server

Spouštěče DML jsou ty spouštěné příkazy DML (INSERT, UPDATE nebo DELETE) na tabulkách nebo pohledech. Takové spouštěče můžeme vytvořit v těchto tabulkách nebo pohledech pouze tam, kde jsou uložena data, aby v nich přijímaly příkazy DML.

Na základě času spuštění/vyvolání mohou být spouštěče DML následujících typů:

  • PRO nebo PO Typ spouštěče – spouštěč je vyvolán po úspěšném dokončení příkazu DML na tabulce nebo pohledu. Poznámka:spouštěč AFTER je možné vytvořit pouze pro tabulky, nikoli pro zobrazení.
  • MÍSTO Typ spouštěče – Spouštěč bude vyvolán před (NAMÍSTO) skriptu DML spuštěného v tabulce nebo pohledu.

SQL Server vytvoří dvě speciální nebo logické tabulky s názvem INSERTED a AKTUALIZOVÁNO kdykoli jsou spouštěče DML vytvořeny v tabulkách nebo zobrazeních. Tyto logické tabulky pomáhají identifikovat změny záznamů, ke kterým dochází prostřednictvím operací INSERT/UPDATE/DELETE. Tímto způsobem zajišťuje, že spouštěče DML fungují efektivně.

  • VLOŽENO logická tabulka ukládá kopie nových záznamů záznamů upravených během operací INSERT a UPDATE. Když je nový záznam přidán do aktuální tabulky, je přidán také do tabulky INSERTED. Podobně jakékoli změny existujících záznamů prostřednictvím příkazu UPDATE přesunou nejnovější hodnoty do tabulky INSERTED a starší hodnoty do logické tabulky DELETED.
  • SMAŽENO logická tabulka ukládá kopie starších hodnot během operací UPDATE a DELETE. Kdykoli je záznam aktualizován, starší hodnoty se zkopírují do tabulky DELETED. Kdykoli je záznam odstraněn ze skutečné tabulky, jsou záznamy vloženy do tabulky DELETED.

SQL Server má vestavěné funkce COLUMN_UPDATED() a UPDATE() k identifikaci přítomnosti operací INSERT nebo UPDATE v konkrétním sloupci.

  • COLUMN_UPDATED() vrátí varbinary hodnoty sloupců, které byly ovlivněny operacemi INSERT nebo UPDATE.
  • UPDATE() přijímá název sloupce jako vstupní parametr a vrací informaci o tom, zda daný sloupec v rámci operací INSERT nebo UPDATE změnil nějaké údaje.

NENÍ K REPLIKÁCI vlastnost lze použít ve spouštěčích DML, aby se zabránilo jejich spouštění při změnách přicházejících prostřednictvím procesu replikace.

Spouštěče DML lze vytvářet také pomocí .Net Framework Common Language Runtime (CLR).

Systém DMV sys.triggers ukládá seznam všech spouštěčů v rozsahu databáze. K získání podrobností o všech spouštěčích DML v rámci databáze můžeme použít níže uvedený dotaz:

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

Definice spouštěče DML lze zobrazit, pokud spouštěč není zašifrován. Použijeme kteroukoli z níže uvedených možností:

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() funkce

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

sp_helptext uložená procedura

EXEC sp_helptext '<trigger_name>';

Všechny možné události DML jsou dostupné na sys.events stůl. Můžeme je zobrazit pomocí následujícího dotazu:

SELECT * 
FROM sys.events;

Syntaxe spouštěče 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> }  

Pro účely ukázky jsem vytvořil dvě tabulky s názvem Prodej a Historie prodeje s několika sloupci v testovací databázi:

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

Jak můžete vidět, Historie prodeje tabulka má 3 další sloupce pro sledování data změny a uživatelského jména, které změnu vyvolalo. V případě potřeby můžeme mít ještě jeden sloupec identity definovat a udělejte z něj také primární klíč.

VLOŽTE Spouštěč

Na prodeji vytvoříme jednoduchý INSERT trigger tabulky VLOŽIT jakékoli nové změny záznamu do Historie prodeje stůl. Použijte níže uvedený skript:

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

Abychom vysvětlili syntaxi spouštěče, vytvořili jsme spouštěč DML s názvem TR_INS_Sales na stránce Prodej stůl. Musí spustit spouštěč pouze pro operace INSERT – vkládání záznamů do Historie prodeje tabulky z vložené tabulky.

Jak víme, vloženo je logická tabulka, která zachycuje změny, ke kterým dochází v tabulce Zdroj (Prodej v našem případě tabulka).

Můžeme vidět další speciální logickou tabulku odstraněnou ve spouštěči UPDATE, protože deleted tabulka není použitelná pro spouštěče INSERT.

Pojďme přidat nový záznam, abychom zkontrolovali, zda jsou záznamy vloženy do Historie prodeje tabulka automaticky.

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

I když jsme do Prodeje vložili pouze jeden záznam tabulky, získáme 2 řádky z 1 dotčeného řádku zpráva. Druhý záznam se objeví kvůli operaci INSERT jako součást spouštěče vyvolaného aktivitou INSERT na prodeji tabulka – vložení záznamu do Historie prodeje tabulka.

Pojďme ověřit záznamy napříč prodejem a Historie prodeje tabulky:

SELECT * 
FROM Sales

SELECT * 
FROM SalesHistory

Vidíme, že Datum změny a ChangedUser jsou automaticky vyplněny. Je to proto, že jsme navrhli naši historii tabulka s výchozími hodnotami jako GETDATE() a SUSER_NAME() .

Koncoví uživatelé mohou vidět prostřednictvím spouštěče nebo jiným způsobem, že jejich INSERT byl auditován prostřednictvím dalšího 1 ovlivněného řádku zpráva. Pokud chcete sledovat změny bez informování uživatelů, musíte použít NASTAVIT POČET ŘÁDKŮ ZAPNUTO příkaz. Potlačí výsledky zobrazené pro operace DML, ke kterým dochází uvnitř spouštěče.

ZMĚŇME náš spouštěč pomocí skriptu pomocí SET ROWCOUNT ON a uvidíte ji v akci:

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

Nyní vložíme další záznam do Prodeje tabulka:

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

Vidíme pouze jeden dotčený 1 řádek zpráva. Cílová skupina tedy nemusí vůbec dostat upozornění, že jejich akce jsou sledovány.

Ověřte, zda byl spouštěč INSERT vyvolán, ověřením prodeje a Historie prodeje tabulky.

Ano, událost INSERT na prodeji tabulka byla úspěšně spuštěna. Záznamy byly vloženy do Historie prodeje tabulky bez upozornění uživatelů.

Pokud tedy vytvoříte spouštěče pro účely auditu, SET NOCOUNT ON je nutné. Umožňuje auditování bez upozornění kohokoli.

Aktualizace spouštěče

Před vytvořením skutečného spouštěče UPDATE na prodeji tabulky, odkažme se opět na speciální logické vkládané a odstraňované tabulky. Vytvořte ukázkový spouštěč UPDATE na prodeji tabulka:

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

Aktivační událost UPDATE byla úspěšně vytvořena. Nyní nesprávně VLOŽME nový záznam. Později jej aktualizujeme, abychom ověřili spouštěč UPDATE v akci:

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

V prodeji máme níže uvedené záznamy a Historie prodeje tabulka:

Pojďme aktualizovat SalesId =3 v části Prodej tabulka s novými hodnotami. Uvidíme data napříč vloženými a odstraněnými tabulkami:

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

Když dojde k operaci UPDATE, všechny nové nebo upravené hodnoty budou dostupné ve vložené tabulce a staré hodnoty budou dostupné ve smazané tabulce:

Nyní upravíme spouštěč UPDATE pomocí níže uvedeného skriptu a ověříme jej v akci:

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

Spouštěč UPDATE byl úspěšně změněn a můžeme znovu spustit stejný skript UPDATE:

Nyní vidíme 1 dotčený řádek zprávu dvakrát. Označuje provedení operace UPDATE na prodeji tabulky a operace INSERT v Historie prodeje stůl. Pojďme si to ověřit výběrem v obou tabulkách:

Aktivita UPDATE byla sledována v Historie prodeje tabulka jako nový záznam. Před tímto záznamem máme ještě jeden, který ukazuje, kdy byl záznam vložen jako první.

DELETE Spouštěč

Doposud jsme testovali PRO nebo PO typ spouštěčů pro operace INSERT nebo UPDATE. Nyní můžeme zkusit použít MÍSTO typ spouštěče DML pro operaci DELETE. Použijte níže uvedený skript:

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

Aktivační událost DELETE byla úspěšně vytvořena. Místo provedení příkazu DELETE na prodeji odešle klientovi chybovou zprávu tabulka.

Zkusme smazat záznam SalesID =3 z Prodeje tabulky pomocí skriptu níže:

DELETE FROM Sales
WHERE SalesId = 3

Zabránili jsme uživatelům mazat záznamy z prodeje stůl. Spouštěč vyvolal chybovou zprávu.

Ověřte také, zda byl záznam smazán z Prodeje a zda došlo k nějakým změnám v Historie prodeje tabulka:

Protože jsme spouštěcí skript provedli před skutečným příkazem DELETE pomocí spouštěče INSTEAD OF, operace DELETE na SalesId=3 nebyla vůbec úspěšná. V obou prodejích se tedy neprojevily žádné změny a Historie prodeje tabulka.

Upravme spouštěč pomocí níže uvedeného skriptu k identifikaci pokusu DELETE v tabulce:

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

Spoušť byla úspěšně upravena. Odstraníme SalesId =3 záznam z prodeje znovu tabulka:

Provedení příkazu DELETE zobrazí 1 dotčený řádek zprávu dvakrát. Pojďme zkontrolovat záznamy napříč prodejem a Historie prodeje tabulky, abyste viděli, co se tam přesně děje:

Logika použitá ve spouštěči DELETE byla zachytit všechny pokusy DELETE na stole, aniž by se záznam ve skutečnosti odstranil z prodeje tabulky pomocí MÍSTO spoušť. Můžeme potvrdit, že záznam nebyl smazán z Prodeje tabulky a do Historie prodeje byl vložen nový záznam tabulka.

Jediný spouštěč pro zpracování operací INSERT, UPDATE a DELETE

Doposud jsme vytvořili 3 spouštěče pro zpracování operací INSERT, UPDATE a DELETE na jedné tabulce. Pokud máme více spouštěčů, bylo by obtížné je zvládnout, zvláště pokud nejsou řádně zdokumentovány. Pokud vývojáři použili protichůdnou logiku napříč více spouštěči, mohou nastat problémy s výkonem.

Osobně doporučuji používat jeden spouštěč se všemi logickými prvky, abyste se vyhnuli potenciální ztrátě dat nebo problémům s výkonem. Pro lepší výkon můžeme zkusit zkombinovat 3 spouštěče v jednom spouštěči. Než to však uděláme, prozkoumáme, jak ZRUŠIT existující spouštěče a jak spouštěče zakázat nebo povolit.

Spusťte spoušť

Chcete-li sloučit 3 spouštěče do jednoho, musíme nejprve tyto 3 spouštěče DROP. Je to možné prostřednictvím přístupu SSMS i T-SQL.

V SSMS rozbalte Test databáze > Tabulky > Prodej tabulka> Spouštěče .

Můžeme vidět naše 3 dosud vytvořené spouštěče:

Chcete-li spouštěč zahodit, jednoduše na něj klikněte pravým tlačítkem> Smazat > OK .

Pokud dáváte přednost použití T-SQL, podívejte se na níže uvedenou syntaxi pro zrušení spouštěče:

DROP TRIGGER <trigger_name>

Existuje TR_INS_Sales spouštěč, který jsme vytvořili na prodeji stůl. Skript bude:

DROP TRIGGER TR_INS_Sales

Důležité :Vypuštěním tabulky se ve výchozím nastavení zruší všechny spouštěče.

Zakázat a povolit spouštění

Namísto spouštění jej můžeme dočasně deaktivovat pomocí Zakázat spouštěč možnost přes SSMS nebo T-SQL.

V SSMS klikněte pravým tlačítkem na Název spouštěče> Zakázat . Po deaktivaci se spoušť nespustí, dokud jej znovu nepovolíte.

Zatímco Trigger funguje, Povolit možnost je zašedlá. Když jej zakážete, zobrazí se Povolit možnost se stane viditelnou a aktivní.

Pokud dáváte přednost používání T-SQL, můžete spouštěče zakázat a povolit pomocí níže uvedených skriptů:

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

Chcete-li deaktivovat a povolit naše konkrétní TR_INS_Sales spustit na prodeji tabulky, používáme níže uvedené skripty:

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

Tak jsme se naučili, jak DROP , ZAKÁZAT a AKTIVOVAT spouštěče. Vypustím 3 existující spouštěče a vytvořím jediný spouštěč pokrývající všechny 3 operace nebo vkládání, aktualizaci a mazání pomocí skriptu níže:

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

Vytvoření jediného spouštěče bylo úspěšné. Použili jsme logiku k identifikaci operace pomocí vložených a odstraněných tabulek.

Pro operaci INSERT nebude odstraněná tabulka naplněna. Pro operaci DELETE se vložená tabulka nevyplní. Tyto operace můžeme snadno identifikovat. Pokud se tyto 2 podmínky neshodují, jedná se o operaci UPDATE a můžeme použít jednoduchý příkaz ELSE.

Použil jsem UPDATE() funkce, která ukazuje, jak to funguje. Pokud by v těchto sloupcích byly nějaké aktualizace, spustí se akce spouštěče UPDATE. Můžeme také použít COLUMNS_UPDATED() funkce, kterou jsme probrali dříve, abychom také identifikovali operaci UPDATE.

Pojďme otestovat náš nový spouštěč vložením nového záznamu:

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

Ověřování záznamů napříč prodejem a Historie prodeje tabulkách ukazuje data, jak je uvedeno níže:

Zkusme aktualizovat SalesId =2 záznam:

UPDATE Sales
SET price = 250
WHERE SalesId = 2;

Zkusme skript DELETE pomocí tohoto postupu na SalesId =4 záznam:

DELETE FROM Sales
WHERE SalesId = 4;

Jak si můžeme všimnout, SalesId =4 byl smazán z Prodeje tabulka, protože se jedná o PRO nebo PO spouštěče, díky kterému bude operace DELETE úspěšná na prodeji tabulky a poté vložte záznam do Historie prodeje tabulka.

Účel spouštěčů DML

Spouštěče DML efektivně slouží pro následující scénáře:

  1. Sledujte historické změny operací INSERT, UPDATE a DELETE na konkrétní tabulce.
  2. Auditujte události DML probíhající na stole, aniž byste uživatelům odhalili aktivitu auditu.
  3. Zabraňte provádění změn DML na stole pomocí MÍSTO spouští a varuje uživatele konkrétní chybovou zprávou.
  4. Při dosažení předem definovaných podmínek zasílejte oznámení cílovým lidem.
  5. Spusťte úlohu SQL Server Agent Job nebo jakýkoli jiný proces, kdykoli dosáhnete předem definovaných podmínek.

A můžete je použít pro jakékoli další požadavky obchodní logiky, které můžete implementovat pomocí příkazů T-SQL.

Závěr

Tělo spouštěče DML je podobné uložené proceduře. Můžeme implementovat jakoukoli požadovanou obchodní logiku, ale při psaní této logiky musíme být opatrní, abychom se vyhnuli potenciálním problémům.

Přestože SQL Server podporuje vytváření více spouštěčů v jedné tabulce, je lepší je konsolidovat do jednoho spouštěče. Tímto způsobem můžete snadno udržovat spouštěče a rychleji je odstraňovat. Kdykoli jsou pro účely auditu implementovány spouštěče DML, ujistěte se, že SET NOCOUNT ON možnost je efektivně využita.

V příštím článku prozkoumáme spouštěče DDL a spouštěče přihlášení.


  1. Seznam dotazů spuštěných na serveru SQL Server

  2. SQL SELECT AND operátor

  3. Musím v Oracle vytvářet indexy cizích klíčů?

  4. Jak přistupovat ke vzdálenému serveru pomocí lokálního klienta phpMyAdmin?