Ви можете спробувати цю процедуру, щоб запитувати файли резервного копіювання журналу та виявити, у яких файлах резервного копіювання журналу певне значення стовпця таблиці все ще / останнє присутнє.
Щоб знайти користувача, після того, як ви знайдете в якому резервному копії журналу значення, яке існувало останнім часом, ви можете відновити базу даних до тих пір, поки не буде зроблено резервну копію цього журналу, а потім дотримуйтесь відповіді Марка Сторі-Сміта .
Деякі передумови
- знати, які значення з яких стовпців було видалено
- Знаходяться під повною моделлю відновлення та беруть резервні копії журналу
- у ваших резервних копіях журналу є дати або ідентифікатори, наприклад, під час використання рішення Ola Hallengren
Відмова від відповідальності
Це рішення далеко не водонепроникне, і в ньому потрібно вкласти набагато більше роботи.
Він не був протестований у великих масштабах або навіть будь-яких середовищах, окрім кількох невеликих тестів. Поточний запуск відбувся на SQL Server 2017.
Ви можете використовувати нижче процедуру від Мухаммеда Імрана, яку я змінив для роботи зі вмістом резервного копіювання журналу замість вмісту журналу живої бази даних.
Таким чином, ви технічно не виконуєте відновлення, а натомість скидаєте вміст журналу у тимчасову таблицю. Це, мабуть, все ще буде повільно, і дуже відкрито для помилок та проблем. Але теоретично це може працювати.
Збережена процедура використовує недокументовану fn_dump_dblog
функцію для зчитування файлів журналу.
Тестове середовище
Розглянемо цю базу даних, куди ми вставляємо кілька рядків, беремо 2 резервні копії журналу, а на третьому резервному копії журналу видаляємо всі рядки.
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
Процедура
Ви можете знайти та завантажити збережену процедуру тут .
Я не міг би його тут додати, оскільки він більший, ніж межа символів, і зробив би цю відповідь ще менш зрозумілою, ніж є.
Крім цього, ви повинні мати можливість запустити процедуру.
Запуск процедури
Приклад цього, коли я додаю всі свої файли журналу ( 4
) до збереженої процедури та запускаю процедуру, шукаючу value1
EXEC dbo.Recover_Deleted_Data_Proc @Database_Name= 'WrongDeletesDatabase',
@SchemaName_n_TableName= 'dbo.WrongDeletes',
@SearchString = 'value1',
@SearchColumn = 'val',
@LogBackupFolder ='C:\temp\Logs\'
Це отримує мене:
ID val LogFileName
1 value1 c:\temp\Logs\log3.trn
1 value1 c:\temp\Logs\log1.trn
Де ми можемо знайти, коли востаннє відбулася операція value1
, видалити в log3.trn
.
Ще кілька тестових даних, додавши таблицю з різними стовпцями
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
Зміна імен файлів журналу та повторне виконання процедури
EXEC dbo.Recover_Deleted_Data_Proc @Database_Name= 'WrongDeletesDatabase',
@SchemaName_n_TableName= 'dbo.WrongDeletes',
@SearchString = 'value1',
@SearchColumn = 'val',
@LogBackupFolder ='C:\temp\Logs\'
Результат
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
Новий запуск, пошук цілого числа ( 2
) у val3
стовпціdbo.WrongDeletes2
EXEC dbo.Recover_Deleted_Data_Proc @Database_Name= 'WrongDeletesDatabase',
@SchemaName_n_TableName= 'dbo.WrongDeletes2',
@SearchString = '2',
@SearchColumn = 'Val3',
@LogBackupFolder ='C:\temp\Logs\'
Результат
Anotherval Val3 Wow LogFileName
value2 2 c c:\temp\Logs\log2.trn
value2 2 c c:\temp\Logs\log3.trn
Застосовуючи Марку Сторі-Сміт відповідь «сек
Тепер ми знаємо, що це сталося в третьому файлі журналу, відновимо до цього моменту:
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
Запуск останнього запиту у його відповіді
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]
Результат для мене (sysadmin)
UserName TransactionStartTime
dbo 2019/08/09 17:14:10:450
dbo 2019/08/09 17:14:10:450