sql >> Database >  >> RDS >> Sqlserver

SQL Server-triggers:DML-triggers

In SQL Server zijn triggers database-objecten die worden uitgevoerd wanneer een triggergebeurtenis plaatsvindt op de database of server.

Triggers spelen een sleutelrol bij het bereiken van zakelijke vereisten, zoals het waarschuwen van gerichte mensen, het starten van een baan of andere activiteiten. Aangezien Triggers veel van dergelijke bewerkingen aankan, moeten we ze zorgvuldig definiëren om prestatie-impact te voorkomen.

In dit artikel zullen we triggers, soorten triggers en verschillende beschikbare trigger-opties onderzoeken. We zullen ook de nodige voorzorgsmaatregelen onderzoeken bij het gebruik van DML-triggers.

Triggers in SQL

Een trigger is een speciaal type Opgeslagen procedure die wordt uitgevoerd bij gedefinieerde gebeurtenissen, waarbij het script wordt uitgevoerd dat is gedefinieerd in de triggertekst. Er zijn verschillende soorten triggers:

  • DML-triggers – voor het uitvoeren van DML-bewerkingen zoals INSERT-, UPDATE- en DELETE-commando's op tabellen.
  • DDL-triggers - voor het uitvoeren van DDL-bewerkingen zoals CREATE-, ALTER- en DROP-opdrachten voor alle objecten op de database of server.
  • Aanmeldingstriggers – voor de poging om in te loggen op een exemplaar van SQL Server tijdens de LOGON-gebeurtenis.

DML-triggers in SQL Server

DML-triggers zijn triggers die worden geactiveerd door DML-commando's (INSERT, UPDATE of DELETE) op tabellen of views. We kunnen dergelijke triggers alleen voor die tabellen of weergaven maken waar gegevens zich bevinden, zodat ze DML-opdrachten daarop accepteren.

Op basis van het tijdstip van afvuren/aanroepen kunnen DML-triggers van de volgende typen zijn:

  • VOOR of NA Triggertype - de trigger wordt aangeroepen na de succesvolle voltooiing van de DML-instructie op een tabel of weergave. Opmerking:het is mogelijk om de AFTER-trigger alleen voor tabellen te maken, niet voor weergaven.
  • IN PLAATS VAN Triggertype - Trigger wordt aangeroepen voordat (IN PLAATS VAN) het DML-script dat op de tabel of weergave wordt uitgevoerd.

SQL Server maakt twee speciale of logische tabellen met de naam INSERTED en GE-UPDATE wanneer DML-triggers worden gemaakt in tabellen of weergaven. Deze logische tabellen helpen bij het identificeren van de recordwijzigingen die plaatsvinden via INSERT/UPDATE/DELETE-bewerkingen. Op deze manier zorgt het ervoor dat DML-triggers effectief werken.

  • INGEVOERD logische tabel slaat kopieën op van nieuwe records van records die zijn gewijzigd tijdens de INSERT- en UPDATE-bewerkingen. Wanneer een nieuwe record aan de eigenlijke tabel wordt toegevoegd, wordt deze ook toegevoegd aan de INSERTED-tabel. Evenzo verplaatsen alle wijzigingen in bestaande records via de UPDATE-instructie de nieuwste waarden naar de INSERTED-tabel en oudere waarden naar de logische tabel DELETED.
  • VERWIJDERD logische tabel slaat kopieën op van oudere waarden tijdens de bewerkingen UPDATE en DELETE. Telkens wanneer een record wordt bijgewerkt, worden oudere waarden gekopieerd naar de tabel VERWIJDERD. Telkens wanneer een record uit de eigenlijke tabel wordt verwijderd, worden records ingevoegd in de VERWIJDERDE tabel.

SQL Server heeft ingebouwde functies COLUMN_UPDATED() en UPDATE() om de aanwezigheid van INSERT- of UPDATE-bewerkingen op de betreffende kolom te identificeren.

  • COLUMN_UPDATED() geeft varbinaire waarden terug van kolommen die werden beïnvloed door de INSERT- of UPDATE-bewerkingen.
  • UPDATE() accepteert de kolomnaam als invoerparameter en retourneert de informatie of die kolom gegevenswijzigingen heeft als onderdeel van de INSERT- of UPDATE-bewerkingen.

DeNIET VOOR REPLICATIE eigenschap kan worden gebruikt in DML-triggers om te voorkomen dat ze worden geactiveerd voor wijzigingen die via het replicatieproces komen.

DML-triggers kunnen ook worden gemaakt met .Net Framework Common Language Runtime (CLR).

Het systeem DMV sys.triggers slaat de lijst op met alle triggers in het databasebereik. We kunnen de onderstaande query gebruiken om de details van alle DML-triggers binnen een database op te halen:

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

