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

SQL Server Trigger:Pochopení a alternativy

Spouštěč SQL Server je speciální typ uložených procedur, které se automaticky spouštějí, když na konkrétním databázovém serveru dojde k události. SQL Server nám poskytuje dva hlavní typy spouštěčů:DML Spouštěče a DDL spouštěče. Aktivační události DDL budou aktivovány v reakci na různé události jazyka DDL (Data Definition Language), jako je například provádění příkazů T-SQL CREATE, ALTER, DROP, GRANT, DENY a REVOKE. Spouštěč DDL může reagovat na akce DDL tím, že zabrání těmto změnám v ovlivnění databáze, provede jinou akci v reakci na tyto akce DDL nebo zaznamená tyto změny, které se provedou v databázi.

Spouštěč SQL Server DML je speciálním typem uložených procedur, které jsou navrženy k provádění sekvence akcí na databázové tabulce, ke které je spouštěč připojen, když dojde k události jazyka DML (Data Manipulation Language), jako je INSERT, UPDATE nebo DELETE. dojde k úpravě obsahu databázových tabulek nebo pohledů bez ohledu na to, zda jsou či nejsou ovlivněny řádky tabulky. Spouštěče se liší od uložených procedur tím, že se spouštějí automaticky, když dojde k úpravě předdefinovaných dat. Spouštěče DML lze použít k udržení integrity dat a vynucení podnikových pravidel společnosti, stejně jako funkce kontroly tabulek a omezení cizích klíčů, prováděním procesů auditu a dalších akcí po DML. Spouštěče DML můžete použít k dotazování na jiné tabulky a provádění složitých dotazů T-SQL.

Pokud se spustí trigger, speciální typ virtuálních tabulek nazvaný Inserted a Smazáno tabulky budou použity k zachování hodnot dat před a po úpravě. Příkaz spouštěče bude fungovat v rámci stejné transakce, která spouští tento spouštěč. To znamená, že transakce nebude zcela potvrzena, dokud nebude příkaz spouštěče úspěšně dokončen. Na druhou stranu bude transakce vrácena zpět, pokud příkaz spouště selže.

Existují dva typy spouštěčů DML:PO nebo PRO spoušť a MÍSTO spoušť. Spouštěč AFTER se spustí a provede po provedení akce INSERT, UPDATE nebo DELETE, která jej úspěšně spustí. Před spuštěním spouště by také měly být úspěšné všechny referenční kaskádové akce a kontroly omezení. Spouštěč AFTER lze definovat pouze na úrovni tabulky bez možnosti definovat jej na pohledech. Spouštěč NAMÍSTO OF se používá k přepsání příkazu akce, která spouští spouštěč, příkazem uvedeným ve spouštěči, přičemž tento příkaz vrátí zpět po vyvolání chyby, když se někdo pokouší provést akci, která porušuje konkrétní zásady, jako je aktualizace kritický finanční sloupec nebo zapsání změny do auditní tabulky před provedením změny. Spouštěč INSTEAD OF vám umožňuje VLOŽIT, AKTUALIZOVAT nebo VYMAZAT data z pohledů, které odkazují na data z více tabulek, navíc k možnosti odmítnout část dávkového dotazu a úspěšně provést jinou část této dávky. Spouštěč INSTEAD OF nelze použít s aktualizovatelnými pohledy, které mají WITH CHECK OPTION a v tabulkách s referenčním vztahem, který určuje kaskádové akce při DELETE nebo UPDATE.

Po teoretickém probrání spouštěčů začneme ukazovat to, o čem diskutujeme prakticky. V nadcházejících ukázkách ukážeme různé situace, ve kterých můžeme využít výhody spouštěčů SQL Serveru.

PO… Spouštění DML

Předpokládejme, že potřebujeme sledovat akce DML, které se provádějí na konkrétní tabulce, a zapsat tyto protokoly do tabulky historie, kde se do tabulky historie zapíše ID vloženého, ​​aktualizovaného nebo smazaného záznamu a akce, která se provede. Níže uvedené příkazy CREATE TABLE T-SQL lze použít k vytvoření tabulky zdroje i tabulky historie:

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

