So finden Sie heraus, wer einige Daten gelöscht hat SQL Server

29

Mein Chef hatte gestern eine Frage von einem Kunden, wie er herausfinden könnte, wer einige Daten in seiner SQL Server-Datenbank gelöscht hat (es ist die Express-Edition, wenn das wichtig ist).

Ich dachte, dies könnte aus dem Transaktionsprotokoll gefunden werden (vorausgesetzt, es wurde nicht abgeschnitten) - ist das richtig? Und wenn ja, wie finden Sie diese Informationen heraus?

Matt Wilko
quelle

Antworten:

35

Ich habe fn_dblog nicht auf Express 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'

Dann identifizieren Sie den Benutzer anhand der SID:

SELECT
    *
FROM 
    sysusers
WHERE
    [sid] = @SID

Bearbeiten: Bringen Sie das alles zusammen, um Löschungen 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]
Mark Storey-Smith
quelle
Dies funktioniert zwar mit SQL Express, aber auf meinem System werden nur die Transaktionen angezeigt, die heute stattgefunden haben. Ich hätte nicht gedacht, dass SQL Express ein Transaktionsprotokoll hat, das sofort abgeschnitten wird.
Matt Wilko
5
Wenn sich Ihre Datenbank in einem einfachen Wiederherstellungsmodell befindet, können Sie keine Annahmen darüber treffen, wie lange inaktive Transaktionen im Protokoll verbleiben.
Aaron Bertrand
3
Das Transaktionsprotokoll ist nicht optional, sondern von grundlegender Bedeutung. Was ist das Wiederherstellungsmodell für die Datenbank (einfach oder vollständig) und wie werden Sicherungen konfiguriert (nur vollständig oder Protokollsicherung + vollständig)?
Mark Storey-Smith
Ich habe dies für meine Antwort hier gestohlen , obwohl es ein wenig überarbeitet wurde, um das Selbstverbinden zu vermeiden fn_dblog. Ein Nachteil ist, dass die Datenbank zurückgegeben wird USERNAME()und nicht der viel nützlichere Anmeldename.
Martin Smith
3

Befindet sich die Datenbank im vollständigen Wiederherstellungsmodus oder verfügen Sie über Transaktionsprotokollsicherungen, können Sie versuchen, diese mit Protokolllesegeräten von Drittanbietern zu lesen.

Sie können ApexSQL Log (Premium, aber kostenlose Testversion) oder SQL Log Rescue (kostenlos, aber nur SQL 2000) ausprobieren.

Tony Melanik
quelle
3

wie sie herausfinden könnten, wer einige Daten in ihrer SQL Server-Datenbank gelöscht hat

Obwohl dies beantwortet wurde, möchte ich hinzufügen, dass für SQL Server eine Standardablaufverfolgung aktiviert ist und dass damit ermittelt werden kann, wer die Objekte gelöscht / geändert hat.

Objektereignisse

Objektereignisse umfassen: Objekt geändert, Objekt erstellt und Objekt gelöscht

Hinweis: SQL Server verfügt standardmäßig über 5 Ablaufverfolgungsdateien mit jeweils 20 MB, und es gibt keine bekannte unterstützte Methode, um dies zu ändern. Wenn Sie ein ausgelastetes System haben, werden die Ablaufverfolgungsdateien möglicherweise viel zu schnell übertragen (sogar innerhalb von Stunden) und Sie können möglicherweise einige der Änderungen nicht erfassen.

Es gibt ein hervorragendes Beispiel: Die Standardablaufverfolgung in SQL Server - die Leistungsfähigkeit und Sicherheitsprüfung

Kin Shah
quelle
1

Mit dieser Prozedur können Sie die Protokollsicherungsdateien abfragen und feststellen, in welchen Protokollsicherungsdateien ein bestimmter Wert einer Tabellenspalte noch bzw. zuletzt vorhanden war.

Um den Benutzer zu finden, nachdem Sie in welcher Protokollsicherung den zuletzt vorhandenen Wert gefunden haben, können Sie eine Datenbank bis zu dieser Protokollsicherung wiederherstellen und dann Mark Storey-Smiths Antwort folgen .

Einige Voraussetzungen

  • wissen, welche Werte aus welchen Spalten gelöscht wurden
  • Sind unter dem vollständigen Wiederherstellungsmodell und führen Protokollsicherungen durch
  • Ihre Protokollsicherungen enthalten Daten oder Kennungen, z. B. bei der Verwendung der Lösung von Ola Hallengren

Haftungsausschluss

Diese Lösung ist weit davon entfernt, wasserdicht zu sein, und es muss noch viel mehr Arbeit aufgewendet werden.

Es wurde nicht in großen Umgebungen oder sogar in anderen Umgebungen getestet, mit Ausnahme einiger kleinerer Tests. Die aktuelle Ausführung erfolgte auf SQL Server 2017.

Sie können das folgende Verfahren von Muhammad Imran verwenden , das ich geändert habe, um mit dem Inhalt von Protokollsicherungen anstelle des Inhalts des Protokolls einer Live-Datenbank zu arbeiten.

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 die undokumentierte fn_dump_dblogFunktion, um die Protokolldateien auszulesen.


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

Der Ablauf

Sie können die gespeicherte Prozedur hier finden und herunterladen .

Ich konnte es hier nicht hinzufügen, da es größer als die Zeichenbegrenzung ist, und würde diese Antwort noch unklarer machen, als es ist.

Abgesehen davon sollten Sie in der Lage sein, die Prozedur auszuführen.

Prozedur ausführen

Ein Beispiel dafür, wenn ich alle meine Protokolldateien ( 4) zur gespeicherten Prozedur hinzufüge und die Prozedur auf der Suche nach value1 ausführe

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 auf:

ID  val LogFileName
1   value1  c:\temp\Logs\log3.trn
1   value1  c:\temp\Logs\log1.trn

Wo wir finden können, wann das letzte Mal eine Operation value1stattgefunden hat, ist 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 des Vorgangs

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 ( 2) in der val3Spalte von suchtdbo.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 der Antwort von Mark Storey-Smith

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

Ausführen der letzten Abfrage in seiner Antwort

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
Randi Vertongen
quelle