每年大约 5 次,我们最关键的表之一有一个特定列,其中所有值都替换为 NULL。我们已经针对此运行了日志浏览器,我们看不到任何登录名/主机名填充了更新,我们只能看到记录已更改。我们已经搜索了我们所有的存储过程、函数等,以查找在我们服务器上的所有数据库上触及该表的任何更新语句。该表确实对此列有外键约束。它是在更新期间建立的整数值,但更新是特定于身份密钥的。该字段还有一个索引。关于在 t-sql 更新语句之外可能导致此问题的任何建议?
3 回答
如果可能的话,我会首先拒绝任何客户端动态 SQL。审计存储过程以确保它们执行正确的 sql(包括正确的 where 子句)要容易得多。除非您的 sql 服务器严重损坏,否则它们更新数据的唯一方法是因为您正在针对它运行 sql。
所有存储的过程、脚本等都应该在被允许运行之前进行审计。
如果您没有强制执行任何动态客户端 sql 的 mojo,请添加应用程序日志记录,以在执行每个客户端 sql 之前捕获它。就个人而言,当缺少 where 子句时,我会让日志记录例程抛出异常(在记录它之后),但至少,您应该能够通过查看日志找出下次数据在哪里被炸毁。确保您的日志捕获了足够的信息,以便您可以将其追溯到确切的来源。为执行的每个可能的动态 sql 语句分配一个唯一的“名称”,例如,每个为每个程序分配一个 3 字符代码,然后在程序中为每个可能的调用编号 1..nn,这样您就可以知道哪个调用炸毁了您的数据“abc123”以及有缺陷的确切 sql。
添加评论
后来想到了这个。您可能能够在 sql 表上添加/修改更新触发器以查看行数更新阻止更新如果行数超过对您有意义的阈值。所以,做了一点搜索,发现有人已经写了一篇关于这个的文章,就像在这个片段中一样
CREATE TRIGGER [Purchasing].[uPreventWholeUpdate]
ON [Purchasing].[VendorContact]
FOR UPDATE AS
BEGIN
DECLARE @Count int
SET @Count = @@ROWCOUNT;
IF @Count >= (SELECT SUM(row_count)
FROM sys.dm_db_partition_stats
WHERE OBJECT_ID = OBJECT_ID('Purchasing.VendorContact' )
AND index_id = 1)
BEGIN
RAISERROR('Cannot update all rows',16,1)
ROLLBACK TRANSACTION
RETURN;
END
END
尽管这并不是真正的正确解决方案,但如果您正确记录此问题,我敢打赌您可以弄清楚是什么试图搞砸您的数据并修复它。
祝你好运
事务日志浏览器应该能够查看执行命令的人员、时间以及命令的具体情况。
您使用哪个日志浏览器?如果您使用的是ApexSQL 日志,您需要启用连接监视器功能以捕获其他登录详细信息。
这可能就像使用大锤敲击图钉一样,但您是否考虑过使用 SQL Server 审计(前提是您使用的是 SQL Server Enterprise 2008 或更高版本)?