sql >> Database teknologi >  >> RDS >> Sqlserver

SQL Server-triggere:DML-triggere

I SQL Server er triggere databaseobjekter, som bliver eksekveret, når der sker en udløsende hændelse på databasen eller serveren.

Triggere spiller en nøglerolle i at opnå forretningskrav som at advare målrettede personer, starte et job eller andre operationer. Da triggere kan håndtere mange sådanne operationer, bør vi definere dem med omhu for at undgå præstationspåvirkninger.

I denne artikel vil vi undersøge triggere, typer af triggere og forskellige tilgængelige triggermuligheder. Vi vil også undersøge de nødvendige forholdsregler, mens vi bruger DML-triggere.

Udløsere i SQL

En udløser er en speciel type lagret procedure, som udføres ved definerede hændelser, der kører det script, der er defineret i udløserlegemet. Der er flere typer triggere:

  • DML-udløsere – til at udføre DML-handlinger som INSERT, UPDATE og DELETE-kommandoer på tabeller.
  • DDL-udløsere – til at udføre DDL-handlinger som CREATE, ALTER og DROP-kommandoer på tværs af alle objekter på databasen eller serveren.
  • Logonudløsere – for forsøget på at logge på en forekomst af SQL Server under LOGON-hændelsen.

DML-udløsere i SQL Server

DML-triggere er dem, der udløses af DML-kommandoer (INSERT, UPDATE eller DELETE) på tabeller eller visninger. Vi kan kun oprette sådanne triggere på de tabeller eller visninger, hvor data findes, så de accepterer DML-kommandoer på dem.

Baseret på tidspunktet for udløsning/påkaldelse kan DML-udløsere være af følgende typer:

  • FOR eller EFTER Triggertype – triggeren aktiveres efter en vellykket gennemførelse af DML-sætningen på en tabel eller visning. Bemærk:det er muligt kun at oprette AFTER-udløseren på tabeller, ikke visninger.
  • I STEDET FOR Triggertype – Trigger vil blive påkaldt før (I STEDET FOR) DML-scriptet, der udføres på tabellen eller visningen.

SQL Server opretter to specielle eller logiske tabeller med navnet INSERTED og OPDATERET hver gang der oprettes DML-udløsere på tværs af tabeller eller visninger. Disse logiske tabeller hjælper med at identificere de registreringsændringer, der sker via INSERT/UPDATE/DELETE operationer. På denne måde sikrer det, at DML-triggere fungerer effektivt.

  • INSERT logisk tabel gemmer kopier af nye registreringer af poster, der er ændret under INSERT- og UPDATE-operationerne. Når en ny post føjes til den faktiske tabel, tilføjes den også i INSERTED-tabellen. På samme måde flytter eventuelle ændringer på eksisterende poster via UPDATE-sætningen de seneste værdier til INSERTED-tabellen og ældre værdier – til den DELETED logiske tabel.
  • SLETTET logisk tabel gemmer kopier af ældre værdier under UPDATE- og DELETE-operationerne. Når en post opdateres, bliver ældre værdier kopieret til DELETED-tabellen. Når en post slettes fra den faktiske tabel, indsættes poster i tabellen SLETTEDE.

SQL Server har indbyggede funktioner COLUMN_UPDATED() og OPDATERING() for at identificere tilstedeværelsen af ​​INSERT eller UPDATE operationer på den bestemte kolonne.

  • COLUMN_UPDATED() returnerer varbinære værdier af kolonner, der blev påvirket af INSERT- eller UPDATE-operationerne.
  • OPDATERING() accepterer kolonnenavnet som en inputparameter og returnerer informationen, om den kolonne har dataændringer som en del af INSERT- eller UPDATE-operationerne.

IKKE TIL REPLIKATION egenskab kan bruges i DML-udløsere for at undgå at udløse dem for ændringer, der kommer via replikeringsprocessen.

DML-triggere kan også oprettes med .Net Framework Common Language Runtime (CLR).

Systemet DMV sys.triggers gemmer listen over alle database-omfattede triggere. Vi kan bruge nedenstående forespørgsel til at hente detaljerne for alle DML-udløsere i en database:

SELECT * FROM sys.triggersWHERE type ='TR'; 