Pro sledování operace INSERT vytvoříme spouštěč DML, který se spustí po provedení operace INSERT v nadřazené tabulce. Tento spouštěč načte poslední vloženou hodnotu ID do této nadřazené tabulky z virtuální vložené tabulky, jako v příkazu CREATE TRIGGER T-SQL níže:

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

Sledování operace DELETE lze dosáhnout vytvořením aktivační události DML, která se spustí po provedení operace DELETE v nadřazené tabulce. Spouštěč opět načte hodnotu ID posledního smazaného záznamu z této nadřazené tabulky z virtuální odstraněné tabulky, jako v příkazu CREATE TRIGGER T-SQL níže:

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

Nakonec budeme sledovat také operaci UPDATE vytvořením spouštěče DML, který se spustí po provedení operace UPDATE na nadřazené tabulce. V rámci tohoto spouštěče získáme poslední aktualizovanou hodnotu ID z této nadřazené tabulky z virtuální vložené tabulky, přičemž vezmeme v úvahu, že proces UPDATE se provádí odstraněním záznamu a vložením nového záznamu s aktualizovanými hodnotami, jako v CREATE TRIGGER Příkaz T-SQL níže:

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

Tabulky a spouštěče jsou nyní připraveny k našemu testování. Pokud se pokusíte vložit nový záznam do nadřazené tabulky pomocí příkazu INSERT INTO T-SQL níže:

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

Poté kontrolou prováděcího plánu vygenerovaného provedením předchozího příkazu INSERT uvidíte, že budou provedeny dvě operace vložení, které ovlivní dvě tabulky; nadřazená tabulka s hodnotami zadanými v příkazu INSERT a tabulkou historie kvůli spuštění spouštěče AFTER INSERT, jak je znázorněno v plánu provádění níže:

Je to také jasné, když zkontrolujete data vložená do rodičovské tabulky a tabulky historie pomocí příkazů SELECT níže:

SELECT * FROM TriggerDemo_Parent
GO
SELECT * FROM TriggerDemo_History

Kde budou hodnoty zadané v příkazu INSERT vloženy do nadřazené tabulky a vložit protokol obsahující ID vloženého záznamu a provedené operace bude vložen do tabulky historie, jak ukazuje výsledek níže:

Pokud se nyní pokusíte aktualizovat existující záznam v nadřazené tabulce pomocí příkazu UPDATE T-SQL níže:

UPDATE TriggerDemo_Parent SET Emp_Salary=550 WHERE ID=1

A zkontrolujte plán provádění vygenerovaný provedením předchozího příkazu UPDATE, uvidíte, že po operaci aktualizace bude následovat operace vložení ovlivňující dvě různé tabulky; nadřazená tabulka bude aktualizována o hodnotu zadanou v příkazu UPDATE a operaci vložení do tabulky historie kvůli spuštění spouštěče AFTER UPDATE, jak je znázorněno v plánu provádění níže:

Kontrola nadřazených záznamů i záznamů tabulky historie pomocí příkazů SELECT níže:

SELECT * FROM TriggerDemo_Parent
GO
SELECT * FROM TriggerDemo_History

Uvidíte, že příkaz aktualizace upraví hodnotu Emp_Salary v nadřazené tabulce na hodnotu uvedenou v příkazu UPDATE a protokol aktualizace obsahující ID aktualizovaného záznamu a provedené operace bude vložen do tabulky historie, jako zobrazeno ve výsledku níže:

V posledním scénáři spouštěče AFTER DML budeme sledovat odstranění existujícího záznamu z nadřazené tabulky pomocí příkazu DELETE T-SQL níže:

DELETE FROM  TriggerDemo_Parent WHERE ID=1

