web-dev-qa-db-ja.com

DBCC CHECKDBの修正不可能な破損:インデックス付きビューには、ビュー定義によって生成されなかった行が含まれています

TL; DR:インデックス付きビューに修正不可能な破損があります。詳細は次のとおりです。


ランニング

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

データベースの1つで次のエラーが発生します。

メッセージ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)

NOEXPANDは、ViewNameの(のみ)インデックスの使用を強制するために使用されました。 FORCESCANは、インデックス付きビューのマッチングが行われないようにするために使用されました。実行計画は、両方の手段が機能していることを確認します。

ここでは行は返されません。つまり、2つのテーブルは同一です。 (整数と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。これに関する2つの接続バグはクローズされました。

14
usr

クエリプロセッサは、DBCCによって生成された(正しい)クエリに対して無効な実行プランを生成し、ビューインデックスが基になるビュークエリと同じ行を生成することを確認できます。

クエリプロセッサによって作成されたプランは、NULLs列のImageObjectIDを誤って処理します。ビュークエリがこの列のNULLsを拒否しないと誤って推論します。 NULLsが除外されていると考えると、ImageObjectID IS NOT NULLでフィルタリングするUsersテーブルでフィルタリングされた非クラスター化インデックスを照合できます。

このフィルター処理されたインデックスを使用するプランを作成することにより、NULLImageObjectIDが含まれる行が検出されないようにします。これらの行はビューインデックスから(正しく)返されるため、破損がない場合は破損しているように見えます。

ビューの定義は次のとおりです。

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

ONAdminUserIDの間のID句の等価比較は、これらの列の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);

これは、NULL対応の方法で値を比較する汎用コードです。確かに冗長ですが、ロジックは問題ありません。

クエリプロセッサの推論のバグは、フィルターされたインデックスを誤って使用するクエリプランが、以下のサンプルプランのフラグメントのように生成される可能性があることを意味します。

Erroneous plan

DBCCクエリは、クエリプロセッサを介してユーザークエリとは異なるコードパスを使用します。このコードパスにはバグが含まれています。フィルター選択されたインデックスを使用するプランが生成された場合、USE PLANヒントと共に使用して、ユーザーデータベース接続から送信された同じクエリテキストでそのプランの形状を強制することはできません。

メインオプティマイザーコードパス(ユーザークエリ用)にはこのバグが含まれていないため、DBCCによって生成されるような内部クエリに固有です。

14
Paul White 9

さらに調査したところ、これはDBCC CHECKDBのバグであることがわかりました。 Microsoft Connectのバグが開かれました: 修正不能のDBCC CHECKDBエラー(これも誤検出であり、それ以外は奇妙です) 。幸い、バグを見つけて修正できるように、再現を作成することができました。

データベーススキーマを操作することで、バグを非表示にできます。無関係のフィルターされたインデックスを削除するか、フィルターを削除すると、バグが非表示になります。詳しくは接続アイテムをご覧ください。

接続項目には、DBCC CHECKDBがビューの内容を検証するために使用する内部クエリも含まれています。結果は返されず、これがバグであることを示しています。

このバグは一部のリリースで修正されています。 SQL Server 2014 SP2 CU 5ではそれを再現できなくなりました。

6
usr