DML Trigger-definitionerne kan ses, hvis triggeren ikke er krypteret. Vi bruger en af ​​nedenstående muligheder:

sys.sql_modules

VÆLG OBJECT_SCHEMA_NAME(objekt_id, db_id()) Skemanavn, OBJECT_NAVN(objekt_id) Trigger_Name, definitionFROM sys.sql_modules WHERE object_id =OBJECT_ID();  

OBJECT_DEFINITION() funktion

SELECT OBJECT_DEFINITION (OBJECT_ID()) AS ObjectDefinition;

sp_helptext lagret procedure

EXEC sp_helptext ''; 

Alle mulige DML-begivenheder er tilgængelige i sys.events bord. Vi kan se dem ved at bruge nedenstående forespørgsel:

VÆLG * FRA sys.events; 

DML-udløserens syntaks

OPRET TRIGGER ON  [ MED  [ ,...n ] ] { FOR | EFTER | I STEDET FOR} AS { sql_statement | EKSTERNT NAVN  }  

Til demoformål har jeg oprettet to tabeller med navnet Salg og Salgshistorik med få kolonner 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, er salgshistorikken tabellen har 3 yderligere kolonner til at spore den ændrede dato og det brugernavn, der påkaldte ændringen. Hvis det er nødvendigt, kan vi have en mere Identitetskolonne defineret og gør det også til en primær nøgle.

INSERT Trigger

Vi opretter en simpel INSERT-trigger på Salg tabeller for at INDSÆTTE eventuelle nye registreringsændringer til Salgshistorik bord. Brug nedenstående script:

CREATE TRIGGER TR_INS_Salg ON SalesFOR INSERT ASBEGIN INSERT INTO SalesHistory(SalesId,SalesDate,Itemcount,price,ChangeType) SELECT SalesId ,SalesDate ,Itemcount ,Price ,'INSERT' FROM insert  

For at forklare triggersyntaksen har vi oprettet en DML-trigger ved navn TR_INS_Sales Salg bord. Den skal kun udløse triggeren for INSERT-operationerne – indsættelse af poster i SalesHistory tabel fra den indsatte tabel.

Som vi ved, indsat er en logisk tabel, der fanger de ændringer, der sker i kildetabellen (Salg tabel i vores tilfælde).

Vi kan se en anden speciel logisk tabel slettet i UPDATE-udløseren, fordi den slettede tabel er ikke anvendelig for INSERT-triggere.

Lad os tilføje en ny post for at kontrollere, om poster er indsat i Salgshistorikken tabel automatisk.

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

Selvom vi kun har indsat én post i Salg tabel, får vi 2 linjer af 1 berørt række besked. Den anden post vises på grund af INSERT-handlingen som en del af triggeren, der påkaldes af INSERT-aktiviteten på Salg tabel – indsættelse af en post i salgshistorikken tabel.

Lad os verificere registreringerne på tværs af både salg og Salgshistorik tabeller:

VÆLG * FRA SalgsVÆLG * FRA Salgshistorik 

Vi kan se den ChangeDate og ChangedUser udfyldes automatisk. Det er fordi vi har designet vores Historie tabel med standardværdierne som GETDATE() og SUSER_NAME() .

Slutbrugere kan se via Trigger eller på anden måde, deres INSERT blev revideret via den yderligere 1 berørte række besked. Hvis du vil overvåge ændringer uden at informere brugerne, skal du anvende SET ROWCOUNT ON kommando. Det undertrykker de viste resultater for DML-operationer, der sker inde i triggeren.

Lad os ÆNDRE vores trigger ved at bruge scriptet med SET ROWCOUNT ON mulighed og se den i aktion:

ALTER TRIGGER TR_INS_Salg ON SalesFOR INSERT ASBEGINSET NOCOUNT ON INSERT INTO SalesHistory(SalesId,SalesDate,Itemcount,price,ChangeType) SELECT SalesId ,SalesDate ,Itemcount ,Pris ,'INSERT'END GO incode 

Nu indsætter vi endnu en post i Salg tabel:

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

Vi kan kun se en enkelt 1 række påvirket besked. Derfor vil målgruppen muligvis slet ikke få besked om, at deres handlinger er under overvågning.