Poté zkontrolujte prováděcí plán vygenerovaný provedením předchozího příkazu DELETE, uvidíte, že po operaci DELETE bude následovat operace vložení, která ovlivní dvě různé tabulky; nadřazená tabulka, ze které bude odstraněn záznam s poskytnutým ID v klauzuli WHERE příkazu DELETE, a operace vložení do tabulky historie kvůli spuštění spouštěče AFTER DELETE, jak je znázorněno v plánu provádění níže:

Pokud zkontrolujete záznamy nadřazené i historické tabulky pomocí příkazů SELECT níže:

SELECT * FROM TriggerDemo_Parent
GO
SELECT * FROM TriggerDemo_History

Uvidíte, že záznam s hodnotou ID rovnou 1 byl odstraněn z nadřazené tabulky, která je uvedena v příkazu DELETE, a protokol mazání obsahující ID smazaného záznamu a provedené operace bude vložen do tabulky historie. , jak ukazuje výsledek níže:

MÍSTO… Spouštěče DML

Druhým typem spouštěčů DML je spouštěč MÍSTO DML. Jak již bylo zmíněno dříve, spouštěč NAMÍSTO OF přepíše příkaz akce, která spouští spouštěč, příkazem uvedeným ve spouštěči. Předpokládejme, že potřebujeme protokolovat akce DML, které se uživatelé pokoušejí provést na konkrétní tabulce, aniž bychom jim umožnili tuto akci provést. Níže uvedené příkazy CREATE TABLE T-SQL lze použít k vytvoření zdrojové i alternativní tabulky:

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

Po vytvoření dvou tabulek vložíme jeden záznam do zdrojové tabulky pro naši ukázku pomocí příkazu INSERT INTO níže:

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

Pro tuto ukázku vytvoříme tři spouštěče pro přepsání operací INSERT, UPDATE a DELETE. První spouštěč bude použit k zabránění jakékoli operaci vložení v nadřazené tabulce a protokolu, které se změní na alternativní tabulku. Spouštěč se vytváří pomocí příkazu CREATE TRIGGER T-SQL níže:

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

Druhý spouštěč se používá k zabránění jakékoli operaci aktualizace nadřazené tabulky a protokolu, které se změní na alternativní tabulku. Tento spouštěč je vytvořen následovně:

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

Poslední spouštěč bude použit k zabránění jakékoli operaci odstranění nadřazené tabulky a protokolu, které se změní na alternativní tabulku. Tento spouštěč je vytvořen následovně:

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

Dva stoly a tři spouštěče jsou nyní připraveny. Pokud se pokusíte vložit novou hodnotu do nadřazené tabulky pomocí příkazu INSERT INTO T-SQL níže:

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

Poté zkontrolujte nadřazené i alternativní záznamy tabulky pomocí příkazů SELECT níže:

SELECT * FROM TriggerDemo_NewParent
GO
SELECT * FROM TriggerDemo_InsteadParent

Vzhledem k tomu, že v nadřazené tabulce máme trigger INSTEAD OF INSERT, uvidíte z výsledku, že do nadřazené tabulky není vložen žádný nový záznam a do alternativní tabulky je vložen protokol operace vložení, jak je znázorněno ve výsledku níže:

Pokus o aktualizaci existujícího záznamu v nadřazené tabulce pomocí příkazu UPDATE T-SQL níže:

UPDATE TriggerDemo_NewParent SET Emp_Salary=550 WHERE ID=1

Poté zkontrolujte nadřazené i alternativní záznamy tabulky pomocí příkazů SELECT níže:

SELECT * FROM TriggerDemo_NewParent
GO
SELECT * FROM TriggerDemo_InsteadParent

Z výsledku uvidíte, že hodnota Emp_Salary záznamu s hodnotou ID rovnou 1 z nadřazené tabulky se nezmění a protokol operace aktualizace se vloží do alternativní tabulky kvůli aktivaci INSTEAD OF UPDATE v nadřazené tabulce, jak ukazuje výsledek níže:

Nakonec, pokud se pokusíme odstranit existující záznam z nadřazené tabulky pomocí příkazu DELETE T-SQL níže:

DELETE FROM  TriggerDemo_NewParent  WHERE ID=1

A zkontrolujte nadřazené i alternativní záznamy tabulky pomocí příkazů SELECT níže:

SELECT * FROM TriggerDemo_NewParent
GO
SELECT * FROM TriggerDemo_InsteadParent

Z výsledku bude zřejmé, že záznam s hodnotou ID rovnou 1 z nadřazené tabulky nebude smazán a protokol operace odstranění se vloží do alternativní tabulky kvůli tomu, že v nadřazené tabulce bude mít spouštěč MÍSTO DELETE. tabulka, jak ukazuje výsledek níže:

PO… Spustit DML se zprávami

Spouštěč AFTER lze také použít k vyvolání varovné zprávy pro uživatele. V tomto případě bude dotaz informační zprávou, která nezabrání provedení příkazu, který spustí aktivační událost. Pojďme vypustit dříve vytvořený spouštěč NAMÍSTO AKTUALIZACE a nahradit jej jiným spouštěčem AFTER UPDATE, který vyvolá chybu varování po provedení jakékoli operace aktualizace pomocí příkazů T-SQL DROP/CREATE TRIGGER níže:

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  

Pokud se pokusíte aktualizovat hodnotu Emp_Salary zaměstnance s hodnotou ID rovnou 1 pomocí níže uvedeného příkazu UDPATE:

UPDATE TriggerDemo_NewParent SET Emp_Salary=550 WHERE ID=1

V části Zprávy se zobrazí chybová zpráva kartu, která obsahuje zprávu poskytnutou ve vytvořeném spouštěči, jak je znázorněno níže:

Kontrola dat nadřazené tabulky pomocí příkazu SELECT níže:

SELECT * FROM TriggerDemo_NewParent

Z výsledku uvidíte, že Emp_Salary je úspěšně aktualizován, jak je ukázáno níže:

Pokud potřebujete spouštěč AFTER UPDATE k zastavení operace aktualizace po vyvolání chybové zprávy, ROLLBACK příkaz lze přidat do spouštěče, aby bylo možné vrátit zpět operaci aktualizace, která spustila tento spouštěč, a připomenout si, že spouštěč a příkaz, který spouštěč spouští, budou provedeny ve stejné transakci. Toho lze dosáhnout pomocí příkazu ALTER TRIGGER T-SQL, viz:

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

Pokud se pokusíte aktualizovat hodnotu Emp_Salary zaměstnance s ID rovným 1 pomocí příkazu UPDATE níže:

UPDATE TriggerDemo_NewParent SET Emp_Salary=700 WHERE ID=1

V části Zprávy se opět zobrazí chybová zpráva , ale tentokrát bude operace aktualizace zcela vrácena zpět, jak je znázorněno v chybových zprávách níže:

Kontrola hodnoty ze zdrojové tabulky pomocí příkazu SELECT níže:

SELECT * FROM TriggerDemo_NewParent

Uvidíte, že hodnota Emp_Salary se nezměnila, protože spouštěč AFTER UPDATE vrátil zpět celou transakci poté, co vyvolal chybovou zprávu, jak ukazuje výsledek v tabulce níže:

Nevýhody spouštěče

Se všemi zmíněnými výhodami spouštěčů SQL Server zvyšují spouštěče složitost databáze. Pokud je spouštěč špatně navržen nebo nadměrně používán, povede to k velkým problémům s výkonem, jako jsou zablokované relace, kvůli prodloužení životnosti transakce na delší dobu, další režie na systém kvůli jejímu provedení při každém INSERT, UPDATE nebo Je provedena akce DELETE nebo může vést k problémům se ztrátou dat. Také není snadné prohlížet a sledovat databázové spouštěče, zvláště pokud o nich neexistuje žádná dokumentace, protože je vývojářům a aplikacím neviditelná.

Spouštět alternativy… Prosadit integritu

