sql >> Databasteknik >  >> RDS >> Sqlserver

SQL Server-utlösare:DML-utlösare

I SQL Server är utlösare databasobjekt som 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, starta ett jobb eller andra operationer. Eftersom triggers kan hantera många sådana operationer bör vi definiera dem med omsorg för att undvika prestandapåverkan.

I den här artikeln kommer vi att undersöka triggers, typer av triggers och olika tillgängliga triggeralternativ. Vi kommer också att undersöka nödvändiga försiktighetsåtgärder när du använder DML-utlösare.

Triggers i SQL

En utlösare är en speciell typ av lagrad procedur som exekveras vid definierade händelser och kör skriptet som definieras i utlösarkroppen. Det finns flera typer av utlösare:

  • DML-utlösare – för att utföra DML-operationer som INSERT, UPDATE och DELETE-kommandon på tabeller.
  • DDL-utlösare – för att utföra DDL-operationer som CREATE, ALTER och DROP-kommandon över alla objekt på databasen eller servern.
  • Inloggningsutlösare – för försöket att logga in på en instans av SQL Server under LOGON-händelsen.

DML-utlösare i SQL Server

DML-utlösare är de som utlöses av DML-kommandon (INSERT, UPDATE eller DELETE) på tabeller eller vyer. Vi kan skapa sådana utlösare på de tabellerna eller vyerna endast där data finns så att de accepterar DML-kommandon på dem.

Baserat på tidpunkten för avfyrning/anrop, kan DML-utlösare vara av följande typer:

  • FÖR eller EFTER Triggertyp – utlösaren anropas efter framgångsrikt slutförande av DML-satsen på en tabell eller vy. Obs:det är möjligt att skapa AFTER-utlösaren endast på tabeller, inte på vyer.
  • I STÄLLET FÖR Triggertyp – Trigger kommer att anropas före (I STÄLLET FÖR) DML-skriptet som körs i tabellen eller vyn.

SQL Server skapar två speciella eller logiska tabeller med namnet INSERTED och UPPDATERAD närhelst DML-utlösare skapas över tabeller eller vyer. Dessa logiska tabeller hjälper till att identifiera poständringar som sker via INSERT/UPDATE/DELETE-operationer. På så sätt säkerställer det att DML-utlösare fungerar effektivt.

  • INSERT logisk tabell lagrar kopior av nya poster av poster som modifierats under INSERT- och UPDATE-operationerna. När en ny post läggs till i den faktiska tabellen läggs den också till i INSERTED-tabellen. På liknande sätt flyttar alla ändringar på befintliga poster via UPDATE-satsen de senaste värdena till tabellen INSERTED och äldre värden – till den logiska tabellen DELETED.
  • RADERAT logisk tabell lagrar kopior av äldre värden under UPDATE- och DELETE-operationerna. När en post uppdateras, kopieras äldre värden till tabellen DELETED. Närhelst en post raderas från den faktiska tabellen, infogas posterna i tabellen DELETED.

SQL Server har inbyggda funktioner COLUMN_UPDATED() och UPPDATERING() för att identifiera närvaron av INSERT- eller UPDATE-operationer på den specifika kolumnen.

  • COLUMN_UPDATED() returnerar varbinära värden för kolumner som påverkades av operationerna INSERT eller UPDATE.
  • UPPDATERING() accepterar kolumnnamnet som en indataparameter och returnerar informationen om kolumnen har några dataändringar som en del av INSERT- eller UPDATE-operationerna.

INTE FÖR REPLIKATION egenskapen kan användas i DML-utlösare för att undvika att aktivera dem för ändringar som kommer via replikeringsprocessen.

DML-utlösare kan också skapas med .Net Framework Common Language Runtime (CLR).

Systemet DMV sys.triggers lagrar listan över alla databasomfattade utlösare. Vi kan använda frågan nedan för att hämta information om alla DML-utlösare i en databas:

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

DML-utlösarens definitioner kan ses om utlösaren inte är krypterad. Vi använder något av nedanstående alternativ:

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

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

sp_hjälptext lagrad procedur

EXEC sp_helptext '<trigger_name>';

