sql-server – 带有持久计算的CLR发起数据的表,标记为has_unchecked_assembly_data = 1,但DBCC CHECKTABLE不会取消设置此标记

我们有一个包含列的OrderLines表:

Quantity int not null
QtyCancelled int not null
QtyBackorder int not null
QtyPicking int not null
QtyPacking int not null
QtyShiped int not null

使用计算和持久列FulfillmentStatusId:

dbo.clr_GetFulfillmentStatusByLineQuantities(Quantity, QtyCancelled, QtyBackorder, QtyPicking, QtyPacking, QtyShiped)

当然,“clr_GetFulfillmentStatusByLineQuantities”通过标量函数映射到数据库中的ASSEMBLY,其关联方法(C#)标记为:

[SqlFunction(DataAccess = None, IsDeterministic = true, IsPrecise = true, SystemDataAccess = None)]

参考this question,我多次对该表运行DBCC CHECKTABLE,它仍然保持为has_unchecked_assembly_data = 1.当列首次添加到表中时,这是真的,但我相信DBCC CHECKTABLE命令在迁移到SQL 2016之前成功.

虽然该表已经在has_unchecked_assembly_data = 1中使用了很长时间,但似乎对表的功能没有影响.我担心ALTER ASSEMBLY在不使用WITH UNCHECKED DATA时抛出异常,然后在我使用该选项时抛出另一个异常,但似乎“根据MVID”更新程序集.

对不起,如果我絮絮叨叨,我不会问太多问题而且我大多潜伏着.

但是,请提前感谢!

更新(20180917T17:34-05:00):
感谢肖恩建议的初步方向. DBCC CHECKTABLE(‘OrderLines’)WITH EXTENDED_LOGICAL_CHECKS的结果是两行类似于:

Msg 2537, Level 16, State 106, Line 1
Table error: object ID 1486732449, index ID 1, partition ID 72057594403946496, alloc unit ID 72057594422755328 (type In-row data), page (1:1430554), row 5. The record check (valid computed column) failed. The values are 38 and 0.

所以我在表的主键上执行了一个REBUILD(这是上面指出的索引ID 1),这是成功的.但是,重新运行DBCC CHECKTABLE(‘OrderLines’)WITH EXTENDED_LOGICAL_CHECKS再次返回类似的错误,只有这次使用不同的分区ID和分配单元ID(如人们所料).

极端的解决方案可能是使用标识插入来删除,重新创建和重新填充表格,“从轨道上取出网站”可以这么说,但出于显而易见的原因,这是极端的.

什么是“表格聚集索引中似乎存在不一致的计算列数据”的正确解决方案?

最佳答案
OP在这里,回答我自己的(和第一个)问题!

在Sean Gallardy的帮助下,使用EXTENDED_LOGICAL_CHECKS指出我与持久计算列的数据不一致.我首先尝试重新创建主键(聚集索引),但这实际上没有修复任何东西(极端和奇怪的解决方案).

我也尝试使用DBCC CHECKTABLE(‘{table}’,REPAIR_ALLOW_DATA_LOSS),但是再次返回相同的错误.我不相信任何东西都得到了修复.

我的第一个解决方案是删除并重新创建持久计算列.这很有效.

但是,认为这也有点“极端”(考虑到表有10M行并且需要永远这样做,因为还有一个针对该列的索引),我很好奇一个更直接的解决方案.

由于DBCC CHECKTABLE只报告了两行及其页面和行引用,因此我查看了DBCC PAGE.精确地使用此命令可以获得实际的行数据/行标识,当我执行驱动计算列的CLR方法时,确实存在不一致.

基本的实际和不一致的表数据:

OrderLineId  QtyOrd  QtyCancl  QtyBack  QtyPick  QtyPack  QtyShip  FulfillId
    1234567       4        12        0        0        0        0          0
    4567890       4        12        0        0        0        0          0

在这种情况下,FulfillId应为1(表示已取消),但这两个表行指示两行的0(打开/未完成).

我使用SELECT语句通过CLR函数运行实际的行数据:

select
  ol.OrderLineId,
  ol.QtyOrd, ol.QtyCancl, ol.QtyBack, ol.QtyPick, ol.QtyPack, ol.QtyShip,
  ol.FulfillId [CurrentFulfillId],
  CLR(ol.QtyOrd, ol.QtyCancl, ol.QtyBack, ol.QtyPick, ol.QtyPack, ol.QtyShip) [ComputedFulfillId],
from
  OrderLines [ol]
where
  ol.OrderLineId in (1234567, 4567890)

这基本上返回了完全相同的信息,ComputedFulfillId也解析为0(打开).所以我直接在.Net控制台应用程序中重新测试了CLR代码,并给出了应该在SQL调用中传递的显式值,结果是期望值1(已取消).

我修改了SELECT语句,使用显式常量值而不是来自可疑行的值来调用CLR方法:

SELECT ..., CLR(4, 12, 0, 0, 0, 0) [ForcedFulfillId] FROM OrderLines [ol] ...

ForcedFulfillId列包含正确的值1.

使用DBCC PAGE查找确切的行标识并更新有问题的行,强制重新计算计算列数据.最后一个DBCC CHECKTABLE没有返回任何错误,一切都很好.

因此,不是删除并重新创建整个计算列,使用DBCC PAGE和来自DBCC CHECKTABLE({table})WITH EXTENDED_LOGICAL_CHECKS的信息允许我修复原始的“has_unchecked_assembly_data = 1”问题导致ALTER ASSEMBLY错误.

对于这个冗长的解释感到抱歉 – 我想确保记录下我为下一个可怜的灵魂所做的旅行.

转载注明原文:sql-server – 带有持久计算的CLR发起数据的表,标记为has_unchecked_assembly_data = 1,但DBCC CHECKTABLE不会取消设置此标记 - 代码日志