Beim Einrichten eines Prüfpfads habe ich kein Problem damit, zu verfolgen, wer Datensätze aktualisiert oder in eine Tabelle einfügt. Das Verfolgen, wer Datensätze löscht, scheint jedoch problematischer.
Ich kann Einfügungen / Aktualisierungen verfolgen, indem ich in das Feld Einfügen / Aktualisieren das Feld "UpdatedBy" einfüge. Dadurch kann der INSERT / UPDATE-Trigger über auf das Feld "UpdatedBy" zugreifen inserted.UpdatedBy
. Mit dem Delete-Trigger werden jedoch keine Daten eingefügt / aktualisiert. Gibt es eine Möglichkeit, Informationen an den Trigger "Löschen" zu übergeben, damit dieser weiß, wer den Datensatz gelöscht hat?
Hier ist ein Einfüge- / Aktualisierungsauslöser
ALTER TRIGGER [dbo].[trg_MyTable_InsertUpdate]
ON [dbo].[MyTable]
FOR INSERT, UPDATE
AS
INSERT INTO AuditTable (IdOfRecordedAffected, UserWhoMadeChanges)
VALUES (inserted.ID, inserted.LastUpdatedBy)
FROM inserted
Verwenden von SQL Server 2012
sql-server
sql-server-2012
trigger
audit
Webworm
quelle
quelle
SUSER_SNAME()
ist der Schlüssel, um herauszufinden, wer den Datensatz gelöscht hat.SUSER_SNAME()
dies in einer Situation wie einer Webanwendung funktionieren würde, in der ein einzelner Benutzer für die Datenbankkommunikation für die gesamte Anwendung verwendet werden könnte.Antworten:
Ja, indem Sie eine sehr coole (und nicht ausreichend genutzte) Funktion verwenden
CONTEXT_INFO
. Es ist im Wesentlichen der Sitzungsspeicher, der in allen Bereichen vorhanden ist und nicht an Transaktionen gebunden ist. Es kann verwendet werden, um Informationen (alle Informationen - also alle, die in den begrenzten Raum passen) an Trigger sowie zwischen Sub-Proc / EXEC-Aufrufen hin und her zu übergeben. Und ich habe es schon einmal für genau dieselbe Situation verwendet.Kontextinformationen sind ein VARBINARY (128)
Set via: SET CONTEXT_INFO
Erhalten Sie über: CONTEXT_INFO ()
Testen Sie Folgendes, um zu sehen, wie es funktioniert. Beachten Sie, dass ich
CHAR(128)
vor dem konvertiereCONVERT(VARBINARY(128), ..
. Dies dient dazu, das Auffüllen von Leerzeichen zu erzwingen, um das Zurückkonvertieren zu erleichtern,VARCHAR
wenn es herausgenommen wird,CONTEXT_INFO()
daVARBINARY(128)
es mit0x00
s rechts aufgefüllt ist .Ergebnisse:
Alles zusammenfügen:
Die App sollte eine gespeicherte Prozedur "Löschen" aufrufen, die den Benutzernamen (oder was auch immer) übergibt, der den Datensatz löscht. Ich gehe davon aus, dass dies bereits das verwendete Modell ist, da es so klingt, als würden Sie bereits Einfüge- und Aktualisierungsvorgänge verfolgen.
Die gespeicherte Prozedur "Löschen" führt Folgendes aus:
Der Audit-Trigger führt Folgendes aus:
Bitte beachten Sie, dass, wie @SeanGallardy in einem Kommentar hervorhob, aufgrund anderer Verfahren und / oder Ad-hoc-Abfragen beim Löschen von Datensätzen aus dieser Tabelle Folgendes möglich ist:
CONTEXT_INFO
wurde nicht eingestellt und ist nochNULL
:Aus diesem Grund habe ich das oben Gesagte aktualisiert
INSERT INTO AuditTable
, umCOALESCE
den Wert als Standardwert zu verwenden. Wenn Sie keine Standardeinstellung wünschen und einen Namen benötigen, können Sie Folgendes tun:CONTEXT_INFO
wurde auf einen Wert festgelegt, der kein gültiger Benutzername ist und daher möglicherweise die Größe desAuditTable.[UserWhoMadeChanges]
Felds überschreitet :Aus diesem Grund habe ich eine
LEFT
Funktion hinzugefügt , um sicherzustellen, dass alles, woraus herausgegriffenCONTEXT_INFO
wird, nicht kaputt gehtINSERT
. Wie im Code angegeben, müssen Sie nur50
die tatsächliche Größe desUserWhoMadeChanges
Felds festlegen .UPDATE FÜR SQL SERVER 2016 UND NEUER
SQL Server 2016 hat eine verbesserte Version dieses Sitzungsspeichers hinzugefügt: Sitzungskontext. Der neue Sitzungskontext ist im Wesentlichen eine Hash-Tabelle von Schlüssel-Wert-Paaren, wobei der "Schlüssel" vom Typ
sysname
(dhNVARCHAR(128)
) und der "Wert" istSQL_VARIANT
. Bedeutung:CONTEXT_INFO()
(Details finden Sie in meinem Beitrag: Warum gibt CONTEXT_INFO () den von SET CONTEXT_INFO festgelegten genauen Wert nicht zurück? )CONTEXT_INFO
)Weitere Informationen finden Sie auf den folgenden Dokumentationsseiten:
quelle
@@SPID
. Dies ist der PER-Session / Connection-Speicher. Eine Sitzung kann die Kontextinformationen einer anderen Sitzung nicht überschreiben. Und wenn sich die Sitzung abmeldet, verschwindet der Wert. Es gibt kein "zuvor festgelegtes Element".Dies ist nur möglich, wenn Sie die SQL Server-Benutzer-ID anstelle einer Anwendungsebene 1 aufzeichnen möchten.
Sie können einen Soft-Löschvorgang durchführen, indem Sie eine Spalte mit dem Namen DeletedBy verwenden und diese nach Bedarf festlegen. Anschließend kann Ihr Update-Trigger den eigentlichen Löschvorgang (oder die Archivierung des Datensatzes, ich vermeide im Allgemeinen harte Löschvorgänge, soweit dies möglich und legal ist) sowie die Aktualisierung Ihres Prüfpfads durchführen . Um das Löschen auf diese Weise zu erzwingen, definieren Sie einen
on delete
Trigger, der einen Fehler auslöst. Wenn Sie Ihrer physischen Tabelle keine Spalte hinzufügen möchten, können Sie eine Ansicht definieren, in der die Spalte hinzugefügt undinstead of
Trigger für die Aktualisierung der Basistabelle definiert werden. Dies kann jedoch zu viel des Guten sein.quelle
SPARSE
Spalte zu sein?Ja, anscheinend gibt es zwei Möglichkeiten ;-). Wenn es irgendwelche Vorbehalte gegen die Verwendung gibt,
CONTEXT_INFO
wie ich in meiner anderen Antwort hier vorgeschlagen habe , habe ich mir nur einen anderen Weg überlegt, der eine sauberere funktionale Trennung von anderen Codes / Prozessen aufweist: Verwenden Sie eine lokale temporäre Tabelle.Der temporäre Tabellenname sollte den Tabellennamen enthalten, aus dem gelöscht wird, da dies dazu beiträgt, ihn von jedem anderen Code zu trennen, der möglicherweise in derselben Sitzung ausgeführt wird. Etwas in der Art von:
#<TableName>DeleteAudit
Ein Vorteil einer lokalen temporären Tabelle
CONTEXT_INFO
besteht darin, dass der Unterprozess a) einen neuen lokalen Namen erstellt, wenn jemand in einem anderen Prozess - der irgendwie von diesem bestimmten "Löschen" -Prozess aufgerufen wird - zufällig denselben temporären Tabellennamen falsch verwendet temporäre Tabelle des angeforderten Namens, die von dieser anfänglichen temporären Tabelle getrennt wird (obwohl sie denselben Namen hat), und b) DML-Anweisungen für die neue lokale temporäre Tabelle im Unterprozess wirken sich nicht auf Daten in der aus Lokale temporäre Tabelle, die hier im übergeordneten Prozess erstellt wurde, daher kein Überschreiben von Daten. Natürlich, wenn eine Subprozess Frage eine DML - Anweisung gegen diesen Namen temporären Tabelle , ohne zuerst eine Ausgabe Tabelle des gleichen Namen erstellen, dann wird diese DML - Anweisungen werden die Daten in dieser Tabelle beeinflussen. ABER an diesem Punkt bekommen wir wirklichEdge-Casey hier, noch mehr als mit der Wahrscheinlichkeit überlappender Verwendungen vonCONTEXT_INFO
(ja, ich weiß, dass es passiert ist, weshalb ich eher "Edge-Case" als "es wird nie passieren" sage).Die App sollte eine gespeicherte Prozedur "Löschen" aufrufen, die den Benutzernamen (oder was auch immer) übergibt, der den Datensatz löscht. Ich gehe davon aus, dass dies bereits das verwendete Modell ist, da es so klingt, als würden Sie bereits Einfüge- und Aktualisierungsvorgänge verfolgen.
Die gespeicherte Prozedur "Löschen" führt Folgendes aus:
Der Audit-Trigger führt Folgendes aus:
Ich habe diesen Code in einem Trigger getestet und er funktioniert wie erwartet.
quelle