Alla möjliga DML-händelser är tillgängliga i sys.events tabell. Vi kan se dem med hjälp av nedanstående fråga:

SELECT * 
FROM sys.events;

DML-utlösarens syntax

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

I demosyfte har jag skapat två tabeller med namnet Försäljning och Säljhistorik med få kolumner i testdatabasen:

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

Som du kan se är försäljningshistoriken Tabellen har ytterligare 3 kolumner för att spåra det ändrade datumet och användarnamnet som anropade ändringen. Om det behövs kan vi ha ytterligare en Identitetskolumn definieras och gör den till en primärnyckel också.

INSERT Trigger

Vi skapar en enkel INSERT-utlösare på försäljningen tabeller för att INFOGA eventuella nya poständringar i Säljhistorik tabell. Använd skriptet nedan:

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

För att förklara triggersyntaxen har vi skapat en DML-trigger som heter TR_INS_Sales Försäljning tabell. Den måste aktivera utlösaren endast för INSERT-operationerna – infoga poster i Säljhistoriken tabell från den infogade tabellen.

Som vi vet, insatt är en logisk tabell som fångar de förändringar som sker i källtabellen (Försäljning tabell i vårt fall).

Vi kan se en annan speciell logisk tabell raderad i UPDATE-utlösaren eftersom den raderade tabellen är inte tillämplig för INSERT-utlösare.

Låt oss lägga till en ny post för att kontrollera om poster har infogats i försäljningshistoriken tabell automatiskt.

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

Även om vi bara har infogat en post i Försäljning tabell får vi två rader av 1 påverkad rad meddelande. Den andra posten visas på grund av INSERT-operationen som en del av triggern som anropas av INSERT-aktiviteten på Försäljning tabell – infoga en post i försäljningshistoriken bord.

Låt oss verifiera uppgifterna över både försäljningen och Säljhistorik tabeller:

SELECT * 
FROM Sales

SELECT * 
FROM SalesHistory

Vi kan se det ChangeDate och ChangedUser fylls i automatiskt. Det är för att vi utformade vår Historia tabell med standardvärdena GETDATE() och SUSER_NAME() .

Slutanvändare kan se via Trigger eller på något annat sätt att deras INSERT granskades via den ytterligare 1 påverkade raden meddelande. Om du vill övervaka ändringar utan att informera användarna måste du använda STÄLL IN RADANTAL PÅ kommando. Den undertrycker resultaten som visas för DML-operationer som sker inuti utlösaren.

Låt oss ÄNDRA vår trigger genom att använda skriptet med STÄLL IN ROWCOUNT PÅ alternativet och se det i aktion:

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

Nu infogar vi ytterligare en post i Försäljningen tabell:

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

Vi kan bara se en enda 1 rad påverkad meddelande. Därför kanske målgruppen inte alls får besked om att deras handlingar övervakas.

Låt oss verifiera om INSERT-utlösaren anropades genom att verifiera försäljningen och Säljhistorik tabeller.

Ja, INSERT-händelsen på Försäljning tabellen utlöstes framgångsrikt. Posterna infogades i försäljningshistoriken tabell utan att meddela användare.

Därför, om du skapar utlösare för granskningsändamål, STÄLL IN NOCOUNT PÅ är nödvändigt. Det möjliggör granskning utan att varna någon.

Uppdateringsutlösare

Innan du skapar en faktisk UPDATE-utlösare på Försäljningen tabell, låt oss återigen hänvisa till de speciella logiska infogade och borttagna tabellerna. Skapa en exempelutlösare för UPPDATERING på Försäljning tabell:

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

UPDATE-utlösaren skapades framgångsrikt. Nu, låt oss INFOGA en ny post felaktigt. Senare kommer vi att uppdatera den för att verifiera UPDATE-utlösaren i aktion:

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

Vi har nedanstående poster över försäljningen och Säljhistorik tabell:

Låt oss uppdatera SalesId =3 i Försäljning tabell med nya värden. Vi kommer att se data över de infogade och borttagna tabellerna:

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

När UPDATE-operationen äger rum kommer alla nya eller modifierade värden att vara tillgängliga i den infogade tabellen, och gamla värden kommer att vara tillgängliga i den raderade tabellen:

Nu, låt oss modifiera UPDATE-utlösaren med skriptet nedan och verifiera att den fungerar:

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

UPDATE-utlösaren har ändrats och vi kan köra samma UPDATE-skript igen:

Nu kan vi se 1 rad påverkad meddelande två gånger. Det indikerar utförandet av UPDATE-operationen på Försäljning tabellen och INSERT-operationen i SalesHistory tabell. Låt oss verifiera detta genom att välja mellan båda tabellerna:

UPDATE-aktiviteten spårades i försäljningshistoriken tabellen som nytt rekord. Innan den posten har vi en annan som visar när posten infogades först.

DELETE Trigger

Hittills har vi testat FOR eller EFTER typ av utlösare för både INSERT- eller UPDATE-operationer. Nu kan vi försöka använda I STÄLLET FÖR typ av DML-utlösare för DELETE-operationen. Använd skriptet nedan:

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

DELETE-utlösaren har skapats. Det kommer att skicka ett felmeddelande till klienten istället för att utföra kommandot DELETE på Försäljning bord.

Låt oss försöka ta bort posten Försäljnings-ID =3 från Försäljning tabell med skriptet nedan:

DELETE FROM Sales
WHERE SalesId = 3

Vi har förhindrat användare från att ta bort poster från försäljningen tabell. Utlösaren gav ett felmeddelande.

Låt oss också verifiera om posten raderades från försäljningen tabellen och om det gjordes några ändringar i försäljningshistoriken tabell:

Eftersom vi har kört triggerskriptet före den faktiska DELETE-satsen med INSTEAD OF trigger, lyckades inte DELETE-operationen på SalesId=3 alls. Därför återspeglades inga förändringar i både försäljningen och Säljhistorik bord.

Låt oss ändra utlösaren med hjälp av skriptet nedan för att identifiera DELETE-försöket på bordet:

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

Utlösaren ändrades framgångsrikt. Låt oss ta bort SalesId =3 post från Försäljning tabell igen:

Utförande av DELETE-satsen visar 1 rad som påverkas meddelande två gånger. Låt oss kontrollera poster över försäljningen och Säljhistorik tabeller för att se exakt vad som händer där:

Logiken som användes i DELETE-utlösaren var att fånga alla DELETE-försök på bordet utan att faktiskt radera posten från Försäljning tabell med I STÄLLET FÖR utlösare. Vi kan bekräfta att posten inte togs bort från försäljningen tabellen, och en ny post infogades i försäljningshistoriken bord.

En enda trigger för att hantera INSERT, UPDATE och DELETE operation

Hittills har vi skapat 3 utlösare för att hantera INSERT, UPDATE och DELETE operationer på en enda tabell. Om vi ​​har flera triggers skulle det vara svårt att hantera dem, särskilt om de inte är ordentligt dokumenterade. Det kan uppstå prestandaproblem om utvecklarna använder motsägande logik över flera triggers.

Jag rekommenderar personligen att du använder en enda trigger med all logik kombinerad för att undvika potentiella dataförluster eller prestandaproblem. Vi kan prova att kombinera 3 triggers i en enda trigger för bättre prestanda. Men innan vi gör det, låt oss undersöka hur man SLAPPAR befintliga utlösare och hur man inaktiverar eller aktiverar utlösare.

Släpp utlösaren

För att slå samman 3 triggers till en enda måste vi först SLAPPA dessa 3 triggers. Det är möjligt via både SSMS och T-SQL-metoder.

Expandera Test i SSMS databas > Tabell > Försäljning tabell> Utlösare .

Vi kan se våra tre utlösare som har skapats hittills:

För att släppa en utlösare, högerklicka helt enkelt på den> Ta bort > OK .

Om du föredrar att använda T-SQL, se syntaxen nedan för att släppa triggern:

DROP TRIGGER <trigger_name>

Det finns TR_INS_Sales trigger som vi skapade på Försäljning tabell. Skriptet kommer att vara:

DROP TRIGGER TR_INS_Sales

Viktigt :Om du släpper en tabell försvinner alla utlösare som standard.

Inaktivera och aktivera trigger