Lad os kontrollere, om INSERT-udløseren blev aktiveret ved at bekræfte Salg og Salgshistorik tabeller.

Ja, INSERT-begivenheden på Salg tabel blev udløst. Optegnelserne blev indsat i salgshistorikken tabel uden at give brugerne besked.

Derfor, hvis du opretter udløsere til revisionsformål, INDSTIL ANTAL TIL er nødvendigt. Det giver mulighed for revision uden at advare nogen.

OPDATERING-trigger

Før du opretter en faktisk UPDATE-trigger på Salg tabel, lad os igen henvise til de specielle logiske indsatte og slettede tabeller. Opret et eksempel på UPDATE-trigger på Salg tabel:

OPRET TRIGGER TR_UPD_Salg PÅ Salg FOR OPDATERING ASBEGINVÆLG * FRA indsat VÆLG * FRA slettetENDGO 

UPDATE-udløseren blev oprettet. Lad os nu indsætte en ny post forkert. Senere opdaterer vi den for at bekræfte UPDATE-udløseren i aktion:

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

Vi har nedenstående optegnelser på tværs af salg og Salgshistorik tabel:

Lad os opdatere SalesId =3 i Salg tabel med nye værdier. Vi vil se dataene på tværs af de indsatte og slettede tabeller:

OPDATER SalesSET SalesDate ='2021-03-01' , Vareantal =3 , pris =500WHERE SalesId =3 

Når UPDATE-handlingen finder sted, vil alle nye eller ændrede værdier være tilgængelige i den indsatte tabel, og gamle værdier vil være tilgængelige i den slettede tabel:

Lad os nu ændre UPDATE-triggeren med nedenstående script og verificere den i aktion:

ALTER TRIGGER TR_UPD_Salg ON Sales FOR UPDATE AS BEGIN INSERT INTO SalesHistory(SalesId,SalesDate,Itemcount,price,ChangeType) SELECT SalesId ,SalesDate ,Itemcount ,Price ,'UPDATE'FRA  

UPDATE-triggeren blev ændret med succes, og vi kan udføre det samme UPDATE-script igen:

Nu kan vi se 1 række påvirket besked to gange. Det angiver udførelsen af ​​OPDATERING på Salg tabellen og INSERT-operationen i Salgshistorik bord. Lad os bekræfte dette ved at vælge på tværs af begge tabeller:

UPDATE-aktiviteten blev sporet i Salgshistorikken tabel som ny rekord. Før den post har vi en anden, der viser, hvornår posten blev indsat først.

DELETE Trigger

Indtil nu har vi testet FOR eller EFTER type triggere for både INSERT eller UPDATE operationer. Nu kan vi prøve at bruge I STEDET FOR type DML-udløser for DELETE-handlingen. Brug nedenstående script:

CREATE TRIGGER TR_DEL_Sales ON SalesI STED OF DELETE ASBEGIN RAISERROR ('Underret salgsteam', 16, 10); ENDGO 

DELETE-udløseren er oprettet. Det vil sende en fejlmeddelelse til klienten i stedet for at udføre kommandoen DELETE på Salg tabel.

Lad os prøve at slette posten Salgs-ID =3 fra Salg tabel ved hjælp af scriptet nedenfor:

DELETE FROM SalesWHERE SalesId =3 

Vi har forhindret brugere i at slette poster fra Salg bord. Udløseren rejste en fejlmeddelelse.

Lad os også kontrollere, om posten blev slettet fra Salg tabellen, og hvis der var ændringer i Salgshistorikken tabel:

Da vi har udført trigger-scriptet før den faktiske DELETE-sætning ved hjælp af INSTEAD OF trigger, var DELETE-operationen på SalesId=3 slet ikke vellykket. Derfor blev ingen ændringer afspejlet på tværs af både salg og Salgshistorik tabel.

Lad os ændre udløseren ved hjælp af nedenstående script for at identificere SLET-forsøget på bordet:

ALTER TRIGGER TR_DEL_Salg ON SalesI STEDET FOR DELETE ASBEGIN INSERT INTO SalesHistory(SalesId,SalesDate,Itemcount,price,ChangeType) SELECT SalesId ,SalesDate ,Itemcount ,price ,'DELETE> ATP END GOROM'FRA 

