2012-04-09 9 views
10

In MS SQL Server 2008 R2 möchten wir einen Trigger vor dem Einfügen und vor der Aktualisierung, die etwas überprüft und ermöglicht oder Rollbacks (über raiserror) die laufende Einfügung/Aktualisierung.Rollback-Transaktion von Trigger

Frage: In INSTEAD OF auslösen. Muss man das Insert oder Update wirklich explizit schreiben? Weil wir wollen, dass die Standardeinfügung oder Aktualisierung durchgeführt wird und nur die "Vorprüfung" durchführt.

+0

Was ist die Natur der „precheck“? Trigger haben einen Overhead, der die "eingefügten"/"gelöschten" Tabellen verwaltet. Ist es etwas, das anders durchgesetzt werden kann? –

+0

Alles, was wir brauchen, ist eine eindeutige Beschränkung für Tripel (3 Spalten), die leere Strings in einer Spalte ignorieren. – Cartesius00

+0

Also für Tupel '(a, b, c)' wenn 'c' einen Wert für eine leere Zeichenkette hat, wollen Sie dieses Tupel für die Zwecke der einzigartigen Einschränkung vollständig ignorieren? –

Antwort

9

Ja.

Sie müssen die explizite INSERT oder UPDATE schreiben.

Der Trigger läuft INSTEAD OF die DML-Operation. Wenn Sie den Auslöser leer lassen, wird keine andere Aktion ausgeführt als die /DELETED Tabellen, die in tempdb erstellt und ausgefüllt werden.

Obwohl von Diskussion in den Kommentaren würde ich keinen Auslöser für diese überhaupt verwenden, aber einen eindeutigen gefilterten Index CREATE UNIQUE INDEX ix ON T(a,b,c) WHERE c <> '' verwenden. Dies ist wahrscheinlich leistungsfähiger und vermeidet mögliche logische Probleme im Umgang mit Nebenläufigkeit.

+0

Danke. Und was sind die Aliase für eingefügte und aktualisierte Zeilen in diesen DML-Operationen? – Cartesius00

+0

@James - 'INSERTED' und' DELETED', aber diese Tabellen sind keine Zeilen. Der Trigger wird einmal pro Anweisung ausgelöst. 'INSERT INTO YourTable SELECT * FROM INSERTED' wäre also ein typischer 'INSTEAD OF INSERT'-Trigger. –

+0

Und was, wenn die 'YourTable' Identitätsspalte und viele andere Spalten enthält. 'SELECT *' ist dann ein Problem, oder? – Cartesius00

3

Sie möchten wahrscheinlich keinen INSTEAD OF Trigger, außer Sie möchten die tatsächliche Einfügung oder Aktualisierung ersetzen. In Ihrem Fall möchten Sie stattdessen einen FOR INSERT, UPDATE Auslöser.

Dieser Beispieltrigger gibt eine Nachricht an den Client aus, wenn jemand versucht, Daten in der titles-Tabelle hinzuzufügen oder zu ändern.

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 ('inserts and updates to the titles table is not allowed', 16, 1) 
GO 

Sie können auch Dinge wie IF EXISTS oder COLUMNS_UPDATED auch verwenden.

Hier ein weiteres Beispiel, das Rollback verwendet.

USE pubs 
IF EXISTS (SELECT name FROM sysobjects 
     WHERE name = 'employee_insupd' AND type = 'TR') 
    DROP TRIGGER employee_insupd 
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 

Ich bin mir nicht sicher, ob Sie eine Transaktion haben oder nicht, aber in Ihrem Fall würden Sie so etwas wie die folgenden wollen:

USE myDatabase 

    IF EXISTS (SELECT name FROM sysobjects 
      WHERE name = 'myTable' AND type = 'TR') 
     DROP TRIGGER tr_myTrigger 
    GO 
    CREATE TRIGGER tr_myTrigger 
    ON myTable 
    FOR INSERT, UPDATE 
    AS 

if(exists(select * from inserted where rtrim(c) <> '')) 
begin 
    -- check to make sure the insert(s) are unique 

    if(exists(
     select * from inserted i 
     join dbo.myTable t on i.a = t.a and i.b = t.b and i.c = t.c) 

    begin 
     raiserror('Duplicate(s) found', 16, 1) 
     rollback transaction 
    end 
end