我的老板昨天从客户那里有一个查询,询问他们如何找出谁在SQL Server数据库中删除了一些数据(如果很重要的话,这是Express Edition)。

我认为可以从交易日志中找到这一点(前提是它没有被截断) - 这是正确的吗?如果是这样,您实际上如何找到这些信息?

有帮助吗?

解决方案

我没有在Express上尝试过FN_DBLOG,但是如果可用,以下将为您删除操作:

SELECT 
    * 
FROM 
    fn_dblog(NULL, NULL) 
WHERE 
    Operation = 'LOP_DELETE_ROWS'

以您感兴趣的交易的交易ID,并确定启动交易的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日志 (溢价但有免费试用)或 SQL原木救援 (免费但仅SQL 2000)。

他们如何找出谁在SQL Server数据库中删除了一些数据

尽管已经回答了这一点,但想添加SQL Server启用了默认跟踪,可以用来找出谁掉落/更改对象。

对象事件

对象事件包括:对象更改,创建对象并删除对象

笔记: 默认情况下,SQL Server有5个跟踪文件,每个20 MB,并且没有已知的支持方法可以更改此文件。如果您有一个繁忙的系统,则跟踪文件可能会滚动太快(即使在几个小时之内),并且您可能无法捕获一些更改。

可以找到很好的例子: SQL Server中的默认跟踪 - 性能和安全性审核的功能

您可以尝试此过程来查询日志备份文件,并查找在哪个日志备份文件中仍然/上次存在的表列的特定值。

要查找用户,在发现最后一个值的登录备份中,您可以还原数据库,直到该日志备份,然后关注 标记楼层史密斯的答案。

一些先决条件

  • 知道从哪些列删除了哪些值
  • 在完整的恢复模型下,正在进行日志备份
  • 您的日志备份中有日期或标识符,例如使用Ola Hallengren的解决方案时

免责声明

该解决方案远非防水,需要更多的工作。

它尚未在大规模环境中,甚至在几个小测试中进行测试。当前运行在SQL Server 2017上。

您可以在下面使用 程序穆罕默德·伊姆兰(Muhammad Imran) 我修改了以与内容的内容合作 日志备份 而不是实时数据库日志的内容。

这样,您从技术上讲,您不进行还原,而是将日志内容倒在临时表中。它可能仍然很慢,并且对错误和问题非常开放。但从理论上讲,它可以起作用。

存储的过程使用未记录的 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) 在里面 val3dbo.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