Вопрос

У моего босса был запрос от клиента вчера, спрашивая, как они могут выяснить, кто удалил некоторые данные в своей базе данных 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
Лицензировано под: CC-BY-SA с атрибуция
Не связан с dba.stackexchange
scroll top