Come scoprire chi ha eliminato alcuni dati SQL Server


29

Il mio capo ha ricevuto una query da un cliente ieri che chiedeva come potevano scoprire chi ha eliminato alcuni dati nel loro database di SQL Server (è l'edizione espressa se ciò che conta).

Ho pensato che questo potesse essere trovato nel registro delle transazioni (a condizione che non fosse stato troncato) - è corretto? E se sì, come si fa effettivamente a trovare queste informazioni?

Risposte:


35

Non ho provato fn_dblog su Express ma se è disponibile quanto segue ti darà operazioni di eliminazione:

SELECT 
    * 
FROM 
    fn_dblog(NULL, NULL) 
WHERE 
    Operation = 'LOP_DELETE_ROWS'

Prendi l'ID transazione per le transazioni che ti interessano e identifica il SID che ha avviato la transazione con:

SELECT
    [Transaction SID]
FROM
    fn_dblog(NULL, NULL)
WHERE
    [Transaction ID] = @TranID
AND
    [Operation] = 'LOP_BEGIN_XACT'

Quindi identificare l'utente dal SID:

SELECT
    *
FROM 
    sysusers
WHERE
    [sid] = @SID

Modifica: riunendo tutto per trovare le eliminazioni su una tabella specificata:

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]

Questo funziona davvero con SQL Express ma sul mio sistema mostra solo le transazioni avvenute oggi. Non pensavo che SQL Express avesse un troncamento del registro delle transazioni pronto all'uso?
Matt Wilko,

5
Se il database è in un semplice modello di recupero, non è possibile fare ipotesi sulla durata delle transazioni inattive nel registro.
Aaron Bertrand

3
Il registro delle transazioni è fondamentale, piuttosto che facoltativo. Qual è il modello di recupero per il database (semplice o completo) e come vengono configurati i backup (solo completo o backup del registro + completo)?
Mark Storey-Smith,

Ho rubato questo per la mia risposta qui, anche se un po 'rifattorizzato per evitare il self-join fn_dblog. Uno svantaggio è che restituisce il database USERNAME()anziché il nome di accesso molto più utile.
Martin Smith,

3

Se il database è in modalità di recupero completo o se si dispone di backup del registro delle transazioni, è possibile provare a leggerli utilizzando lettori di registro di terze parti.

Puoi provare ApexSQL Log (premium ma ha una prova gratuita) o SQL Log Rescue (gratuito ma solo sql 2000).


3

come hanno potuto scoprire chi ha eliminato alcuni dati nel loro database di SQL Server

Nonostante ciò abbia risposto, ha voluto aggiungere che SQL Server ha una traccia predefinita abilitata e può essere usata per scoprire chi ha lasciato / modificato gli oggetti.

Eventi oggetto

Gli eventi oggetto includono: Oggetto modificato, Oggetto creato ed Oggetto eliminato

nota: per impostazione predefinita SQL Server ha 5 file di traccia, 20 MB ciascuno e non è noto alcun metodo supportato per modificarlo. Se si dispone di un sistema occupato, i file di traccia potrebbero scorrere troppo velocemente (anche entro poche ore) e potrebbe non essere possibile rilevare alcune delle modifiche.

È possibile trovare un esempio eccellente: la traccia predefinita in SQL Server: la potenza delle prestazioni e il controllo della sicurezza


1

È possibile provare questa procedura per eseguire una query sui file di backup del registro e trovare in quali file di backup del registro un valore specifico di una colonna di una tabella era ancora / presente per ultimo.

Per trovare l'utente, dopo aver trovato nel backup del registro l'ultimo valore esistente, è possibile ripristinare un database fino al backup del registro e quindi seguire la risposta di Mark Storey-Smith .

Alcuni prerequisiti

  • sapere quali valori da quali colonne sono state eliminate
  • Sono sotto il modello di recupero completo e stanno eseguendo backup del registro
  • hai date o identificatori nei tuoi backup di log, come quando usi la soluzione di Ola Hallengren

disconoscimento

Questa soluzione è tutt'altro che impermeabile e richiede molto più lavoro.

Non è stato testato su ambienti di grandi dimensioni, o su qualsiasi ambiente, a parte alcuni piccoli test. L'esecuzione corrente era su SQL Server 2017.

È possibile utilizzare la procedura seguente di Muhammad Imran che ho modificato per funzionare con il contenuto dei backup del registro invece del contenuto del registro di un database live.

In questo modo tecnicamente non si eseguono ripristini, ma si scarica invece il contenuto del registro in una tabella temporanea. Probabilmente sarà ancora lento, ed è molto aperto a bug e problemi. Ma potrebbe funzionare, in teoria ™.

La procedura memorizzata utilizza la fn_dump_dblogfunzione non documentata per leggere i file di registro.


Ambiente di test

Considera questo database, in cui inseriamo alcune righe, eseguiamo 2 backup del log e sul terzo backup del log eliminiamo tutte le righe.

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

La procedura

Puoi trovare e scaricare la procedura memorizzata qui .

Non potrei aggiungerlo qui poiché è più grande del limite del personaggio e renderebbe questa risposta ancora meno chiara di quello che è.

A parte questo, dovresti essere in grado di eseguire la procedura.

Esecuzione della procedura

Un esempio di ciò, quando aggiungo tutti i miei file di registro ( 4) alla procedura memorizzata ed eseguo la procedura cercando value1

EXEC dbo.Recover_Deleted_Data_Proc  @Database_Name= 'WrongDeletesDatabase',
                                    @SchemaName_n_TableName= 'dbo.WrongDeletes', 
                                    @SearchString = 'value1', 
                                    @SearchColumn = 'val',
                                    @LogBackupFolder ='C:\temp\Logs\'

Questo mi fa:

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

Dove possiamo trovare quando è avvenuta l'ultima volta un'operazione su value1, l'eliminazione in log3.trn.

Alcuni altri dati di test, aggiungendo una tabella con colonne diverse

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

Modifica dei nomi dei file di registro ed esecuzione della procedura

EXEC dbo.Recover_Deleted_Data_Proc  @Database_Name= 'WrongDeletesDatabase',
                                    @SchemaName_n_TableName= 'dbo.WrongDeletes', 
                                    @SearchString = 'value1', 
                                    @SearchColumn = 'val',
                                    @LogBackupFolder ='C:\temp\Logs\'

Risultato

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

Una nuova corsa, alla ricerca dell'intero ( 2) nella val3colonna didbo.WrongDeletes2

EXEC dbo.Recover_Deleted_Data_Proc  @Database_Name= 'WrongDeletesDatabase',
                                    @SchemaName_n_TableName= 'dbo.WrongDeletes2', 
                                    @SearchString = '2', 
                                    @SearchColumn = 'Val3',
                                    @LogBackupFolder ='C:\temp\Logs\'

Risultato

Anotherval  Val3    Wow LogFileName
value2  2   c   c:\temp\Logs\log2.trn
value2  2   c   c:\temp\Logs\log3.trn

Applicando la risposta di Mark Storey-Smith

Ora sappiamo che è successo nel terzo file di registro, ripristiniamo fino a quel punto:

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

Esecuzione dell'ultima query nella sua risposta

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]

Risultato per me (amministratore di sistema)

UserName    TransactionStartTime
dbo 2019/08/09 17:14:10:450
dbo 2019/08/09 17:14:10:450
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.