Mein Chef hatte gestern eine Anfrage von einem Kunden, wie er herausfinden könne, wer einige Daten in seiner SQL Server-Datenbank gelöscht hat (es ist die Express-Edition, wenn dies wichtig ist).
Ich dachte, dies könnte aus dem Transaktionsprotokoll gefunden werden (vorausgesetzt, es wurde nicht abgeschnitten) - ist das richtig? Und wenn ja, wie gehen Sie tatsächlich vor, um diese Informationen herauszufinden?
Ich habe fn_dblog in Express nicht ausprobiert, aber wenn es verfügbar ist, erhalten Sie folgende Löschvorgänge:
SELECT
*
FROM
fn_dblog(NULL, NULL)
WHERE
Operation = 'LOP_DELETE_ROWS'
Nehmen Sie die Transaktions-ID für Transaktionen, an denen Sie interessiert sind, und identifizieren Sie die SID, die die Transaktion initiiert hat, mit:
SELECT
[Transaction SID]
FROM
fn_dblog(NULL, NULL)
WHERE
[Transaction ID] = @TranID
AND
[Operation] = 'LOP_BEGIN_XACT'
Identifizieren Sie dann den Benutzer anhand der SID:
SELECT
*
FROM
sysusers
WHERE
[sid] = @SID
Bearbeiten: Bringen Sie alles zusammen, um Löschvorgänge in einer angegebenen Tabelle zu finden:
DECLARE @TableName sysname
SET @TableName = 'dbo.Table_1'
SELECT
u.[name] AS UserName
, l.[Begin Time] AS TransactionStartTime
FROM
fn_dblog(NULL, NULL) l
INNER JOIN
(
SELECT
[Transaction ID]
FROM
fn_dblog(NULL, NULL)
WHERE
AllocUnitName LIKE @TableName + '%'
AND
Operation = 'LOP_DELETE_ROWS'
) deletes
ON deletes.[Transaction ID] = l.[Transaction ID]
INNER JOIN
sysusers u
ON u.[sid] = l.[Transaction SID]
Wenn sich die Datenbank im vollständigen Wiederherstellungsmodus befindet oder wenn Sie Transaktionsprotokollsicherungen haben, können Sie versuchen, diese mit Protokolllesern von Drittanbietern zu lesen.
Sie können versuchen ApexSQL Log (Premium, hat aber eine kostenlose Testversion) oder SQL Log Rescue (kostenlos, aber nur SQL 2000).
wie sie herausfinden konnten, wer einige Daten in ihrer SQL Server-Datenbank gelöscht hat
Obwohl dies beantwortet wird, wollte ich hinzufügen, dass in SQL Server eine Standardablaufverfolgung aktiviert ist und damit ermittelt werden kann, wer die Objekte gelöscht/geändert hat.
Objektereignisse
Zu den Objektereignissen gehören: Objekt geändert, Objekt erstellt und Objekt gelöscht
Hinweis: SQL Server verfügt standardmäßig über 5 Tracedateien mit jeweils 20 MB, und es ist keine unterstützte Methode zum Ändern bekannt. Wenn Sie ein ausgelastetes System haben, werden die Tracedateien möglicherweise viel zu schnell (sogar innerhalb von Stunden) übertragen, und Sie können möglicherweise einige der Änderungen nicht erfassen.
Ein hervorragendes Beispiel finden Sie hier: Die Standardablaufverfolgung in SQL Server - Leistungsfähigkeit und Sicherheitsüberwachung
Sie können dieses Verfahren ausprobieren, um die Protokollsicherungsdateien abzufragen und festzustellen, in welchen Protokollsicherungsdateien ein bestimmter Wert einer Spalte einer Tabelle noch vorhanden/zuletzt vorhanden war.
Um den Benutzer zu finden, können Sie, nachdem Sie in welcher Protokollsicherung den zuletzt vorhandenen Wert gefunden haben, eine Datenbank bis zu dieser Protokollsicherung wiederherstellen und dann der Antwort von Mark Storey-Smith folgen.
Einige Voraussetzungen
Haftungsausschluss
Diese Lösung ist alles andere als wasserdicht und es muss noch viel mehr Arbeit investiert werden.
Es wurde nicht in großen Umgebungen oder sogar in anderen Umgebungen getestet, abgesehen von einigen kleinen Tests. Der aktuelle Lauf war auf SQL Server 2017.
Sie können unten procedure from Muhammad Imran verwenden, das ich geändert habe, um mit dem Inhalt von Protokollsicherungen zu arbeiten anstelle des Inhalts des Protokolls einer Live-Datenbank.
Auf diese Weise führen Sie technisch gesehen keine Wiederherstellungen durch, sondern sichern den Protokollinhalt in einer temporären Tabelle. Es wird wahrscheinlich immer noch langsam sein und ist sehr offen für Fehler und Probleme. Aber theoretisch könnte es funktionieren.
Die gespeicherte Prozedur verwendet das undokumentierte fn_dump_dblog
Funktion zum Auslesen der Protokolldateien.
Testumgebung
Betrachten Sie diese Datenbank, in der wir einige Zeilen einfügen, zwei Protokollsicherungen durchführen und bei der dritten Protokollsicherung alle Zeilen löschen.
CREATE DATABASE WrongDeletesDatabase
GO
USE WrongDeletesDatabase
GO
BACKUP DATABASE WrongDeletesDatabase TO DISK ='c:\temp\Full.bak'
ALTER DATABASE WrongDeletesDatabase SET RECOVERY FULL
GO
CREATE TABLE dbo.WrongDeletes(ID INT, val varchar(255))
INSERT INTO dbo.WrongDeletes(ID,val)
VALUES (1,'value1')
GO
BACKUP LOG WrongDeletesDatabase TO DISK = 'c:\temp\Logs\log1.trn'
GO
INSERT INTO dbo.WrongDeletes(ID,val)
VALUES (2,'value2')
GO
BACKUP LOG WrongDeletesDatabase TO DISK = 'c:\temp\Logs\log2.trn'
GO
DELETE FROM dbo.WrongDeletes
GO
BACKUP LOG WrongDeletesDatabase TO DISK = 'c:\temp\Logs\log3.trn'
GO
INSERT INTO dbo.WrongDeletes(ID,val)
VALUES (3,'value3')
GO
BACKUP LOG WrongDeletesDatabase TO DISK = 'c:\temp\Logs\log4.trn'
GO
Die Prozedur
Sie können die gespeicherte Prozedur finden und herunterladen hier .
Ich konnte es hier nicht hinzufügen, da es größer als das Zeichenlimit ist und diese Antwort noch weniger klar machen würde als es ist.
Abgesehen davon sollten Sie in der Lage sein, die Prozedur auszuführen.
Ausführen der Prozedur
Ein Beispiel dafür, wenn ich alle meine Protokolldateien hinzufüge (4
) zur gespeicherten Prozedur und führen Sie die Prozedur aus, die nach Wert1 sucht
EXEC dbo.Recover_Deleted_Data_Proc @Database_Name= 'WrongDeletesDatabase',
@SchemaName_n_TableName= 'dbo.WrongDeletes',
@SearchString = 'value1',
@SearchColumn = 'val',
@LogBackupFolder ='C:\temp\Logs\'
Das bringt mich:
ID val LogFileName
1 value1 c:\temp\Logs\log3.trn
1 value1 c:\temp\Logs\log1.trn
Wo können wir finden, wann das letzte Mal eine Operation auf value1
passiert, das Löschen in log3.trn
.
Weitere Testdaten, Hinzufügen einer Tabelle mit verschiedenen Spalten
CREATE TABLE dbo.WrongDeletes2(Wow varchar(255), Anotherval varchar(255),Val3 int)
INSERT INTO dbo.WrongDeletes(ID,val)
VALUES (1,'value1')
INSERT INTO dbo.WrongDeletes2(wOw,Anotherval,Val3)
VALUES ('b','value1',1)
GO
BACKUP LOG WrongDeletesDatabase TO DISK = 'c:\temp\Logs\log1_1.trn'
GO
INSERT INTO dbo.WrongDeletes(ID,val)
VALUES (2,'value2')
INSERT INTO dbo.WrongDeletes2(wOw,Anotherval,Val3)
VALUES ('c','value2',2)
GO
BACKUP LOG WrongDeletesDatabase TO DISK = 'c:\temp\Logs\log2_1.trn'
GO
DELETE FROM dbo.WrongDeletes
DELETE FROM dbo.WrongDeletes2
GO
BACKUP LOG WrongDeletesDatabase TO DISK = 'c:\temp\Logs\log3_1.trn'
GO
INSERT INTO dbo.WrongDeletes(ID,val)
VALUES (3,'value3')
INSERT INTO dbo.WrongDeletes2(wOw,Anotherval,Val3)
VALUES ('d','value3',3)
GO
BACKUP LOG WrongDeletesDatabase TO DISK = 'c:\temp\Logs\log4_1.trn'
GO
Ändern der Protokolldateinamen und erneutes Ausführen der Prozedur
EXEC dbo.Recover_Deleted_Data_Proc @Database_Name= 'WrongDeletesDatabase',
@SchemaName_n_TableName= 'dbo.WrongDeletes',
@SearchString = 'value1',
@SearchColumn = 'val',
@LogBackupFolder ='C:\temp\Logs\'
Ergebnis
ID val LogFileName
1 value1 c:\temp\Logs\log1_1.trn
1 value1 c:\temp\Logs\log3_1.trn
1 value1 c:\temp\Logs\log3_1.trn
Ein neuer Lauf, der nach der Ganzzahl sucht (2
) in dem val3
Spalte von dbo.WrongDeletes2
EXEC dbo.Recover_Deleted_Data_Proc @Database_Name= 'WrongDeletesDatabase',
@SchemaName_n_TableName= 'dbo.WrongDeletes2',
@SearchString = '2',
@SearchColumn = 'Val3',
@LogBackupFolder ='C:\temp\Logs\'
Ergebnis
Anotherval Val3 Wow LogFileName
value2 2 c c:\temp\Logs\log2.trn
value2 2 c c:\temp\Logs\log3.trn
Anwenden von Mark Storey-Smith 's Antwort
Wir wissen jetzt, dass es in der dritten Protokolldatei passiert ist. Lassen Sie uns bis zu diesem Punkt wiederherstellen:
USE master
GO
ALTER DATABASE WrongDeletesDatabase SET OFFLINE WITH ROLLBACK IMMEDIATE
GO
ALTER DATABASE WrongDeletesDatabase SET ONLINE
GO
RESTORE DATABASE WrongDeletesDatabase FROM DISK = 'c:\temp\Logs\Full.bak' WITH NORECOVERY,REPLACE
RESTORE LOG WrongDeletesDatabase FROM DISK = 'c:\temp\Logs\log1.trn' WITH NORECOVERY
RESTORE LOG WrongDeletesDatabase FROM DISK = 'c:\temp\Logs\log2.trn' WITH NORECOVERY
RESTORE LOG WrongDeletesDatabase FROM DISK = 'c:\temp\Logs\log3.trn' WITH RECOVERY
GO
USE WrongDeletesDatabase
GO
Die letzte Abfrage in seiner Antwort ausführen
SELECT
u.[name] AS UserName
, l.[Begin Time] AS TransactionStartTime
FROM
fn_dblog(NULL, NULL) l
INNER JOIN
(
SELECT
[Transaction ID]
FROM
fn_dblog(NULL, NULL)
WHERE
AllocUnitName LIKE @TableName + '%'
AND
Operation = 'LOP_DELETE_ROWS'
) deletes
ON deletes.[Transaction ID] = l.[Transaction ID]
INNER JOIN
sysusers u
ON u.[sid] = l.[Transaction SID]
Ergebnis für mich (sysadmin)
UserName TransactionStartTime
dbo 2019/08/09 17:14:10:450
dbo 2019/08/09 17:14:10:450