Як дізнатися, хто видалив деякі дані SQL Server


29

Учора мій бос запитував клієнта, запитуючи, як вони могли дізнатись, хто видалив деякі дані з їх бази даних SQL Server (якщо це має значення експрес-видання).

Я думав, що це можна знайти з журналу транзакцій (за умови, що він не був усічений) - це правильно? І якщо так, як ви насправді шукаєте цю інформацію?

Відповіді:


35

Я не пробував 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]

Це дійсно працює з SQL express, але в моїй системі він показує лише транзакції, що відбулися сьогодні. Я не думав, що у SQL Express не відбулося усічення журналу транзакцій?
Метт Вілько

5
Якщо ваша база даних є простою моделлю відновлення, ви не можете робити жодних припущень щодо того, як довго в журналі будуть триматися неактивні транзакції.
Аарон Бертран

3
Журнал транзакцій є основним, а не необов’язковим. Яка модель відновлення для бази даних (проста чи повна) та як налаштовані резервні копії (лише повна або резервна копія журналу + повна)?
Марк Сторі-Сміт

Я вкрав це для своєї відповіді тут, хоча трохи відреставрувавшись, щоб уникнути самостійного приєднання fn_dblog. Недоліком є ​​те, що він повертає базу даних, USERNAME()а не набагато корисніше ім’я для входу.
Мартін Сміт

3

Якщо база даних перебуває в повному режимі відновлення або у вас є резервне копіювання журналу транзакцій, ви можете спробувати їх прочитати за допомогою сторонніх читачів журналів.

Ви можете спробувати ApexSQL Log (преміум, але має безкоштовну пробну версію) або SQL Log Rescue (безкоштовно, але лише sql 2000).


3

як вони могли дізнатися, хто видалив деякі дані з своєї бази даних SQL Server

Хоча на це відповіли, хотіли додати, що SQL Server увімкнено трасування за замовчуванням, і його можна використовувати, щоб дізнатися, хто кинув / змінив об'єкти.

Об'єкт події

Події об'єкта включають: Об'єкт змінено, Об'єкт створено та Об'єкт видалено

Примітка: за замовчуванням у SQL Server є 5 файлів слідів, по 20 Мб кожен, і не існує відомого підтримуваного способу зміни цього. Якщо у вас зайнята система, файли слідів можуть перекидатися занадто швидко (навіть протягом декількох годин), і ви, можливо, не зможете зафіксувати деякі зміни.

Чудовий приклад можна знайти: слід за замовчуванням у SQL Server - потужність аудиту продуктивності та безпеки


1

Ви можете спробувати цю процедуру, щоб запитувати файли резервного копіювання журналу та виявити, у яких файлах резервного копіювання журналу певне значення стовпця таблиці все ще / останнє присутнє.

Щоб знайти користувача, після того, як ви знайдете в якому резервному копії журналу значення, яке існувало останнім часом, ви можете відновити базу даних до тих пір, поки не буде зроблено резервну копію цього журналу, а потім дотримуйтесь відповіді Марка Сторі-Сміта .

Деякі передумови

  • знати, які значення з яких стовпців було видалено
  • Знаходяться під повною моделлю відновлення та беруть резервні копії журналу
  • у ваших резервних копіях журналу є дати або ідентифікатори, наприклад, під час використання рішення 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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.