Pokud se zjistí, že spouštěče poškozují výkon vaší instance SQL Server, musíte je nahradit jinými řešeními. Například namísto použití spouštěčů k vynucení integrity entity by měla být vynucena na nejnižší úrovni pomocí omezení PRIMARY KEY a UNIQUE. Totéž platí pro integritu domény, která by měla být vynucena prostřednictvím omezení CHECK, a referenční integritu, která by měla být vynucena prostřednictvím omezení FOREIGN KEY. Spouštěče DML můžete použít pouze v případě, že funkce podporované konkrétním omezením nesplňují požadavky vaší aplikace.

Porovnejme mezi vynucováním integrity domény pomocí spouštěčů DML a pomocí omezení CHECK. Předpokládejme, že potřebujeme vynutit vkládání kladných hodnot pouze do sloupce Emp_Salary. Začneme vytvořením jednoduché tabulky pomocí příkazu CREATE TABLE T-SQL níže:

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

Poté definujte spouštěč AFTER INSERT DML, který zajistí, že vložíte kladnou hodnotu do sloupce Emp_Salary vrácením transakce, pokud uživatel vloží zápornou hodnotu platu, pomocí příkazu CREATE TRIGGER T-SQL níže:

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

Pro účely porovnání vytvoříme další jednoduchou tabulku se stejným schématem a v příkazu CREATE TABLE definujeme omezení CHECK, aby bylo možné přijímat pouze kladné hodnoty ve sloupci Emp_Salary, jak je znázorněno níže:

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
) GO)

Pokud se pokusíte vložit níže uvedený záznam, který obsahuje zápornou hodnotu Emp_Salary do první tabulky, která má předdefinovaný spouštěč, pomocí níže uvedeného příkazu INSERT INTO:

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

Příkaz INSERT selže a zobrazí chybovou zprávu ukazující, že nemůžete vložit zápornou hodnotu do sloupce Emp_Salary a vrátit zpět celkovou transakci kvůli aktivaci AFTER INSERT, jak je uvedeno v chybové zprávě níže:

Pokud se také pokusíte vložit stejný záznam, který obsahuje zápornou hodnotu Emp_Salary, do druhé tabulky, která má předdefinované omezení CHECK pomocí příkazu INSERT INTO níže:

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

Příkaz INSERT znovu selže, což ukazuje, že se pokoušíte vložit hodnotu, která je v konfliktu s omezující podmínkou CHECK, jak je uvedeno v chybové zprávě níže:

Z předchozích výsledků vidíte, že jak spouštěcí, tak omezující metody CHECK dosahují cíle tím, že vám brání vkládat záporné hodnoty Emp_Salary. Ale který z nich je lepší? Porovnejme výkonnost těchto dvou metod kontrolou váhy realizačního plánu pro každou z nich. Z vygenerovaných plánů provádění po provedení dvou dotazů uvidíte, že váha metody spouštění je třikrát váhu metody omezení CHECK, jak je uvedeno v porovnání prováděcího plánu níže:

Abychom porovnali dobu provádění spotřebovanou každým z nich, spusťte každý z nich 1000krát pomocí příkazů T-SQL níže:

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

Uvidíte, že první metoda, která používá spouštěč, bude trvat asi 31 ms k úplnému provedení, kde druhá metoda využívající omezení CHECK zabere pouze 17 ms , což je asi 0,5 času požadované v metodě používající spoušť. Důvodem je skutečnost, že aktivační událost prodlouží životnost transakce a vrátí zpět dotaz, který spustí aktivační událost po jeho provedení, když je zjištěno narušení integrity, což způsobí snížení výkonu v důsledku procesu vrácení zpět. Případ je jiný při použití omezení CHECK, kde omezení vykoná svou práci před provedením jakékoli úpravy v datech, což nevyžaduje žádné vrácení v případě porušení.

Alternativy spouštění… Auditování