Istället för att tappa utlösaren kan vi inaktivera den tillfälligt med Inaktivera utlösare alternativ via SSMS eller T-SQL.

I SSMS högerklickar du på Triggernamnet> Inaktivera . När utlösaren har inaktiverats aktiveras den inte förrän du aktiverar den igen.

Medan utlösaren fungerar visas Aktivera alternativet är nedtonat. När du inaktiverar den visas Aktivera alternativet blir synligt och aktivt.

Om du föredrar att använda T-SQL kan du inaktivera och aktivera triggers med hjälp av skripten nedan:

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

För att inaktivera och aktivera vår specifika TR_INS_försäljning trigger på Försäljning tabell använder vi nedanstående skript:

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

Således har vi lärt oss hur man DROPPA , AVAKTIVERA och AKTIVERA utlösare. Jag släpper tre befintliga utlösare och skapar en enda utlösare som täcker alla tre operationer eller infogar, uppdaterar och tar bort med skriptet nedan:

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

Skapandet av singelutlösaren lyckades. Vi har använt logik för att identifiera operationen genom att använda de infogade och borttagna tabellerna.

För INSERT-operationen kommer den borttagna tabellen inte att fyllas i. För DELETE-operationen kommer den infogade tabellen inte att fyllas i. Vi kan lätt identifiera dessa operationer. Om dessa två villkor inte stämmer överens är det en UPDATE-operation, och vi kan använda en enkel ELSE-sats.

Jag har använt UPDATE() funktion för att visa hur det fungerar. Om det fanns några uppdateringar på dessa kolumner skulle åtgärden UPPDATERING utlösas. Vi kan också använda COLUMNS_UPDATED() funktion som vi diskuterade tidigare för att identifiera en UPDATE-operation också.

Låt oss testa vår nya trigger genom att infoga en ny post:

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

Verifiera poster över försäljningen och Säljhistorik tabeller visar data enligt nedan:

Låt oss försöka uppdatera SalesId =2 spela in:

UPDATE Sales
SET price = 250
WHERE SalesId = 2;

Låt oss prova ett DELETE-skript via den här proceduren på SalesId =4 spela in:

DELETE FROM Sales
WHERE SalesId = 4;

Som vi kan märka är SalesId =4 togs bort från försäljningen tabell eftersom detta är ett FÖR eller EFTER trigger, vilket gör att DELETE-operationen lyckas på försäljningen tabell och infoga sedan en post i försäljningshistoriken bord.

Syftet med DML-utlösare

DML-utlösare fungerar effektivt för följande scenarier:

  1. Spåra historiska ändringar av INSERT, UPDATE och DELETE-operationerna på en specifik tabell.
  2. Granska DML-händelserna som händer på en tabell utan att exponera granskningsaktiviteten för användarna.
  3. Förhindra att DML-ändringar sker på ett bord via I STÄLLET FÖR utlöser och varnar användare med ett specifikt felmeddelande.
  4. Skicka aviseringar till riktade personer när du uppnår fördefinierade villkor.
  5. Starta SQL Server Agent Job eller någon annan process när du uppnår några fördefinierade villkor.

Och du kan använda dem för alla andra affärslogikkrav som du kan implementera med T-SQL-satser.

Slutsats

DML-utlösarkroppen liknar den lagrade proceduren. Vi kan implementera vilken affärslogik som helst, men vi måste vara försiktiga när vi skriver den logiken för att undvika potentiella problem.

Även om SQL Server stöder skapandet av flera utlösare på en enda tabell, är det bättre att konsolidera till en enda utlösare. På så sätt kan du enkelt underhålla triggers och felsöka dem snabbare. Närhelst DML-utlösare implementeras för granskningsändamål, se till att STÄLL IN NOCOUNT PÅ alternativet används effektivt.

I nästa artikel kommer vi att undersöka DDL-utlösare och inloggningsutlösare.


  1. SQL-klient för Mac OS X som fungerar med MS SQL Server

  2. Hur man skapar en databas i MySQL Workbench med hjälp av GUI

  3. Hur man distribuerar mycket tillgänglig PostgreSQL med en enda slutpunkt för WordPress

  4. Python-anrop sql-server lagrad procedur med tabellvärderad parameter