Puede intentar este procedimiento para consultar los archivos de respaldo de registro y encontrar en qué archivo (s) de respaldo de registro todavía estaba / último presente un valor específico de una columna de una tabla.
Para encontrar al usuario, después de encontrar en qué respaldo de registro existía el último valor, puede restaurar una base de datos hasta ese respaldo de respaldo y luego seguir la respuesta de Mark Storey-Smith .
Algunos requisitos previos
- saber qué valores de qué columnas se eliminaron
- Están bajo el modelo de recuperación completa y están realizando copias de seguridad de registros
- tiene fechas o identificadores en sus copias de seguridad de registros, como cuando usa la solución de Ola Hallengren
Renuncia
Esta solución está lejos de ser resistente al agua, y se necesita mucho más trabajo.
No se ha probado en entornos a gran escala, o incluso en cualquier entorno aparte de unas pocas pruebas pequeñas. La ejecución actual fue en SQL Server 2017.
Puede usar el siguiente procedimiento de Muhammad Imran que modifiqué para trabajar con el contenido de las copias de seguridad de registros en lugar de los contenidos del registro de una base de datos en vivo.
De esta manera, técnicamente no está haciendo restauraciones, sino que está volcando el contenido del registro en una tabla temporal. Probablemente seguirá siendo lento y está muy abierto a errores y problemas. Pero podría funcionar, en teoría ™.
El procedimiento almacenado utiliza la fn_dump_dblog
función no documentada para leer los archivos de registro.
Entorno de prueba
Considere esta base de datos, donde insertamos algunas filas, tomamos 2 copias de seguridad de registro, y en la tercera copia de seguridad de registro eliminamos todas las filas.
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
El procedimiento
Puede encontrar y descargar el procedimiento almacenado aquí .
No podría agregarlo aquí, ya que es más grande que el límite de caracteres, y haría que esta respuesta sea aún menos clara de lo que es.
Aparte de esto, debería poder ejecutar el procedimiento.
Ejecutando el procedimiento
Un ejemplo de esto, cuando agrego todos mis archivos de registro ( 4
) al procedimiento almacenado y ejecuto el procedimiento buscando valor1
EXEC dbo.Recover_Deleted_Data_Proc @Database_Name= 'WrongDeletesDatabase',
@SchemaName_n_TableName= 'dbo.WrongDeletes',
@SearchString = 'value1',
@SearchColumn = 'val',
@LogBackupFolder ='C:\temp\Logs\'
Esto me atrapa:
ID val LogFileName
1 value1 c:\temp\Logs\log3.trn
1 value1 c:\temp\Logs\log1.trn
Donde podemos encontrar cuándo fue la última vez que value1
ocurrió una operación , la eliminación en log3.trn
.
Algunos datos de prueba más, agregando una tabla con diferentes columnas
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
Cambiar los nombres de los archivos de registro y volver a ejecutar el procedimiento
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
Una nueva ejecución, buscando el entero ( 2
) en la val3
columna 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 la respuesta de Mark Storey-Smith
Ahora sabemos que sucedió en el tercer archivo de registro, restauremos hasta ese 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
Ejecutando la última consulta en su respuesta
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 mí (administrador de sistemas)
UserName TransactionStartTime
dbo 2019/08/09 17:14:10:450
dbo 2019/08/09 17:14:10:450