Jak jsme již zmínili dříve, spouštěče lze také použít k auditu a sledování změn provedených na konkrétní tabulce. Pokud tato metoda auditování způsobí snížení výkonu ve vaší instanci SQL Server, můžete ji snadno nahradit parametrem OUTPUT doložka. Klauzule OUTPUT vrací informace o každém řádku ovlivněném operací INSERT, UPDATE nebo DELETE ve tvaru potvrzovací zprávy nebo hodnoty, kterou lze vložit do historické tabulky. Metoda klauzule OUTPUT nám také poskytuje větší kontrolu nad prováděným kódem, protože bude přidána do samotného příkazu pro vložení, úpravu nebo odstranění dat, kdykoli budete chtít, na rozdíl od spouštěče, který bude vždy proveden.

Porovnejme mezi protokolováním vkládání a úpravy dat do tabulky historie pomocí spouštěčů DML a pomocí klauzule OUTPUT. Začneme vytvořením níže uvedených produkčních a historických tabulek pomocí příkazu CREATE TABLE T-SQL níže:

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

Jakmile jsou obě tabulky úspěšně vytvořeny, vytvoříme trigger AFTER INSERT, UPDATE DML, který zapíše záznam do tabulky historie, pokud je do produkční tabulky vložen nový řádek nebo je existující záznam upraven pomocí příkazu CREATE TRIGGER T-SQL. níže:

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

Abychom mohli porovnat protokolování změn pomocí spouštěcí metody a klauzule OUTPUT, musíme vytvořit dvě nové jednoduché tabulky, produkční a historické tabulky, se stejným schématem jako předchozí dvě tabulky, ale tentokrát bez definování spouštěče, pomocí CREATE TABLE T-SQL příkazy níže:

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

Nyní jsou čtyři stoly připraveny k testování. Do první produkční tabulky, která má spouštěč, vložíme jeden záznam pomocí příkazu INSERT INTO T-SQL níže:

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

Poté stejný záznam vložíme do druhé produkční tabulky pomocí klauzule OUTPUT. Níže uvedený příkaz INSERT INTO bude fungovat jako dva příkazy vložení; první vloží stejný záznam do produkční tabulky a druhý příkaz insert vedle klauzule OUTPUT vloží log vložení do tabulky historie:

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

Při kontrole dat vložených do čtyř tabulek produkce a historie uvidíte, že obě metody, metoda spouštění a metoda OUTPUT, zapíší stejný protokol do tabulky historie úspěšně a stejným způsobem, jak ukazuje výsledek níže:

Z vygenerovaných plánů provádění po provedení dvou dotazů uvidíte, že váha spouštěcí metody je přibližně (21 %+36 %) 57 % z celkové hmotnosti, kde váha metody OUTPUT je asi 43 % , s malým rozdílem hmotnosti, jak je znázorněno v porovnání plánů provádění níže:

Rozdíl ve výkonu je zřejmý při porovnání doby provádění spotřebované každou metodou, kde protokolování změn pomocí metody spouštění spotřebuje (114+125) 239 ms být zcela proveden a metoda, která používá metodu klauzule OUTPUT, spotřebuje pouze 5 ms , což jsou 2 % času použitého v metodě spouštění, jak jasně ukazuje Statistika času níže:

Z předchozího výsledku je nyní jasné, že použití metody OUTPUT je lepší než použití triggerů pro auditování změn.

Užitečné odkazy:

  • CREATE TRIGGER (Transact-SQL) https://docs.microsoft.com/en-us/sql/t-sql/statements/create-trigger-transact-sql
  • Spouštěče DML https://docs.microsoft.com/en-us/sql/relational-databases/triggers/dml-triggers
  • Spouštěče DDL https://docs.microsoft.com/en-us/sql/relational-databases/triggers/ddl-triggers
  • Ustanovení OUTPUT (Transact-SQL) https://docs.microsoft.com/en-us/sql/t-sql/queries/output-clause-transact-sql

  1. SQL Server 2008 Časová razítka vložení a aktualizace řádků

  2. OracleParameter a klauzule IN

  3. Jak by filtrované indexy mohly být výkonnější funkcí

  4. Problém s obnovením databáze Heroku