DBCC CHECKDB 无法修复的损坏:索引视图包含不是由视图定义生成的行

usr*_*usr 17 sql-server materialized-view corruption dbcc-checkdb

TL;DR:我在索引视图中有一个无法修复的损坏。以下是详细信息:


跑步

DBCC CHECKDB([DbName]) WITH EXTENDED_LOGICAL_CHECKS, DATA_PURITY, NO_INFOMSGS, ALL_ERRORMSGS

在我的数据库之一上产生以下错误:

消息 8907,级别 16,状态 1,第 1 行 空间索引、XML 索引或索引视图“ViewName”(对象 ID 784109934)包含不是由视图定义生成的行。这不一定表示此数据库中的数据存在完整性问题。(……)

CHECKDB 在表“ViewName”中发现 0 个分配错误和 1 个一致性错误。

repair_rebuild 是最低修复级别 (...)。

我确实理解此消息表明索引视图“ViewName”的物化数据与基础查询生成的数据不同。但是,手动验证数据不会出现任何差异:

SELECT * FROM ViewName WITH (NOEXPAND)
EXCEPT
SELECT ...
from T1 WITH (FORCESCAN)
join T2 on ...

SELECT ...
from T1 WITH (FORCESCAN)
join T2 on ...
EXCEPT
SELECT * FROM ViewName WITH (NOEXPAND)
Run Code Online (Sandbox Code Playgroud)

NOEXPAND用于强制在 上使用(唯一)索引ViewNameFORCESCAN用于防止索引视图匹配发生。执行计划确认这两种措施都有效。

这里没有返回任何行,这意味着两个表是相同的。(只有整数和 guid 列,排序规则不起作用)。

无法通过在视图上重新创建索引或运行DBCC CHECKDB REPAIR_ALLOW_DATA_LOSS. 重复修复也无济于事。为什么会DBCC CHECKDB报这个错误?如何摆脱它?

(即使重建修复了它,我的问题仍然存在 - 尽管我的数据检查查询成功运行,但为什么报告错误?)


更新:该错误已在某些版本中修复。我无法再在 SQL Server 2014 SP2 CU 5 中重现它。2014 SP2 KB包含一个没有 KB 文章的修复程序:Creating non-clustered index causes DBCC CheckDB With Extended_Logical_Checks to raise corruption error。关于此的两个连接错误已关闭:

Pau*_*ite 17

查询处理器可以为 DBCC 生成的(正确)查询生成无效的执行计划,以检查视图索引是否生成与基础视图查询相同的行。

查询处理器生成的计划错误地处理NULLs了该ImageObjectID列。它错误地认为视图查询拒绝NULLs此列,而事实并非如此。考虑NULLs排除,它能够匹配过滤Users表上的过滤非聚集索引ImageObjectID IS NOT NULL

通过生成使用此过滤索引的计划,可以确保不会遇到带有NULLin 的行ImageObjectID。这些行是从视图索引(正确)返回的,因此在没有损坏时似乎存在损坏。

视图定义是:

SELECT
    dbo.Universities.ID AS Universities_ID, 
    dbo.Users.ImageObjectID AS Users_ImageObjectID
FROM dbo.Universities
JOIN dbo.Users
    ON dbo.Universities.AdminUserID = dbo.Users.ID
Run Code Online (Sandbox Code Playgroud)

ON之间子句相等比较AdminUserIDID废品NULLs在这些栏,但不能从ImageObjectID柱上。

DBCC 生成的查询的一部分是:

SELECT [Universities_ID], [Users_ImageObjectID], 0 as 'SOURCE'
FROM [dbo].[mv_Universities_Users_ID] tOuter WITH (NOEXPAND) 
WHERE NOT EXISTS
( 
    SELECT 1 
    FROM   [dbo].[mv_Universities_Users_ID] tInner
    WHERE 
    (
        (
            (
                [tInner].[Universities_ID] = [tOuter].[Universities_ID]
            ) 
            OR 
            (
                [tInner].[Universities_ID] IS NULL
                AND [tOuter].[Universities_ID] IS NULL
            )
        )
        AND
        (
            (
                [tInner].[Users_ImageObjectID] = [tOuter].[Users_ImageObjectID]
            ) 
            OR 
            (
                [tInner].[Users_ImageObjectID] IS NULL 
                AND [tOuter].[Users_ImageObjectID] IS NULL
            )
        )
    )
)
OPTION (EXPAND VIEWS);
Run Code Online (Sandbox Code Playgroud)

这是以NULL感知方式比较值的通用代码。这当然是冗长的,但逻辑是好的。

查询处理器推理中的错误意味着可能会生成错误使用过滤索引的查询计划,如下面的示例计划片段所示:

错误的计划

DBCC 查询通过查询处理器采用与用户查询不同的代码路径。此代码路径包含错误。当生成使用过滤索引的计划时,它不能与USE PLAN提示一起使用以强制使用从用户数据库连接提交的相同查询文本的计划形状。

主要优化器代码路径(用于用户查询)不包含此错误,因此它特定于内部查询,例如 DBCC 生成的查询。

  • @usr DBCC 执行用户连接无法执行的各种操作。我想它会以这种方式工作,因为它必须这样做,但是你必须问像 Paul Randal 这样的人才能获得真正的细节。当然,他可能不能随意说。我确实知道在 DBCC 之外还有很多东西可以做更奇怪的事情;有些甚至在不经过优化器的情况下构建执行计划! (4认同)

usr*_*usr 7

进一步调查表明这是 DBCC CHECKDB 中的一个错误。已打开 Microsoft Connect 错误:Unfixable DBCC CHECKDB 错误(这也是误报,否则很奇怪)。幸运的是,我能够制作一个重现,以便可以找到并修复错误。

可以通过使用数据库模式来隐藏错误。删除不相关的过滤索引,或移除过滤器,隐藏错误。有关详细信息,请参阅连接项。

连接项还包含 DBCC CHECKDB 用来验证视图内容的内部查询。它没有返回任何结果,表明这是一个错误。

该错误已在某些版本中修复。我无法再在 SQL Server 2014 SP2 CU 5 中重现它。