Você pode tentar este procedimento para consultar os arquivos de backup de log e encontrar em quais arquivos de backup de log um valor específico de uma coluna de uma tabela ainda estava / último presente.
Para encontrar o usuário, depois de encontrar em qual backup de log o valor existia pela última vez, você pode restaurar um banco de dados até esse backup de log e seguir a resposta de Mark Storey-Smith .
Alguns pré-requisitos
- saber quais valores de quais colunas foram excluídas
- Estão no modelo de recuperação completa e estão fazendo backups de log
- você tem datas ou identificadores em seus backups de log, como ao usar a solução de Ola Hallengren
aviso Legal
Esta solução está longe de ser à prova d'água e é necessário muito mais trabalho.
Não foi testado em ambientes de grande escala, nem em ambientes que não sejam alguns testes pequenos. A execução atual foi no SQL Server 2017.
Você pode usar o procedimento abaixo de Muhammad Imran, que eu modifiquei para trabalhar com o conteúdo dos backups de log em vez do conteúdo do log de um banco de dados ativo.
Dessa forma, tecnicamente, você não está fazendo restaurações, mas descartando o conteúdo do log em uma tabela temporária. Provavelmente ainda será lento e está muito aberto a bugs e problemas. Mas poderia funcionar, em teoria ™.
O procedimento armazenado usa a fn_dump_dblog
função não documentada para ler os arquivos de log.
Ambiente de teste
Considere este banco de dados, onde inserimos algumas linhas, fazemos 2 backups de log e, no terceiro, excluímos todas as linhas.
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
O procedimento
Você pode encontrar e baixar o procedimento armazenado aqui .
Eu não poderia adicioná-lo aqui, pois é maior que o limite de caracteres e tornaria essa resposta ainda menos clara do que é.
Além disso, você deve poder executar o procedimento.
Executando o procedimento
Um exemplo disso, quando adiciono todos os meus arquivos de log ( 4
) ao procedimento armazenado e executo o procedimento procurando valor1
EXEC dbo.Recover_Deleted_Data_Proc @Database_Name= 'WrongDeletesDatabase',
@SchemaName_n_TableName= 'dbo.WrongDeletes',
@SearchString = 'value1',
@SearchColumn = 'val',
@LogBackupFolder ='C:\temp\Logs\'
Isso me deixa:
ID val LogFileName
1 value1 c:\temp\Logs\log3.trn
1 value1 c:\temp\Logs\log1.trn
Onde podemos encontrar a última vez que uma operação value1
ocorreu, a exclusão de log3.trn
.
Mais alguns dados de teste, adicionando uma tabela com colunas diferentes
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
Alterando os nomes dos arquivos de log e executando o procedimento novamente
EXEC dbo.Recover_Deleted_Data_Proc @Database_Name= 'WrongDeletesDatabase',
@SchemaName_n_TableName= 'dbo.WrongDeletes',
@SearchString = 'value1',
@SearchColumn = 'val',
@LogBackupFolder ='C:\temp\Logs\'
Resultado
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
Uma nova execução, procurando o número inteiro ( 2
) na val3
coluna dedbo.WrongDeletes2
EXEC dbo.Recover_Deleted_Data_Proc @Database_Name= 'WrongDeletesDatabase',
@SchemaName_n_TableName= 'dbo.WrongDeletes2',
@SearchString = '2',
@SearchColumn = 'Val3',
@LogBackupFolder ='C:\temp\Logs\'
Resultado
Anotherval Val3 Wow LogFileName
value2 2 c c:\temp\Logs\log2.trn
value2 2 c c:\temp\Logs\log3.trn
Aplicando a resposta de Mark Storey-Smith
Sabemos agora que isso aconteceu no terceiro arquivo de log, vamos restaurar até esse ponto:
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
Executando a última consulta em sua resposta
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]
Resultado para mim (sysadmin)
UserName TransactionStartTime
dbo 2019/08/09 17:14:10:450
dbo 2019/08/09 17:14:10:450