De definities van de DML-trigger kunnen worden bekeken als de trigger niet is versleuteld. We gebruiken een van de onderstaande opties:

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

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

sp_helptext opgeslagen procedure

EXEC sp_helptext '<trigger_name>';

Alle mogelijke DML-gebeurtenissen zijn beschikbaar in de sys.events tafel. We kunnen ze bekijken met behulp van de onderstaande zoekopdracht:

SELECT * 
FROM sys.events;

Syntaxis van de DML-trigger

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

Voor demo-doeleinden heb ik twee tabellen gemaakt met de naam Sales en Verkoopgeschiedenis met weinig kolommen in de testdatabase:

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

Zoals u kunt zien, is de SalesHistory tabel heeft 3 extra kolommen om de wijzigingsdatum en gebruikersnaam bij te houden die de wijziging aanriepen. Indien nodig kunnen we nog een Identiteitskolom hebben gedefinieerd en maak er ook een primaire sleutel van.

INSERT-trigger

We maken een eenvoudige INSERT-trigger op de Sales tabellen om eventuele nieuwe recordwijzigingen in SalesHistory in te voegen tafel. Gebruik het onderstaande script:

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

Om de triggersyntaxis uit te leggen, hebben we een DML-trigger gemaakt met de naam TR_INS_Sales op de Verkoop tafel. Het moet alleen de trigger activeren voor de INSERT-bewerkingen - records invoegen in de SalesHistory tabel uit de ingevoegde tabel.

Zoals we weten, ingevoegd is een logische tabel die de veranderingen vastlegt die plaatsvinden in de brontabel (de Verkoop tafel in ons geval).

We kunnen een andere speciale logische tabel zien verwijderd in de UPDATE-trigger omdat de deleted tabel is niet van toepassing op INSERT-triggers.

Laten we een nieuwe record toevoegen om te controleren of records zijn ingevoegd in de SalesHistory tafel automatisch.

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

Ook al hebben we slechts één record ingevoegd in de Sales tabel, krijgen we 2 regels van de 1 getroffen rij bericht. De tweede record verschijnt vanwege de INSERT-bewerking als onderdeel van de trigger die wordt aangeroepen door de INSERT-activiteit op de Sales tabel – een record invoegen in de SalesHistory tafel.

Laten we de records verifiëren voor zowel de Sales en Verkoopgeschiedenis tabellen:

SELECT * 
FROM Sales

SELECT * 
FROM SalesHistory

We kunnen zien dat ChangeDate en Gewijzigde Gebruiker worden automatisch ingevuld. Het is omdat we onze Geschiedenis . hebben ontworpen tabel met de standaardwaarden als GETDATE() en SUSER_NAME() .

Eindgebruikers kunnen via Trigger of op een andere manier zien dat hun INSERT is gecontroleerd via de extra 1 betrokken rij bericht. Als u wijzigingen wilt volgen zonder gebruikers hiervan op de hoogte te stellen, moet u de SET ROWCOUNT ON . toepassen opdracht. Het onderdrukt de resultaten die worden weergegeven voor DML-bewerkingen die plaatsvinden binnen de trigger.

Laten we onze trigger WIJZIGEN door het script te gebruiken met de SET ROWCOUNT ON optie en zie het in actie:

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 voegen we nog een record in de Sales tafel:

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

We zien slechts één enkele 1 rij aangetast bericht. Het is dus mogelijk dat de doelgroep geen melding krijgt dat hun acties helemaal niet worden gecontroleerd.

Laten we controleren of de INSERT-trigger is aangeroepen door de Sales te verifiëren en Verkoopgeschiedenis tabellen.

Ja, het INSERT-evenement op de Sales tabel is succesvol geactiveerd. De records zijn ingevoegd in de SalesHistory tafel zonder gebruikers op de hoogte te stellen.

Als u dus triggers maakt voor controledoeleinden, wordt de STEL NOCOUNT ON is noodzakelijk. Het maakt controle mogelijk zonder iemand te waarschuwen.

UPDATE-trigger

Voordat u een echte UPDATE-trigger maakt op de Sales tabel, laten we opnieuw verwijzen naar de speciale logische ingevoegde en verwijderde tabellen. Maak een voorbeeld van een UPDATE-trigger op de Sales tafel:

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

De UPDATE-trigger is gemaakt. Laten we nu een nieuw record onjuist INVOEGEN. Later zullen we het bijwerken om de UPDATE-trigger in actie te verifiëren:

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

We hebben de onderstaande gegevens over de Verkoop en Verkoopgeschiedenis tafel:

Laten we SalesId =3 updaten in de Verkoop tabel met nieuwe waarden. We zien de gegevens in de ingevoegde en verwijderde tabellen:

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

