2008-10-06 10 views
32

Gibt es in SQL Server 2005 eine Möglichkeit für einen Trigger, um herauszufinden, welches Objekt für das Auslösen des Triggers verantwortlich ist? Ich möchte dies verwenden, um den Trigger für eine gespeicherte Prozedur zu deaktivieren.Gibt es eine Möglichkeit, einen SQL Server-Trigger nur für einen bestimmten Ausführungsumfang zu deaktivieren?

Gibt es eine andere Möglichkeit, den Trigger nur für die aktuelle Transaktion zu deaktivieren? Ich könnte den folgenden Code verwenden, aber wenn ich mich nicht irre, würde es auch gleichzeitige Transaktionen beeinflussen - was eine schlechte Sache wäre.

DISABLE TRIGGER { [ schema_name . ] trigger_name [ ,...n ] | ALL } ON { object_name | DATABASE | ALL SERVER } [ ; ] 

ENABLE TRIGGER { [ schema_name . ] trigger_name [ ,...n ] | ALL } ON { object_name | DATABASE | ALL SERVER } [ ; ] 

Wenn möglich, würde ich gerne die Technik mit einem „NoTrigger“ Feld in meinem Tisch zu vermeiden und ein NoTrigger = null tun, weil ich den Tisch so klein wie möglich halten mag.

Der Grund, warum ich den Trigger vermeiden möchte, ist, weil er Logik enthält, die für manuelle Aktualisierungen der Tabelle wichtig ist, aber meine gespeicherte Prozedur wird sich um diese Logik kümmern. Weil dies eine sehr gebräuchliche Prozedur sein wird, möchte ich, dass es schnell ist.

Auslöser verursachen zusätzlichen Aufwand für den Server, weil sie eine implizite Transaktion initiieren. Sobald ein Trigger ausgeführt wird, wird eine neue implizite Transaktion gestartet, und jeder Datenabruf innerhalb einer Transaktion enthält Sperren für betroffene Tabellen.

Von: http://searchsqlserver.techtarget.com/tip/1,289483,sid87_gci1170220,00.html#trigger

+1

Sie können ** Exec ** -Funktion verwenden, um Trigger aus einer gespeicherten Prozedur zu deaktivieren und zu aktivieren. Beispiel: 'EXEC ('ENABLE TRIGGER dbo.TriggerName auf dbo.TriggeredTable')' –

Antwort

52

ich diesen Artikel gerade gesehen hervorgehoben kürzlich auf dem SQL-Server-Zentral Newsletter und es scheint eine Möglichkeit zu bieten, die Ihnen die CONTEXT_INFO nützliche Verwendung auf der Verbindung finden können:

http://www.mssqltips.com/tip.asp?tip=1591


EDIT durch dosen:

die obige Verbindung enthält den folgenden Code:

USE AdventureWorks; 
GO 
-- creating the table in AdventureWorks database 
IF OBJECT_ID('dbo.Table1') IS NOT NULL 
DROP TABLE dbo.Table1 
GO 
CREATE TABLE dbo.Table1(ID INT) 
GO 
-- Creating a trigger 
CREATE TRIGGER TR_Test ON dbo.Table1 FOR INSERT,UPDATE,DELETE 
AS 
DECLARE @Cinfo VARBINARY(128) 
SELECT @Cinfo = Context_Info() 
IF @Cinfo = 0x55555 
RETURN 
PRINT 'Trigger Executed' 
-- Actual code goes here 
-- For simplicity, I did not include any code 
GO 

Wenn Sie von den Trigger zu verhindern, ausgeführt werden Sie folgendes tun:

SET Context_Info 0x55555 
INSERT dbo.Table1 VALUES(100) 
+1