Udløseren blev ændret. Lad os slette SalesId =3 rekord fra Salg tabel igen:

Udførelse af DELETE-sætningen viser den 1 berørte række besked to gange. Lad os tjekke registreringer på tværs af Salg og Salgshistorik tabeller for at se, hvad der præcist sker der:

Logikken, der blev brugt i DELETE-triggeren, var at fange ethvert DELETE-forsøg på bordet uden faktisk at slette posten fra Salg tabel ved hjælp af I STEDET FOR udløser. Vi kan bekræfte, at posten ikke blev slettet fra Salg tabel, og en ny post blev indsat i Salgshistorikken tabel.

En enkelt trigger til at håndtere INSERT, UPDATE og DELETE operation

Indtil nu har vi oprettet 3 triggere til at håndtere INSERT, UPDATE og DELETE operationerne på en enkelt tabel. Hvis vi har flere triggere, ville det være svært at håndtere dem, især hvis de ikke er ordentligt dokumenteret. Der kan være problemer med ydeevnen, hvis udviklerne brugte modstridende logik på tværs af flere triggere.

Jeg anbefaler personligt at bruge en enkelt trigger med al logik kombineret for at undgå potentielt datatab eller ydeevneproblemer. Vi kan prøve at kombinere 3 triggere i en enkelt trigger for bedre ydeevne. Men før vi gør det, lad os undersøge, hvordan man SLIPPER eksisterende triggere, og hvordan man deaktiverer eller aktiverer triggere.

Slip triggeren

For at slå 3 triggere sammen til en enkelt, skal vi først SLIPPE disse 3 triggere. Det er muligt via både SSMS og T-SQL tilgange.

Udvid Test i SSMS database > Tabeller > Salg tabel> Udløsere .

Vi kan se vores 3 triggere oprettet indtil videre:

For at slippe en trigger skal du blot højreklikke på den> Slet > OK .

Hvis du foretrækker at bruge T-SQL, se nedenstående syntaks for at slippe triggeren:

DROP TRIGGER  

Der er TR_INS_Sales trigger, vi oprettede på Salg bord. Scriptet vil være:

DROP TRIGGER TR_INS_Sales 

Vigtigt :Sletning af en tabel sletter alle triggere som standard.

Deaktiver og aktiver trigger

I stedet for at slippe udløseren kan vi deaktivere den midlertidigt med Deaktiver trigger mulighed via SSMS eller T-SQL.

I SSMS skal du højreklikke på Triggernavnet> Deaktiver . Når den er deaktiveret, udløses triggeren ikke, før du aktiverer den igen.

Mens triggeren virker, er Aktiver indstillingen er nedtonet. Når du deaktiverer det, vises Aktiver mulighed bliver synlig og aktiv.

Hvis du foretrækker at bruge T-SQL, kan du deaktivere og aktivere triggere ved hjælp af nedenstående scripts:

-- For at deaktivere alle triggere på en specifik tabel DEAKTIVER TRIGGER ALLE PÅ ;-- For at deaktivere en specifik trigger på en tabel DEAKTIVER TRIGGER ;-- For at aktivere alle triggere på en tabel specifik tabelENABLE TRIGGER ALL ON ;-- At aktivere en specifik trigger på en tabel

For at deaktivere og aktivere vores særlige TR_INS_Sales trigger på Salg tabel, bruger vi nedenstående scripts:

-- For at deaktivere TR_INS_Salgstrigger på salgstabellen DEAKTIVER TRIGGER TR_INS_Salg ON Sales;-- At aktivere TR_INS_Sales trigger på salgstabellENABLE TRIGGER TR_INS_Salg ON Sales; 

Således har vi lært at DROP , DEAKTIVER , og AKTIVER udløser. Jeg dropper 3 eksisterende triggere og opretter en enkelt trigger, der dækker alle de 3 operationer eller indsætter, opdaterer og sletter ved hjælp af scriptet nedenfor:

DROP TRIGGER TR_INS_SalesDROP TRIGGER TR_UPD_SalesDROP TRIGGER TR_DEL_SalesGOCREATE TRIGGER TR_INS_UPD_DEL_Salg PÅ SalgFOR INDSÆT, OPDATERE, DELETEASBEGINHVIS (VÆLG ANTAL (*) FROM slettet) =0SalgBEGINRisDatoSÆLGE,SalgId.VÆLGT,SalgId.Id.C. SalesDate ,Itemcount ,price ,'INSERT' FROM insertedENDELSE IF (SELECT COUNT (*) FROM inserted) =0BEGIN INSERT INTO SalesHistory(SalesId,SalesDate,Itemcount,price,ChangeType) SELECT SalesId ,SalesDato,price'count,F' slettetENDELSE HVIS (OPDATERING (SalesDate) OR UPDATE (ItemCount) OR UPDATE (Price))BEGIN INSERT INTO SalesHistory(SalesId,SalesDate,Itemcount,price,ChangeType) SELECT SalesId ,SalesDate ,Itemcount ,price ,END UPDATE in' kode> 

Oprettelse af enkelt trigger var vellykket. Vi har brugt logik til at identificere operationen ved at bruge de indsatte og slettede tabeller.

For INSERT-operationen vil den slettede tabel ikke blive udfyldt. For DELETE-handlingen vil den indsatte tabel ikke blive udfyldt. Vi kan nemt identificere disse operationer. Hvis disse 2 betingelser ikke stemmer overens, er det en OPDATERING, og vi kan bruge en simpel ELSE-sætning.

Jeg har brugt OPDATERING() funktion for at vise, hvordan det fungerer. Hvis der var nogen opdateringer på disse kolonner, udløses UPDATE-triggerhandlingen. Vi kan også bruge COLUMNS_UPDATED() funktion, som vi diskuterede tidligere for også at identificere en OPDATERING.

Lad os teste vores nye trigger ved at indsætte en ny post:

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

Bekræftelse af registreringer på tværs af Salg og Salgshistorik tabeller viser data som nedenfor:

Lad os prøve at opdatere SalesId =2 optag:

OPDATERING Salgssætpris =250WHERE SalesId =2; 

Lad os prøve et DELETE-script via denne procedure på SalesId =4 optag:

DELETE FROM SalesWHERE SalesId =4; 

Som vi kan bemærke, er SalesId =4 blev slettet fra Salg tabel, da dette er en FOR eller EFTER trigger, hvilket får DELETE-operationen til at lykkes på Salg tabel, og indsæt derefter en post i Salgshistorikken tabel.

Formål med DML-udløsere

DML-triggere fungerer effektivt til følgende scenarier:

  1. Spor historiske ændringer af INSERT-, UPDATE- og DELETE-handlinger på en bestemt tabel.
  2. Revider de DML-hændelser, der finder sted på en tabel uden at udsætte revisionsaktiviteten for brugerne.
  3. Forhindrer, at DML-ændringer sker på et bord via I STEDET FOR udløser og advarer brugere med en specifik fejlmeddelelse.
  4. Send meddelelser til målrettede personer, når du opnår foruddefinerede betingelser.
  5. Start SQL Server Agent-jobbet eller en hvilken som helst anden proces, når du opnår foruddefinerede betingelser.

Og du kan bruge dem til alle andre forretningslogiske krav, som du kan implementere med T-SQL-sætninger.

Konklusion

DML-udløserlegemet ligner den lagrede procedure. Vi kan implementere enhver påkrævet forretningslogik, men vi skal være forsigtige, når vi skriver den involverede logik for at undgå potentielle problemer.

Selvom SQL Server understøtter oprettelsen af ​​flere triggere på en enkelt tabel, er det bedre at konsolidere til en enkelt trigger. På denne måde kan du nemt vedligeholde triggere og fejlfinde dem hurtigere. Når DML-udløsere implementeres til revisionsformål, skal du sørge for, at SET NOCOUNT ON mulighed bruges effektivt.

I den næste artikel vil vi undersøge DDL-triggere og logon-triggere.


  1. Forståelse af Hadoop Input Output System

  2. Find og erstat tekst i hele tabellen ved hjælp af en MySQL-forespørgsel

  3. En introduktion til Hadoop og Big Data

  4. Meddelelse om den generelle tilgængelighed af SQL Compliance Manager 5.9