Wanneer de UPDATE-bewerking plaatsvindt, zijn alle nieuwe of gewijzigde waarden beschikbaar in de ingevoegde tabel en zijn oude waarden beschikbaar in de verwijderde tabel:

Laten we nu de UPDATE-trigger wijzigen met het onderstaande script en deze in actie verifiëren:

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

De UPDATE-trigger is met succes gewijzigd en we kunnen hetzelfde UPDATE-script opnieuw uitvoeren:

Nu kunnen we de 1 getroffen rij zien twee keer een bericht. Het geeft de uitvoering van de UPDATE-bewerking aan op de Sales tabel en de INSERT-bewerking op de SalesHistory tafel. Laten we dit verifiëren door in beide tabellen te selecteren:

De UPDATE-activiteit werd bijgehouden in de SalesHistory tabel als een nieuw record. Vóór die record hebben we een andere die laat zien wanneer de record het eerst werd ingevoegd.

Trigger VERWIJDEREN

Tot nu toe hebben we de FOR . getest of NA type triggers voor zowel INSERT- als UPDATE-bewerkingen. Nu kunnen we proberen de IN PLAATS VAN type DML-trigger voor de DELETE-bewerking. Gebruik het onderstaande script:

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

De DELETE-trigger is gemaakt. Het stuurt een foutmelding naar de klant in plaats van het DELETE-commando uit te voeren op de Sales tafel.

Laten we proberen record SalesID =3 te verwijderen uit de Verkoop tabel met behulp van het onderstaande script:

DELETE FROM Sales
WHERE SalesId = 3

We hebben voorkomen dat gebruikers records verwijderen uit de Sales tafel. De trigger gaf een foutmelding.

Laten we ook controleren of het record is verwijderd uit de Sales tabel en of er wijzigingen waren in de SalesHistory tafel:

Omdat we het triggerscript vóór de eigenlijke DELETE-instructie hebben uitgevoerd met behulp van de INSTEAD OF-trigger, was de DELETE-bewerking op SalesId=3 helemaal niet succesvol. Daarom werden er geen wijzigingen doorgevoerd in zowel de Verkoop en Verkoopgeschiedenis tafel.

Laten we de trigger wijzigen met behulp van het onderstaande script om de DELETE-poging op de tafel te identificeren:

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

De trigger is gewijzigd. Laten we de SalesId =3 . verwijderen record van de Verkoop tafel opnieuw:

Uitvoering van de DELETE-instructie toont de 1 betrokken rij twee keer een bericht. Laten we de records controleren in de Verkoop en Verkoopgeschiedenis tabellen om te zien wat daar precies gebeurt:

De logica die in de DELETE-trigger werd gebruikt, was om alle DELETE-pogingen op de tafel vast te leggen zonder de record daadwerkelijk te verwijderen uit de Sales tabel met behulp van de IN PLAATS VAN trekker. We kunnen bevestigen dat de record niet is verwijderd uit de Sales tabel, en er is een nieuw record ingevoegd in de SalesHistory tafel.

Een enkele trigger om de INSERT-, UPDATE- en DELETE-bewerkingen af ​​te handelen

Tot nu toe hebben we 3 triggers gemaakt om de INSERT-, UPDATE- en DELETE-bewerkingen op een enkele tabel af te handelen. Als we meerdere triggers hebben, zou het moeilijk zijn om ze te beheren, vooral als ze niet goed gedocumenteerd zijn. Er kunnen prestatieproblemen optreden als de ontwikkelaars tegenstrijdige logica gebruiken bij meerdere triggers.

Ik raad persoonlijk aan om een ​​enkele trigger te gebruiken met alle logica gecombineerd om mogelijk gegevensverlies of prestatieproblemen te voorkomen. We kunnen proberen 3 triggers in een enkele trigger te combineren voor betere prestaties. Maar laten we, voordat we het doen, eens kijken hoe we bestaande triggers kunnen DROPPEN en hoe triggers in- of uitgeschakeld kunnen worden.

Laat de trekker vallen

Om 3 triggers samen te voegen tot één trigger, moeten we deze 3 triggers eerst DROPPEN. Het is mogelijk via zowel SSMS- als T-SQL-benaderingen.

Vouw in SSMS de Test . uit database > Tafels > Verkoop tabel> Triggers .

We kunnen onze 3 triggers tot nu toe zien:

Om een ​​trigger te verwijderen, klikt u er met de rechtermuisknop op> Verwijderen > OK .

Als je liever T-SQL gebruikt, bekijk dan de onderstaande syntaxis om de trigger te laten vallen:

DROP TRIGGER <trigger_name>

Er is de TR_INS_Sales trigger die we hebben gemaakt op de Sales tafel. Het script wordt:

DROP TRIGGER TR_INS_Sales

Belangrijk :als je een tafel laat vallen, worden alle triggers standaard verwijderd.