Ich wünschte wirklich, es gäbe einen besseren Weg für Session (?) Variablen/Zustand: Was passiert, wenn 'Context_Info' bereits für andere Zwecke verwendet wird? (zB Implementierung von Mandantenfähigkeit.) :( –

+1

@pst Nun, Sie haben 128 Bytes von binären Daten, so ist es möglich, eine ganze Datenstruktur dort für verschiedene Zwecke und umschließen sie mit Zugriffsfunktionen. –

+0

Ugh, fühlt sich so schmutzig! –

4

ALTER TABLE Tabl DISABLE TRIGGER trg

http://doc.ddart.net/mssql/sql70/aa-az_5.htm

Ich verstehe nicht, die Bedeutung Ihrer ersten Absatz obwohl

+0

Mein erster Absatz - In meinem Trigger gibt es eine Funktion, die ich verwenden kann, um den Namen des gespeicherten Proc zu erhalten, der meinen Auslöser ausgelöst hat? So ähnlich wie 'if (trigger_nestlevel (object_id ('NameOfTrigger')) = 0)' um sicherzustellen, dass der Trigger sich nicht selbst aufruft – Seibar

1

nicht den Auslöser deaktivieren Sie. Sie haben Recht, das für alle gleichzeitigen Transaktionen deaktiviert wird.

Warum möchten Sie den Trigger deaktivieren? Was tut es? WARUM ist der Auslöser ein Problem? Es ist normalerweise eine schlechte Idee, einen Tigger aus der Perspektive der Datenintegrität zu deaktivieren.

6

Wenn Ihr Auslöser in Ihrer Anwendung zu Leistungsproblemen führt, sollten Sie alle manuellen Aktualisierungen der Tabelle entfernen und alle Aktualisierungen benötigen, um die gespeicherten Prozeduren einzufügen/zu aktualisieren, die die richtige Aktualisierungslogik enthalten. Dann können Sie den Auslöser vollständig entfernen.

Ich schlage vor, Tischupdate-Berechtigungen zu verweigern, wenn nichts anderes funktioniert.

Dies löst auch das Problem von doppeltem Code. Das Duplizieren von Code im Update-SP und im Trigger ist ein Verstoß gegen gute Software-Engineering-Prinzipien und wird ein Wartungsproblem darstellen.

+0

Stimme überhaupt nicht zu. Es gibt viele Möglichkeiten, Daten zu ändern und Geschäftsregeln auf Datenbankebene zu erzwingen. Wenn der Trigger schlecht funktioniert, sollte er neu geschrieben, aber nie entfernt oder deaktiviert werden. Sie können mit Ihrem Ansatz sehr schlechte Datenintegritätsprobleme erstellen. – HLGEM

+0

Die gespeicherte Prozedur behält bereits die Datenintegrität bei, und der Zugriff auf die Tabelle kann eingeschränkt werden, sodass jeder Prozess, der sie aktualisieren möchte, den Prozess verwenden muss. Wenn Sie den Zugriff auf die Tabelle auf viele Arten zulassen, ist der Auslöser nur ein Symptom für das Problem, nicht die Lösung. –

+0

Wenn es möglich ist zu tun, wie Jeffrey vorschlägt, wäre das die beste Lösung. –

2

Da Sie angeben, dass der Trigger Logik enthält, um alle Aktualisierungen zu behandeln, auch manuelle Aktualisierungen, sollte dies der Ort sein, an dem sich die Logik befindet. Das von Ihnen erwähnte Beispiel, bei dem eine gespeicherte Prozedur "sich um diese Logik kümmert", impliziert doppelten Code. Wenn Sie sicherstellen möchten, dass jede UPDATE-Anweisung diese Logik unabhängig vom Autor angewendet hat, ist der Trigger dafür der richtige Ort. Was passiert, wenn jemand eine Prozedur verfasst, aber vergisst, die Logik noch einmal zu duplizieren? Was passiert, wenn es an der Zeit ist, die Logik zu ändern?

1

Erwägen Sie, den Trigger neu zu schreiben, um die Leistung zu verbessern, wenn Leistung das Problem ist.

0

Ich stimme mit einigen anderen Antworten überein. Deaktivieren Sie den Auslöser nicht.

Das ist reine Meinung, aber ich vermeide Auslöser wie die Pest. Ich habe sehr wenige Fälle gefunden, in denen ein Trigger zum Erzwingen von Datenbankregeln verwendet wurde. Nach meiner Erfahrung gibt es offensichtliche Randfälle, und ich habe nur meine Erfahrung, um diese Aussage zu machen. Ich habe in der Regel Trigger zum Einfügen einiger relationaler Daten (die aus der Geschäftslogik stammen sollten) gesehen, um Daten in eine Berichtstabelle einzufügen, dh die Daten zu denormalisieren (was mit einem Prozess außerhalb der Transaktion geschehen kann) oder um die Daten zu transformieren irgendwie.

Es gibt legitime Verwendungen für Trigger, aber ich denke, dass sie im alltäglichen Geschäfts-Programmieren sehr selten sind. Dies hilft möglicherweise nicht bei Ihrem aktuellen Problem, aber Sie könnten in Erwägung ziehen, den Auslöser vollständig zu entfernen und die Arbeit, die der Auslöser auf eine andere Weise ausführt, auszuführen.

+0

"DDL-Anweisungen werden nicht im Kontext einer Transaktion ausgeführt?" Sie können versuchen und sehen, dass dies falsch ist. Übrigens, Red Gates SQL Compare kann ein Deployment-Skript generieren, das alle als eine Transaktion ausgeführt wird. –

+0

Sie haben Recht. Ich denke, meine Zeit im Oracle-Land muss mich vergiftet haben. –

1

Ich waffelte ein wenig auf diesem. Auf der einen Seite bin ich sehr anti-triggern, vor allem, weil es ein weiterer Ort für mich ist, nach Code zu suchen, der gegen meinen Tisch ausgeführt wird, zusätzlich zu den Gründen, die in dem Artikel, der in dem Fragepost verlinkt ist, angegeben sind.

Auf der anderen Seite, wenn Sie Logik haben, um stabile und unveränderliche Geschäftsregeln oder tabellarische Aktionen (wie die Pflege einer Protokolltabelle) zu erzwingen, dann wäre es sicherer, dies in einen Auslöser zu bringen, so dass Autoren und Programmierer t müssen damit umgehen - es funktioniert einfach.

Also, meine Empfehlung ist es, die notwendige Logik in Ihrem Trigger statt in diesem einen Proc, die unvermeidlich zu mehreren Procs mit der gleichen Ausnahme wachsen zu setzen.

0

ich gerade das gleiche Problem konfrontiert und kam mit der folgenden Lösung auf, was für mich funktioniert.

  1. Erstellen Sie eine permanente DB-Tabelle, die einen Datensatz für jeden Trigger enthält, den Sie deaktivieren möchten (z. B. refTriggerManager); Jede Zeile enthält den Namen des Auslösers (z. B. strTriggerName = 'myTrigger') und ein Bit-Flag (z. B. blnDisabled, standardmäßig 0).

  2. Am Anfang des Trigger-Körpers, strTriggerName = 'myTrigger' in refTriggerManager nachschlagen. Wenn blnDisabled = 1 ist, kehren Sie zurück, ohne den Rest des Triggercodes auszuführen, andernfalls setzen Sie den Triggercode bis zum Abschluss fort.

  3. In der gespeicherten proc, in dem Sie den Auslöser deaktivieren möchten, gehen Sie wie folgt vor:


BEGIN TRANSACTION

UPDATE refTriggerManager SET blnDisabled = 1 WHERE strTriggerName = 'MyTrigger'

/* AKTUALISIEREN Sie die Tabelle, die 'myTrigger' besitzt, aber die Sie deaktivieren möchten. Da refTriggerManager.blnDisabled = 1 ist, kehrt 'myTrigger' zurück, ohne seinen Code auszuführen. */

UPDATE refTriggerManager SET blnDisabled = 0 WHERE triggerName =

'MyTrigger'

/* Optional UPDATE-Code, der Trigger auslöst. Seit refTriggerManager.blnDisabled = 0 wird 'myTrigger' vollständig ausgeführt. */

COMMIT TRANSACTION


All dies innerhalb einer Transaktion erfolgt, ist es so von der Außenwelt isoliert und wird nicht anderes Updates auf dem Zieltabelle auswirken.

Hat jemand ein Problem mit diesem Ansatz?

Bill

+3

Dies sieht nach 'READ COMMITTED SNAPSHOT' aus, und im Standard' READ COMMITTED' blockiert es jede andere DML-Transaktion bis zum Ende, wodurch die effektive Verwendung von Sperren auf Zeilenebene verhindert wird. Im Grunde haben Sie eine verkrüppelte Version der angenommenen Antwort implementiert. Wenn das nicht eine so alte Frage wäre, würde ich Ihnen wahrscheinlich -1 wegen der schrecklichen ungarischen Schreibweise und der fehlenden Formatierung geben. – Aaronaught

2

Nicht sicher, ob dies eine gute Idee ist, aber es scheint, für mich zu arbeiten. Die Transaktion sollte Einfügungen in die Tabelle von anderen Prozessen verhindern, während der Trigger deaktiviert ist.

IF OBJECT_ID('dbo.TriggerTest') IS NOT NULL 
DROP PROCEDURE dbo.TriggerTest 
GO 

CREATE PROCEDURE [dbo].[TriggerTest] 
AS 
BEGIN TRANSACTION trnInsertTable1s 
; 
DISABLE TRIGGER trg_tblTable1_IU ON tblTable1 
; 
BEGIN -- Procedure Code 
    PRINT '@@trancount' 
    PRINT @@TRANCOUNT 
    -- Do Stuff 

END -- Procedure Code 
; 
ENABLE TRIGGER trg_tblTable1_IU ON tblTable1 

IF @@ERROR <> 0 ROLLBACK TRANSACTION 
ELSE COMMIT TRANSACTION 
+0

Das ist eine seltsame Möglichkeit, einen vorhandenen Sproc zu erstellen oder zu aktualisieren (es gibt eine nicht genehmigte Bearbeitung, um sie in einen einfachen Drop/Create zu ändern und Ihren Blockkopiefehler in DROP PROCEDURE zu beheben).Hat es einen Vorteil, es so zu machen? Vielen Dank! – Rup

+0

Das wird nur als ein Add/Update verwendet. Wenn Sie die Prozedur löschen, können Sie sicher sein, dass "CREATE PROCEDURE" funktioniert. Ich habe dies beim Speichern gespeicherter Prozeduren in der Quellcodeverwaltung verwendet. Jede .sql-Datei ist ein Skript, das, sofern vorhanden, und dann die aktuelle gespeicherte Prozedur löscht. Es ist nicht relevant für die Antwort. If/Else könnte auch verwendet werden, aber der Inhalt der gespeicherten Prozedur müsste dupliziert werden. – Steve

+0

Sicher, ich habe es gerade wegen der Bearbeitung bemerkt. Ja, wir haben im Allgemeinen "wenn die Prozedur existiert, fallen lassen, erstellen" - ich habe es noch nicht gesehen "drop, create as 'select 1', update" vorher. – Rup

0

Sie verwenden können 'Exec' Funktion Auslöser von einer gespeicherten Prozedur diable und zu aktivieren. Beispiel: EXEC ('ENABLE TRIGGER dbo.TriggerName on dbo.TriggeredTable')

Verwandte Themen