TL; DR: J'ai une corruption inoxyable dans une vue indexée. Voici les détails:
En cours
DBCC CHECKDB([DbName]) WITH EXTENDED_LOGICAL_CHECKS, DATA_PURITY, NO_INFOMSGS, ALL_ERRORMSGS
sur l'une de mes bases de données produit l'erreur suivante:
MSG 8907, Niveau 16, State 1, Ligne 1 L'indice spatial, l'indice XML ou l'affichage indexé 'Nom d'image' (ID d'objet 784109934) contient des lignes qui n'ont pas été produites par la définition de vue. Cela ne représente pas nécessairement une question d'intégrité avec les données de cette base de données. (...)
CheckDB a trouvé 0 erreurs d'allocation et 1 erreurs de consistance dans le tableau 'Viewname'.
réparateur_rebuild est le niveau de réparation minimum (...).
Je comprends que ce message indique que les données matérialisées de la vue indexée "Nom de la vue" ne sont pas identiques à la production de la requête sous-jacente. Cependant, la vérification manuelle des données ne constitue pas des divergences:
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
a été utilisé pour forcer l'utilisation de l'index (uniquement) sur ViewName
. FORCESCAN
a été utilisé pour empêcher la correspondance de la vue indexée de se produire. Le plan d'exécution confirme les deux mesures pour fonctionner.
Aucune lignée n'est renvoyée ici, ce qui signifie que les deux tables sont identiques. (Il n'y a que des colonnes entières et guidologiques, les collations n'entrent pas en jeu).
L'erreur ne peut pas être corrigée en recréant l'index sur la vue ou en exécutant DBCC CHECKDB REPAIR_ALLOW_DATA_LOSS
. Répéter les correctifs n'a également pas aidé. Pourquoi DBCC CHECKDB
Signaler cette erreur? Comment s'en débarrasser de cela?
(Même si la reconstruction de la reconstruction de ma question se poserait toujours - pourquoi une erreur est-elle rapportée bien que mes demandes de vérification des données fonctionnent avec succès?)
Mise à jour: Le bogue a été corrigé dans certaines sorties. Je ne peux plus le reproduire dans SQL Server 2014 SP2 CU 5. Le 2014 SP2 KB contient une solution sans ko article: Creating non-clustered index causes DBCC CheckDB With Extended_Logical_Checks to raise corruption error
. Les deux bugs de connexion à ce sujet ont été fermés:
Le processeur de requête peut produire un plan d'exécution non valide pour la requête (correcte) générée par DBCC pour vérifier que l'index de vue produit les mêmes lignes que la requête de vue sous-jacente.
Le plan produit par le processeur de requête gère incorrectement NULLs
pour la colonne ImageObjectID
. Il s'agit de raisons de manière incorrecte que la requête de vue rejette NULLs
pour cette colonne, quand ce n'est pas le cas. Penser que NULLs
sont exclus, il est capable de faire correspondre l'index filtré non clusterisé sur la table Users
qui filtre sur ImageObjectID IS NOT NULL
.
En produisant un plan qui utilise cet index filtré, il garantit que les lignes avec NULL
in ImageObjectID
ne sont pas rencontrées. Ces lignes sont retournées (correctement) à partir de l'index de vue, il apparaît donc qu'il y a une corruption quand il n'y a pas.
La définition de vue est:
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
La comparaison ON
Clause d'égalité entre AdminUserID
et ID
rejette NULLs
dans ces colonnes, mais pas à partir de la colonne ImageObjectID
.
Une partie de la requête générée par DBCC est la suivante:
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);
Il s'agit de code générique qui compare les valeurs dans une mode NULL
- C'est certainement verbeux, mais la logique va bien.
Le raisonnement du processeur de requête signifie qu'un plan de requête utilisant incorrectement l'indice filtré peut être produit, comme dans le fragment de plan d'exemple ci-dessous:
La requête DBCC prend une trajectoire de code différente via le processeur de requêtes des requêtes de l'utilisateur. Ce chemin de code contient le bogue. Lorsqu'un plan utilisant l'indice filtré est généré, il ne peut pas être utilisé avec le USE PLAN
Astuce Pour forcer ce plan forme avec le même texte de requête soumis à partir d'une connexion de base de données utilisateur.
Le chemin principal du code d'optimisation (pour les requêtes des utilisateurs) ne contient pas ce bogue, il est donc spécifique aux requêtes internes telles que celles générées par DBCC.
Une autre enquête montre qu'il s'agit d'un bogue dans DBCC CheckDB. Un bogue Microsoft Connect a été ouvert: erreur à checkdb DBCC injustifiée (qui est aussi faux positif et sinon étrange) . Heureusement, j'ai pu produire une reproduction afin que le bogue puisse être trouvé et fixé.
Le bogue peut être masqué en jouant avec le schéma de la base de données. Suppression d'un index filtré sans rapport, ou supprimant le filtre, cache le bogue. Pour plus de détails, veuillez consulter l'élément Connect.
L'élément Connect contient également la requête interne que DBCC CheckDB utilise pour valider le contenu de la vue. Il ne renvoie aucun résultat, montrant que ceci est un bug.
Le bogue a été corrigé dans certaines sorties. Je ne peux plus le reproduire dans SQL Server 2014 SP2 CU 5.