Trigger in- en uitschakelen

In plaats van de trigger te laten vallen, kunnen we deze tijdelijk uitschakelen met de Disable trigger optie via SSMS of T-SQL.

Klik in SSMS met de rechtermuisknop op de Triggernaam> Uitschakelen . Eenmaal uitgeschakeld, wordt de trigger niet geactiveerd totdat je hem weer inschakelt.

Terwijl de trigger werkt, wordt de Enable optie is grijs. Wanneer u het uitschakelt, wordt de Inschakelen optie wordt zichtbaar en actief.

Als u liever T-SQL gebruikt, kunt u triggers in- en uitschakelen met behulp van de onderstaande scripts:

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

Om onze specifieke TR_INS_Sales in en uit te schakelen trigger op de Verkoop tabel gebruiken we de onderstaande scripts:

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

Zo hebben we geleerd hoe je DROP , UITSCHAKELEN , en INSCHAKELEN triggers. Ik laat 3 bestaande triggers vallen en maak een enkele trigger die alle 3 bewerkingen dekt of invoegen, bijwerken en verwijderen met behulp van het onderstaande script:

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

Het maken van een enkele trigger is gelukt. We hebben logica gebruikt om de bewerking te identificeren met behulp van de ingevoegde en verwijderde tabellen.

Voor de INSERT-bewerking wordt de verwijderde tabel niet gevuld. Voor de DELETE-bewerking wordt de ingevoegde tabel niet gevuld. We kunnen deze operaties gemakkelijk identificeren. Als deze 2 voorwaarden niet overeenkomen, is het een UPDATE-bewerking en kunnen we een eenvoudige ELSE-instructie gebruiken.

Ik heb de UPDATE() . gebruikt functie om te laten zien hoe het werkt. Als er updates voor die kolommen waren, zou de UPDATE-triggeractie worden geactiveerd. We kunnen ook de COLUMNS_UPDATED() . gebruiken functie die we eerder hebben besproken om ook een UPDATE-bewerking te identificeren.

Laten we onze nieuwe trigger testen door een nieuw record in te voegen:

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

Records verifiëren in de Verkoop en Verkoopgeschiedenis tabellen tonen gegevens zoals hieronder:

Laten we proberen SalesId =2 bij te werken opnemen:

UPDATE Sales
SET price = 250
WHERE SalesId = 2;

Laten we een DELETE-script proberen via deze procedure op SalesId =4 opnemen:

DELETE FROM Sales
WHERE SalesId = 4;

Zoals we kunnen zien, SalesId =4 is verwijderd uit de Verkoop tabel aangezien dit een FOR . is of NA trigger, waardoor de DELETE-bewerking slaagt op de Sales tabel en voeg vervolgens een record toe aan de SalesHistory tafel.

Doel van DML-triggers

DML-triggers zijn effectief voor de volgende scenario's:

  1. Houd historische wijzigingen van INSERT-, UPDATE- en DELETE-bewerkingen op een specifieke tabel bij.
  2. Controleer de DML-gebeurtenissen die plaatsvinden op een tafel zonder de auditactiviteit aan gebruikers bloot te stellen.
  3. Voorkom dat er DML-wijzigingen plaatsvinden op een tafel via IN PLAATS VAN triggers en waarschuwt gebruikers met een specifieke foutmelding.
  4. Stuur meldingen naar gerichte mensen wanneer aan vooraf gedefinieerde voorwaarden wordt voldaan.
  5. Start de SQL Server Agent-taak of een ander proces wanneer aan vooraf gedefinieerde voorwaarden wordt voldaan.

En u kunt ze gebruiken voor alle andere zakelijke logica-vereisten die u kunt implementeren met T-SQL-instructies.

Conclusie

De hoofdtekst van de DML-trigger is vergelijkbaar met de opgeslagen procedure. We kunnen elke vereiste bedrijfslogica implementeren, maar we moeten voorzichtig zijn bij het schrijven van die logica om mogelijke problemen te voorkomen.

Hoewel SQL Server het maken van meerdere triggers op een enkele tabel ondersteunt, is het beter om te consolideren tot een enkele trigger. Op deze manier kunt u triggers gemakkelijk onderhouden en sneller problemen oplossen. Wanneer DML-triggers worden geïmplementeerd voor controledoeleinden, zorg er dan voor dat de STEL NOCOUNT ON optie effectief wordt gebruikt.

In het volgende artikel zullen we DDL-triggers en aanmeldingstriggers onderzoeken.


  1. MySQL NULLIF() uitgelegd

  2. Easysoft ODBC-stuurprogramma's en de ODBCINST-bibliotheek

  3. SQL Pivot - Weet hoe u rijen naar kolommen converteert

  4. Milliseconden in mijn DateTime-wijzigingen wanneer opgeslagen in SQL Server