CREATE TRIGGER (T-SQL)

Werbung
CREATE TRIGGER (TRANSACT-SQL)
Diese Anweisung erstellt einen Trigger, einen besonderen Typ einer gespeicherten Prozedur,
der automatisch ausgeführt wird, wenn ein Benutzer versucht, die angegebene
Datenänderungsanweisung für die angegebene Tabelle auszuführen. Es können mehrere
Trigger für eine angegebene INSERT-, UPDATE- oder DELETE-Anweisung erstellt werden.
Syntax
CREATE TRIGGER trigger_name
ON table
[WITH ENCRYPTION]
{
{ FOR { [DELETE] [,] [INSERT] [,] [UPDATE] }
[WITH APPEND]
[NOT FOR REPLICATION]
AS sql_statement […n]
}
|
{ FOR { [INSERT] [,] [UPDATE] }
[WITH APPEND]
[NOT FOR REPLICATION]
AS
{
IF UPDATE (column)
[{AND | OR} UPDATE (column)]
[...n]
| IF (COLUMNS_UPDATED() {bitwise_operator}
updated_bitmask)
{ comparison_operator} column_bitmask [...n]
}
sql_statement [ ...n]
}
}
Argumente
trigger_name
Der Name des Triggers. Triggernamen müssen den Regeln für Bezeichner entsprechen
und innerhalb der Datenbank eindeutig sein. Die Angabe des Triggerbesitzernamens
ist optional.
table
Die Tabelle, für die der Trigger ausgeführt wird; sie wird manchmal auch als
Triggertabelle bezeichnet. Die Angabe des Tabellenbesitzernamens ist optional.
Sichten können nicht angegeben werden.
WITH ENCRYPTION
Verschlüsselt die syscomments-Einträge, die den Text der CREATE TRIGGERAnweisung enthalten.
{ [DELETE] [,] [INSERT] [,] [UPDATE] } | { [INSERT] [,] [UPDATE]}
Schlüsselwörter, die angeben, welche Datenänderungsanweisungen den Trigger
aktivieren, wenn sie auf diese Tabelle angewendet werden. Es muss mindestens eine
Option angegeben werden. Die Optionen können in beliebiger Kombination und
Reihenfolge in der Triggerdefinition angegeben werden. Wenn Sie mehrere Optionen
angeben, trennen Sie diese durch Kommas.
WITH APPEND
Gibt an, dass ein weiterer Trigger eines vorhandenen Typs hinzugefügt werden soll.
Diese optionale Klausel muss nur dann verwendet werden, wenn der
Kompatibilitätsgrad kleiner oder gleich 65 ist. Wenn der Kompatibilitätsgrad größer
oder gleich 70 ist, wird die optionale WITH APPEND-Klausel nicht benötigt, um
einen zusätzlichen Trigger eines vorhandenen Typs hinzuzufügen (dies ist das
Standardverhalten von CREATE TRIGGER, wenn der Kompatibilitätsgrad größer
oder gleich 70 ist.) Weitere Informationen finden Sie unter sp_dbcmptlevel.
NOT FOR REPLICATION
Zeigt an, dass der Trigger nicht ausgeführt werden soll, wenn ein Replikationsprozess
die vom Trigger betroffene Tabelle ändert.
AS
Die Aktionen, die der Trigger ausführen soll.
sql_statement
Die Triggerbedingung(en) und aktion(en). Triggerbedingungen enthalten zusätzliche
Kriterien, von denen es abhängt, ob der Versuch, eine INSERT-, DELETE- oder
UPDATE-Anweisung auszuführen, die Triggeraktion(en) auslöst.
Die in den Transact-SQL-Anweisungen festgelegten Triggeraktionen werden
ausgeführt, wenn versucht wird, die Benutzeraktion (UPDATE, INSERT oder
DELETE) durchzuführen.
Trigger dürfen beliebig viele Transact-SQL-Anweisungen jeglicher Art enthalten, mit
Ausnahme von SELECT-Anweisungen. Ein Trigger ist konzipiert, um Daten auf der
Grundlage einer Datenänderungsanweisung zu prüfen oder zu ändern, jedoch nicht um
Daten an den Benutzer zurückzugeben. Die Transact-SQL-Anweisungen in einem
Trigger enthalten oft Sprachkonstrukte zur Ablaufsteuerung. In CREATE TRIGGERAnweisungen werden einige besondere Tabellen verwendet:

deleted und inserted sind logische (konzeptionelle) Tabellen. Sie sind ähnlich
strukturiert wie die Tabelle, für die der Trigger definiert ist (die Tabelle, auf
die sich die Benutzeraktion bezieht), und enthalten die alten bzw. neuen Werte
der Zeilen, die durch die Benutzeraktion geändert werden können. Um
beispielsweise alle Werte in der deleted-Tabelle abzurufen, verwenden Sie:
SELECT *
FROM deleted

n
In einem DELETE-, INSERT- oder UPDATE-Trigger lässt SQL Server keine
text-, ntext- oder image-Spaltenverweise in der inserted- und der deletedTabelle zu, wenn der Kompatibilitätsgrad gleich 70 ist. Auf die text-, ntextund image-Werte in der inserted- und der deleted-Tabelle kann nicht
zugegriffen werden. Um den neuen Wert in einem INSERT- oder UPDATETrigger abzurufen, verknüpfen Sie die inserted-Tabelle mit der ursprünglichen
Aktualisierungstabelle. Wenn der Kompatibilitätsgrad kleiner oder gleich 65
ist, werden NULL-Werte zurückgegeben für die Spalten text, ntext oder
image der inserted- oder deleted-Tabelle, die NULL-Werte zulassen.
Zeichenfolgen der Länge Null werden zurückgegeben, wenn die Spalten keine
NULL-Werte zulassen.
Ein Platzhalter, der anzeigt, dass mehrere Transact-SQL-Anweisungen in den Trigger
aufgenommen werden können. In die IF UPDATE (column)-Anweisung können
mehrere Spalten aufgenommen werden, indem die UPDATE (column)-Klausel
wiederholt wird.
IF UPDATE (column)
Testet, ob eine INSERT- oder UPDATE-Aktion an einer angegebenen Spalte
vorgenommen wurde, und wird nicht mit DELETE-Operationen verwendet. Es
können mehrere Spalten angegeben werden. Da der Tabellenname in der ON-Klausel
angegeben ist, nehmen Sie den Tabellennamen nicht vor dem Spaltennamen in eine IF
UPDATE-Klausel auf. Um mehrere Spalten auf eine INSERT- oder UPDATE-Aktion
zu testen, geben Sie nach der ersten eine separate UPDATE(column)-Klausel an.
Anmerkung Die IF UPDATE (column)-Klausel funktioniert genauso wie eine IF-,
IF…ELSE- oder WHILE-Anweisung und kann den BEGIN…END-Block verwenden.
Weitere Informationen finden Sie unter Sprachkonstrukte zur Ablaufsteuerung.
UPDATE(column) kann überall innerhalb des Triggerrumpfes verwendet werden.
column
Der Name der auf eine INSERT- oder UPDATE-Aktion zu testenden Spalte. Diese
Spalte kann einen beliebigen von SQL Server unterstützten Datentyp haben. Weitere
Informationen finden Sie unter Datentypen.
IF (COLUMNS_UPDATED())
Testet in einem INSERT- oder UPDATE-Trigger, ob die genannte(n) Spalte(n)
eingefügt oder aktualisiert wurde(n). COLUMNS_UPDATED gibt ein varbinaryBitmuster zurück, das anzeigt, welche Spalten in der Tabelle eingefügt oder
aktualisiert wurden.
COLUMNS_UPDATED kann überall innerhalb des Triggerrumpfes verwendet
werden.
bitwise_operator
Der im Vergleich zu verwendende bitweise Operator.
updated_bitmask
Die ganzzahlige Bitmaske der tatsächlich aktualisierten oder eingefügten Spalten. Die
t1-Tabelle enthält beispielsweise die Spalten C1, C2, C3, C4 und C5. Um zu prüfen,
ob die Spalten C2, C3 und C4 aktualisiert wurden (wenn die t1-Tabelle einen
UPDATE-Trigger hat), geben Sie den Wert 14 ein. Um zu prüfen, ob nur die C2Spalte aktualisiert wurde, geben Sie den Wert 2 ein.
comparison_operator
Der Vergleichsoperator. Verwenden Sie das Gleichzeichen (=), um zu prüfen, ob alle
in updated_bitmask angegebenen Spalten tatsächlich aktualisiert wurden. Verwenden
Sie das Größer-als-Symbol (>), um zu prüfen, ob eine oder mehrere der in
updated_bitmask angegebenen Spalten aktualisiert wurden.
column_bitmask
Die ganzzahlige Bitmaske der Spalten, für die zu prüfen ist, ob sie aktualisiert oder
eingefügt wurden.
Hinweise
Trigger werden häufig verwendet, um Geschäftsregeln und Datenintegrität zu erzwingen.
SQL Server bietet deklarative referenzielle Integrität (DRI) durch die Anweisungen für das
Erstellen einer Tabelle (ALTER TABLE und CREATE TABLE); DRI bietet jedoch keine
datenbankübergreifende referenzielle Integrität. Um referenzielle Integrität (Regeln über die
Beziehungen zwischen den Primär- und Fremdschlüsseln von Tabellen) zu erzwingen,
verwenden Sie Primär- und Fremdschlüsseleinschränkungen (die PRIMARY KEY- und
FOREIGN KEY-Schlüsselwörter der ALTER TABLE- und der CREATE TABLEAnweisung). Wenn Einschränkungen für die Triggertabelle vorhanden sind, werden sie vor
der Triggerausführung überprüft. Wenn PRIMARY KEY- oder FOREIGN KEYEinschränkungen verletzt werden, wird der Trigger nicht ausgeführt (ausgelöst).
Anmerkung Ob SQL Server eine leere Zeichenfolge als einzelnes Leerzeichen oder als echte
leere Zeichenfolge interpretiert, hängt von der Einstellung für sp_dbcmptlevel ab. Ist der
Kompatibilitätsgrad kleiner oder gleich 65, interpretiert SQL Server leere Zeichenfolgen als
einzelne Leerzeichen. Ist der Kompatibilitätsgrad gleich 70, interpretiert SQL Server leere
Zeichenfolgen als leere Zeichenfolgen. Weitere Informationen finden Sie unter
sp_dbcmptlevel.
Einschränkungen bei der Verwendung von Triggern
CREATE TRIGGER muss die erste Anweisung in einem Stapel sein und kann sich auf nur
eine Tabelle beziehen.
Ein Trigger kann nur in der aktuellen Datenbank erstellt werden; er darf jedoch auf Objekte
außerhalb der aktuellen Datenbank verweisen.
Wenn der Name des Triggerbesitzers angegeben ist (um den Trigger zu kennzeichnen),
kennzeichnen Sie den Tabellennamen auf die gleiche Weise.
Es ist möglich, in derselben CREATE TRIGGER-Anweisung dieselbe Triggeraktion für
mehrere Benutzeraktionen festzulegen (beispielsweise INSERT und UPDATE).
In einem Trigger kann jede beliebige SET-Anweisung angegeben werden. Die gewählte SETOption bleibt während der Ausführung des Triggers in Kraft und kehrt dann zur vorherigen
Einstellung zurück.
Wenn ein Trigger ausgelöst wird, werden die Ergebnisse, wie bei einer gespeicherten
Prozedur, an die aufrufende Anwendung zurückgegeben. Um zu verhindern, dass Ergebnisse
aufgrund einer Triggerauslösung an eine Anwendung zurückgegeben werden, verwenden Sie
in einem Trigger keine SELECT-Anweisungen, die Ergebnisse zurückgeben, oder
Anweisungen, die Variablenzuweisungen durchführen. Ein Trigger, der SELECTAnweisungen enthält, die Ergebnisse an den Benutzer zurückgeben, oder Anweisungen, die
Variablenzuweisungen durchführen, erfordert eine besondere Bearbeitung; diese
zurückgegebenen Ergebnisse müssten sonst in alle Anwendungen geschrieben werden, in
denen Änderungen an der Triggertabelle zulässig sind. Wenn Variablenzuweisungen in einem
Trigger erfolgen müssen, verwenden Sie eine SET NOCOUNT-Anweisung am Anfang des
Triggers, um die Rückgabe von Resultsets zu verhindern.
Ein Trigger kann nicht für eine Sicht erstellt werden.
Eine TRUNCATE TABLE-Anweisung wird von einem DELETE-Trigger nicht
berücksichtigt. Eine TRUNCATE TABLE-Anweisung wirkt sich zwar genauso aus wie eine
DELETE-Anweisung ohne WHERE-Klausel (durch sie werden alle Zeilen entfernt), sie wird
jedoch nicht protokolliert und kann deshalb keinen Trigger ausführen. Da der
Tabellenbesitzer standardmäßig die Berechtigung für die TRUNCATE TABLE-Anweisung
hat und diese nicht übertragbar ist, ist es nur für den Tabellenbesitzer relevant, dass durch eine
TRUNCATE TABLE-Anweisung ein DELETE-Trigger unbeabsichtigt umgangen werden
kann.
Die WRITETEXT-Anweisung, ob protokolliert oder nicht protokolliert, aktiviert keinen
Trigger.
Die folgenden Transact-SQL-Anweisungen sind in einem Trigger nicht zulässig:
ALTER
ALTER PROCEDURE
DATABASE
ALTER TRIGGER ALTER VIEW
CREATE
CREATE INDEX
DEFAULT
CREATE RULE
CREATE SCHEMA
CREATE TRIGGER CREATE VIEW
DISK INIT
DISK RESIZE
DROP DEFAULT DROP INDEX
DROP RULE
DROP TABLE
DROP VIEW
GRANT
LOAD LOG
RESTORE
DATABASE
REVOKE
RECONFIGURE
TRUNCATE
UPDATE STATISTICS
TABLE
ALTER TABLE
CREATE DATABASE
CREATE
PROCEDURE
CREATE TABLE
DENY
DROP DATABASE
DROP PROCEDURE
DROP TRIGGER
LOAD DATABASE
RESTORE LOG
Anmerkung Da SQL Server keine benutzerdefinierten Trigger in Systemtabellen unterstützt,
sollten Sie keine benutzerdefinierten Trigger für Systemtabellen erstellen.
Mehrere Trigger
SQL Server lässt das Erstellen mehrerer Trigger für jedes Datenänderungsereignis (DELETE,
INSERT oder UPDATE) zu. Wenn zum Beispiel CREATE TRIGGER FOR UPDATE für
eine Tabelle ausgeführt wird, die bereits einen UPDATE-Trigger hat, wird ein zusätzlicher
UPDATE-Trigger erstellt. In früheren Versionen war pro Tabelle nur ein Trigger für jedes
Datenänderungsereignis (INSERT, UPDATE, DELETE) zulässig.
Anmerkung Das Standardverhalten für CREATE TRIGGER (bei einem Kompatibilitätsgrad
von 70) ist das Hinzufügen zusätzlicher Trigger zu vorhandenen Triggern, wenn die
Triggernamen sich unterscheiden. Bei gleichen Triggernamen gibt SQL Server eine
Fehlermeldung zurück. Ist der Kompatibilitätsgrad jedoch kleiner oder gleich 65, ersetzen
neue Trigger, die mit der CREATE TRIGGER-Anweisung erstellt wurden, vorhandene
Trigger desselben Typs, selbst dann, wenn die Triggernamen verschieden sind. Weitere
Informationen finden Sie unter sp_dbcmptlevel.
Rekursive Trigger
SQL Server lässt auch das rekursive Aufrufen von Triggern zu, wenn die recursive triggersEinstellung in sp_dboption aktiviert ist.
Rekursive Trigger ermöglichen zwei Arten von Rekursion:


Indirekte Rekursion
Direkte Rekursion
Bei der indirekten Rekursion aktualisiert eine Anwendung die T1-Tabelle, was den TR1Trigger auslöst, der die T2-Tabelle aktualisiert. Dadurch wird der TR2-Trigger ausgelöst, der
die T1-Tabelle aktualisiert.
Bei der direkten Rekursion aktualisiert die Anwendung die T1-Tabelle, was den TR1-Trigger
auslöst, der die T1-Tabelle aktualisiert. Da die T1-Tabelle aktualisiert wurde, wird der TR1Trigger erneut ausgelöst usw.
Dieses Beispiel verwendet sowohl indirekte als auch direkte Triggerrekursion. Angenommen,
zwei Aktualisierungstrigger, TR1 und TR2, sind für die T1-Tabelle definiert. Der TR1Trigger aktualisiert die T1-Tabelle rekursiv. Eine UPDATE-Anweisung führt TR1 und TR2
je einmal aus. Außerdem löst die Ausführung von TR1 die Ausführung von TR1 (rekursiv)
und TR2 aus. Die inserted- und die deleted-Tabelle für einen gegebenen Trigger enthalten
Zeilen, die nur der UPDATE-Anweisung entsprechen, die den Trigger aufgerufen hat.
Anmerkung Das oben beschriebene Verhalten tritt nur dann ein, wenn die recursive
triggers-Einstellung für sp_dboption aktiviert ist. Es gibt keine vorgeschriebene Reihenfolge
für die Ausführung mehrerer für ein festgelegtes Ereignis definierter Trigger. Jeder Trigger
sollte unabhängig sein.
Wenn einer der Trigger eine ROLLBACK TRANSACTION-Anweisung ausführt, werden
unabhängig von der Schachtelungsebene keine weiteren Trigger ausgeführt.
Geschachtelte Trigger
Trigger können bis zu 32 Ebenen tief geschachtelt werden. Falls ein Trigger eine Tabelle
ändert, für die es einen anderen Trigger gibt, wird der zweite Trigger aktiviert und kann dann
seinerseits einen dritten Trigger aufrufen usw. Wenn ein Trigger in der Kette eine
Endlosschleife auslöst, wird die zulässige Schachtelungsebenenzahl überschritten und der
Trigger abgebrochen. Um geschachtelte Trigger zu deaktivieren, stellen Sie die Option nested
triggers von sp_configure auf 0 (deaktiviert) ein. In der Standardkonfiguration sind
geschachtelte Trigger zulässig. Wenn geschachtelte Trigger deaktiviert sind, sind rekursive
Trigger ebenfalls deaktiviert, unabhängig von der recursive triggers-Einstellung von
sp_dboption.
Verzögerte Namensauflösung
SQL Server lässt zu, dass gespeicherte Transact-SQL-Prozeduren, Trigger und Stapel auf
Tabellen verweisen, die zur Kompilierzeit noch nicht vorhanden sind. Diese Fähigkeit wird
verzögerte Namensauflösung genannt. Wenn gespeicherte Transact-SQL-Prozeduren, Trigger
oder Stapel jedoch auf eine Tabelle verweisen, die in der gespeicherten Prozedur oder dem
Trigger definiert ist, wird nur dann eine Warnung bei der Erstellung ausgegeben, wenn der
(durch Ausführung von sp_dbcmptlevel eingestellte) Kompatibilitätsgrad 65 ist. Zur
Kompilierzeit wird eine Warnung ausgegeben, wenn ein Stapel verwendet wird. Eine
Fehlermeldung wird zur Ausführungszeit zurückgegeben, wenn die Tabelle, auf die verwiesen
wird, nicht vorhanden ist.
Berechtigung
Die CREATE TRIGGER-Berechtigung liegt standardmäßig beim Besitzer der Tabelle, für die
der Trigger definiert ist, oder bei Mitgliedern der festen Datenbankrollen db_owner und
db_ddladmin und ist nicht übertragbar.
Beispiele
A. Verwenden eines Triggers mit einer Erinnerungsmeldung
In diesem Beispiel wird jedes Mal, wenn jemand versucht, Daten in die titles-Tabelle
einzufügen bzw. vorhandene Daten zu ändern, eine Meldung an den Client ausgegeben.
Anmerkung Die Meldung 50009 ist eine benutzerdefinierte Meldung, die in sysmessages
gespeichert ist. Informationen zum Erstellen von benutzerdefinierten Meldungen finden Sie
unter sp_addmessage.
USE pubs
IF EXISTS (SELECT name FROM sysobjects
WHERE name = 'reminder' AND type = 'TR')
DROP TRIGGER reminder
GO
CREATE TRIGGER reminder
ON titles
FOR INSERT, UPDATE
AS RAISERROR (50009, 16, 10)
GO
B. Verwenden eines Triggers mit einer E-Mail-Erinnerungsnachricht
In diesem Beispiel wird eine E-Mail an die angegebene Person (MaryM) gesandt, wenn die
titles-Tabelle geändert wird.
USE pubs
IF EXISTS (SELECT name FROM sysobjects
WHERE name = 'reminder' AND type = 'TR')
DROP TRIGGER reminder
GO
CREATE TRIGGER reminder
ON titles
FOR INSERT, UPDATE, DELETE
AS
EXEC master..xp_sendmail 'MaryM',
'Don''t forget to print a report for the distributors.'
GO
C. Verwenden eines Geschäftsregeltriggers zwischen employee- und jobs-Tabelle
Da sich CHECK-Einschränkungen nur auf Spalten beziehen können, für die die Spalten- oder
Tabelleneinschränkung definiert wurde, müssen tabellenübergreifende Einschränkungen (in
diesem Fall Geschäftsregeln) als Trigger definiert werden.
Dieses Beispiel erstellt einen Trigger, der beim Einfügen oder Aktualisieren einer
Einstufungsebene für Angestellte überprüft, ob sich die angegebene Einstufungsebene
(job_lvls), von der das Gehalt abhängt, innerhalb des für die jeweilige Position definierten
Bereichs befindet. Um den geeigneten Bereich zu erhalten, muss ein Verweis auf die jobsTabelle angelegt werden.
USE pubs
IF EXISTS (SELECT name FROM sysobjects
WHERE name = 'reminder' AND type = 'TR')
DROP TRIGGER reminder
GO
CREATE TRIGGER employee_insupd
ON employee
FOR INSERT, UPDATE
AS
/* Get the range of level for this job type from the jobs table. */
DECLARE @min_lvl tinyint,
@max_lvl tinyint,
@emp_lvl tinyint,
@job_id smallint
SELECT @min_lvl = min_lvl,
@max_lvl = max_lvl,
@emp_lvl = i.job_lvl,
@job_id = i.job_id
FROM employee e INNER JOIN inserted i ON e.emp_id = i.emp_id
JOIN jobs j ON j.job_id = i.job_id
IF (@job_id = 1) and (@emp_lvl <> 10)
BEGIN
RAISERROR ('Job id 1 expects the default level of 10.', 16, 1)
ROLLBACK TRANSACTION
END
ELSE
IF NOT (@emp_lvl BETWEEN @min_lvl AND @max_lvl)
BEGIN
RAISERROR ('The level for job_id:%d should be between %d and %d.',
16, 1, @job_id, @min_lvl, @max_lvl)
ROLLBACK TRANSACTION
END
D. Verwenden der verzögerten Namensauflösung
Dieses Beispiel erstellt zwei Trigger, um die verzögerte Namensauflösung zu erläutern.
USE pubs
IF EXISTS (SELECT name FROM sysobjects
WHERE name = 'trig1' AND type = 'TR')
DROP TRIGGER trig1
GO
-- Creating a trigger on a nonexistent table.
CREATE TRIGGER trig1
on authors
FOR INSERT, UPDATE, DELETE
AS
SELECT a.au_lname, a.au_fname, x.info
FROM authors a INNER JOIN does_not_exist x
ON a.au_id = x.au_id
GO
-- Here is the statement to actually see the text of the trigger.
SELECT o.id, c.text
FROM sysobjects o INNER JOIN syscomments c
ON o.id = c.id
WHERE o.type = 'TR' and o.name = 'trig1'
-- Creating a trigger on an existing table, but with a nonexistent
-- column.
USE pubs
IF EXISTS (SELECT name FROM sysobjects
WHERE name = 'trig2' AND type = 'TR')
DROP TRIGGER trig2
GO
CREATE TRIGGER trig2
ON authors
FOR INSERT, UPDATE
AS
DECLARE @fax varchar(12)
SELECT @fax = phone
FROM authors
GO
-- Here is the statement to actually see the text of the trigger.
SELECT o.id, c.text
FROM sysobjects o INNER JOIN syscomments c
ON o.id = c.id
WHERE o.type = 'TR' and o.name = 'trig2'
E. Verwenden von COLUMNS_UPDATED
Dieses Beispiel erstellt zwei Tabellen: employeeData und auditEmployeeData. Die
employeeData-Tabelle, die vertrauliche Gehaltsinformationen enthält, kann von Mitgliedern
der Personalabteilung geändert werden. Wenn die Sozialversicherungsnummer (SSN), das
Jahresgehalt oder die Kontonummer des Angestellten geändert wird, wird ein
Überwachungsdatensatz erzeugt und in die auditEmployeeData-Tabelle eingefügt.
Mithilfe der COLUMNS_UPDATED()-Funktion kann schnell getestet werden, ob
Änderungen an diesen Spalten, die vertrauliche Informationen über Mitarbeiter enthalten,
vorgenommen wurden.
USE pubs
IF EXISTS(SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_NAME = 'employeeData')
DROP TABLE employeeData
IF EXISTS(SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_NAME = 'auditEmployeeData')
DROP TABLE auditEmployeeData
GO
CREATE TABLE employeeData (
emp_id
int NOT NULL,
emp_bankAccountNumber char (10) NOT NULL,
emp_salary
int NOT NULL,
emp_SSN
char (11) NOT NULL,
emp_lname
nchar (32) NOT NULL,
emp_fname
nchar (32) NOT NULL,
emp_manager
int NOT NULL
)
GO
CREATE TABLE auditEmployeeData (
audit_log_id
uniqueidentifier DEFAULT NEWID(),
audit_log_type
char (3) NOT NULL,
audit_emp_id
int NOT NULL,
audit_emp_bankAccountNumber char (10) NULL,
audit_emp_salary
int NULL,
audit_emp_SSN
char (11) NULL,
audit_user
sysname DEFAULT SUSER_SNAME(),
audit_changed
datetime DEFAULT GETDATE()
)
GO
CREATE TRIGGER updEmployeeData
ON employeeData
FOR update AS
-- Check whether columns 2, 3 or 4 has been updated. If any or all of
-- columns 2, 3 or 4 have been changed, create an audit record.
-- The bitmask is: power(2,(2-1))+power(2,(3-1))+power(2,(4-1)) = 14
-- To check if all columns 2, 3, and 4 are updated, use = 14 in place of
-- >0 (below).
IF (COLUMNS_UPDATED() & 14) > 0
-- Use IF (COLUMNS_UPDATED() & 14) = 14 to see if all of columns 2, 3,
-- and 4 are updated.
BEGIN
-- Audit OLD record.
INSERT INTO auditEmployeeData
(audit_log_type,
audit_emp_id,
audit_emp_bankAccountNumber,
audit_emp_salary,
audit_emp_SSN)
SELECT 'OLD',
del.emp_id,
del.emp_bankAccountNumber,
del.emp_salary,
del.emp_SSN
FROM deleted del
-- Audit NEW record.
INSERT INTO auditEmployeeData
(audit_log_type,
audit_emp_id,
audit_emp_bankAccountNumber,
audit_emp_salary,
audit_emp_SSN)
SELECT 'NEW',
ins.emp_id,
ins.emp_bankAccountNumber,
ins.emp_salary,
ins.emp_SSN
FROM inserted ins
END
GO
--Inserting a new employee does not cause the UPDATE trigger to fire.
INSERT INTO employeeData
VALUES ( 101, 'USA-987-01', 23000, 'R-M53550M', N'Mendel', N'Roland', 32)
GO
-- Updating the employee record for employee number 101 to change the
-- salary to 51000 causes the UPDATE trigger to fire and an audit trail
-- to be produced.
UPDATE employeeData
SET emp_salary = 51000
WHERE emp_id = 101
GO
SELECT * FROM auditEmployeeData
GO
--Updating the employee record for employee number 101 to change both the bank account number and social
security number (SSN) causes the UPDATE trigger to fire and an audit trail to be produced.
UPDATE employeeData
SET emp_bankAccountNumber = '133146A0', emp_SSN = 'R-M53550M'
WHERE emp_id = 101
GO
SELECT * FROM auditEmployeeData
GO
Siehe auch
ALTER TABLE
ALTER TRIGGER
CREATE TABLE
DROP TRIGGER
sp_depends
sp_help
sp_helptext
sp_rename
sp_spaceused
Vorgang protokollieren:
create table mittel protokoll
(pr_nr char(4) null,
benutzer char(16) null,
zeit datetime null,
mittel_alt float null,
mittel_neu float null)
create trigger mittel_aendern
on projekt
for update
as if update (mittel)
begin
declare @wert_vor float
declare @wert_danach float
declare @projekt_Nummer char(4)
select @wert_vor = (select mittel from deleted)
select @wert_danach = (select mittel from
inserted)
select @projekt_Nummer = (select pr_nr from
deleted)
insert into mittel_protokoll
values (@projekt_nummer, user_name(), getdate(),
@wert_vor, @wert_danach)
end
Annahme: FOR EACH ROW
(entspricht der Vereinfachung des allgemeinen
Falls)
Einhaltung einer Geschäftsregel:
create trigger tr_gesamt_mittel
on projekt
for update
as if update (mittel)
begin
declare @alte_summe float
declare @neue_summe float
select @alte_summe =(select sum(mittel) from
deleted)
select @neue_summe =(select sum(mittel) from
inserted)
if @neue_summe > @alte_summe * 1.5
begin
print 'Die Änderung der Projektmittel wurde
nicht ausgeführt'
rollback transaction
end
else
print 'Die Änderung der Projektmittel wurde
ausgeführt'
end
Einhaltung der referentiellen Integrität:
create trigger tr_refint_arb
on arbeiten
for insert, update
as if update (m_nr)
begin
if (select mitarbeiter.m_nr
from mitarbeiter, inserted
where mitarbeiter.m_nr = inserted.m_nr) is
null begin
print 'Das Tupel konnte nicht eingefügt/geändert
werden'
rollback transaction
end
else print 'Das Tupel wurde eingefügt/geändert'
end
Integritätsverletzungen zurücksetzen:
create trigger tr_refint_arb2
on mitarbeiter
for delete, update
as if update (m_nr)
begin
if (select count(*)
from arbeiten, deleted
where arbeiten.m_nr = deleted.m_nr) > 0
begin
rollback transaction
print 'Das Tupel konnte nicht gelöscht/geändert
werden'
end
else print 'Das Tupel wurde gelöscht/geändert'
end
INSTEAD_OF-Trigger:
create table bestellung
(bestell_nr int not null,
preis money not null,
menge int not null,
bestell_datum datetime not null,
gesamt_preis as preis * menge,
liefer_datum as dateadd (day, 7, bestell_datum))
create view alle_bestellungen
as
select bestell_nr, preis, menge, bestell_datum,
gesamt_preis, liefer_datum
from bestellung
create trigger tr_bestellung
on alle_bestellungen
instead of insert
as
begin
insert into bestellung
select bestell_nr, preis, menge, bestell_datum
from inserted
end
DDL-Trigger:
CREATE TRIGGER [ schema_name ] trigger_name
ON { ALL SERVER | DATABASE }
[ WITH { ENCRYPTION | EXECUTE AS klausel } ]
{ FOR | AFTER } { event_group | event_type }
AS { batch | EXTERNAL NAME method_name }
event_group - Name einer vordefinierten Gruppe von
Transact-SQL-Anweisungen. Der DDLTrigger wird jedes Mal aktiviert, wenn
eine der Anweisungen aus dieser Gruppe
ausgeführt wird.
event_type - Name des Ereignisses, das die
Aktivierung
des DDL-Triggers verursacht.
Beispiel:
CREATE TRIGGER prevent_drop_triggers
ON DATABASE
FOR DROP TRIGGER
AS
BEGIN
PRINT 'Sie muessen "prevent_drop_triggers"
deaktivieren,
um einen Trigger loeschen zu koennen'
ROLLBACK
END
?
Trigger und prozedurale Sprachen:
Genauso wie Funktionen und Prozeduren können Trigger mit
Hilfe prozeduraler Sprachen, die zur Common Language
Runtime (CLR) gehören, implementiert werden.
Für die Implementierung eines Triggers mit Hilfe von C# bzw.
Visual Basic 2005 sind folgende Schritte notwendig:
–
Trigger mit Hilfe von C# bzw. Visual Basic 2005
implementieren und übersetzen,
–
mittels CREATE ASSEMBLY-Anweisung eine ausführbare Version
(Executable File) des Programms erstellen,
–
den Trigger mit Hilfe der CREATE TRIGGER-Anweisung
speichern.
Beispiel mit C#:
using System;
using System.Data;
using System.Data.SqlClient;
using Microsoft.SqlServer.Server;
public class StoredProcedures {
public static void Modify Budget()
{
SqlTriggerContext context
SqlContext.TriggerContext;
if(context.IsUpdatedColumn(2))
float budget_old;
float budget_new;
string pr_nr;
SqlConnection conn
new SqlConnection("context connection=true");
conn.Open();
SqlCommand cmd conn.CreateCommand();
cmd.CommandText "SELECT mittel FROM deleted";
budget_old =
(float)Convert.ToDouble(cmd.ExecuteScalar());
cmd.CommandText="SELECT mittel FROM inserted";
budget_new =
(float)Convert.ToDouble(cmd.ExecuteScalar());
cmd.CommandText = "SELECT pr_nr FROM deleted";
pr_nr = Convert.ToString(cmd.ExecuteScalar());
cmd.CommandText =
"INSERT INTO mittel protokoll
VALUES(@pr_nr, USER_NAME(), GETDATE(),
@budget_old, @budget_new)";
cmd.Parameters.AddWithValue("@pr_nr",pr_nr);
cmd.Parameters.AddWithValue("@budget_old",budget_old)
;
cmd.Parameters.AddWithValue("@budget_new",budget_new)
;
cmd.ExecuteNonQuery();
C#-Programm übersetzen:
csc /target:library C#_Beispiel.cs
/reference:"c:\Program Files\Microsoft SQL Server\
MSSQL.1\MSSQL\Binn\sqlaccess.dll"
Herunterladen