Как выяснить, кто удалил некоторые данные SQL Server
-
16-10-2019 - |
Вопрос
У моего босса был запрос от клиента вчера, спрашивая, как они могут выяснить, кто удалил некоторые данные в своей базе данных SQL Server (это экспресс -издание, если это имеет значение).
Я думал, что это можно найти из журнала транзакций (при условии, что он не был усечен) - это правильно? И если да, то как вы на самом деле собираетесь найти эту информацию?
Решение
Я не пробовал fn_dblog на Express, но если он доступен, следующее даст вам операции удаления:
SELECT
*
FROM
fn_dblog(NULL, NULL)
WHERE
Operation = 'LOP_DELETE_ROWS'
Возьмите идентификатор транзакции для транзакций, которые вас интересуют, и определите SID, который инициировал транзакцию:
SELECT
[Transaction SID]
FROM
fn_dblog(NULL, NULL)
WHERE
[Transaction ID] = @TranID
AND
[Operation] = 'LOP_BEGIN_XACT'
Затем определите пользователя из SID:
SELECT
*
FROM
sysusers
WHERE
[sid] = @SID
РЕДАКТИРОВАТЬ: Соедините все вместе, чтобы найти удаления в указанной таблице:
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]
Другие советы
Если база данных находится в режиме полного восстановления или если у вас есть резервные копии журнала транзакций, вы можете попытаться прочитать их с помощью сторонних считывателей журнала.
Можешь попробовать ApexSQL Log (премиум, но имеет бесплатную пробную версию) или SQL Log Rescue (бесплатно, но только SQL 2000).
Как они могли выяснить, кто удалил некоторые данные в своей базе данных SQL Server
Несмотря на то, что на это отвечают, хотелось добавить, что SQL Server включает трассу по умолчанию, и его можно использовать, чтобы выяснить, кто отбросил/изменял объекты.
Объектные события
События объектов включают: измененный объект, созданный объект и удаление объекта
примечание: SQL Server по умолчанию имеет 5 файлов трассировки, 20 МБ каждый, и нет известного поддерживаемого метода изменения этого. Если у вас есть занятая система, файлы трассировки могут перевернуться слишком быстро (даже в течение нескольких часов), и вы, возможно, не сможете поймать некоторые изменения.
Отличный пример можно найти: Трассировка по умолчанию в SQL Server - мощность производительности и аудита безопасности
Вы можете попробовать эту процедуру, чтобы запросить файлы резервного копирования журнала и найти, в которых файлы (ы) резервного копирования журнала. Определенное значение столбца таблицы было все еще/последним.
Чтобы найти пользователя, после того, как вы обнаружите, какое резервное резервное копирование существует в последнем значении, вы можете восстановить базу данных до резервного копирования журнала, а затем следуйте Марк Стори-СмитОтвет.
Некоторые предпосылки
- знать, какие значения, из которых были удалены столбцы
- Находятся под моделью полного восстановления и принимают резервные копии журнала
- У вас есть даты или идентификаторы в резервном копировании журнала, например, при использовании решения Олы Халленгрен
Отказ от ответственности
Это решение далеко не водонепроницаемое, и в него нужно много работы.
Он не был протестирован в крупномасштабных условиях или даже в любых средах, кроме нескольких небольших испытаний. Текущий запуск был на 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
) к хранимой процедуре и запустите процедуру в поисках значения1
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