Неисправимое повреждение DBCC CHECKDB: индексированное представление содержит строки, которые не были созданы определением представления

14

TL; DR: у меня есть нефиксированное искажение в индексированном представлении. Вот подробности:


Бег

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

на одной из моих баз выдает следующую ошибку:

Сообщение 8907, уровень 16, состояние 1, строка 1 Пространственный индекс, индекс XML или индексированное представление ViewName (идентификатор объекта 784109934) содержит строки, которые не были созданы определением представления. Это не обязательно представляет проблему целостности данных в этой базе данных. (...)

CHECKDB обнаружил 0 ошибок размещения и 1 ошибок согласованности в таблице «ViewName».

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был использован для предотвращения совпадения индексированного представления. План выполнения подтверждает, что обе меры работают.

Здесь не возвращаются строки, это означает, что две таблицы идентичны. (Есть только целочисленные столбцы и столбцы guid, параметры сортировки не вступают в игру).

Ошибка не может быть исправлена путем воссоздания индекса в представлении или выполнения DBCC CHECKDB REPAIR_ALLOW_DATA_LOSS. Повторение исправлений также не помогло. Почему DBCC CHECKDBсообщает об этой ошибке? Как от этого избавиться?

(Даже если восстановление будет исправлено, мой вопрос не изменится - почему выдается сообщение об ошибке, хотя мои запросы на проверку данных выполняются успешно?)


Обновление: ошибка была исправлена ​​в некоторых выпусках. Я больше не может воспроизвести его в SQL Server 2014 с пакетом обновления 2 CU 5. 2014 SP2 KB содержит исправление без KB статьи: Creating non-clustered index causes DBCC CheckDB With Extended_Logical_Checks to raise corruption error. Две ошибки подключения по этому поводу были закрыты:

USR
источник
1
Вы говорите, что удалили и заново создали индекс в представлении, а DBCC CHECKDB по-прежнему сообщает об ошибке? Как насчет того, чтобы отбросить представление и создать его с нуля?
Аарон Бертран
From BOL: Устранение ошибок DBCC при индексированных представлениях If the indexed view does not contain an aggregate over values of type float or real and you receive errors 8907 or 8708, drop the index on the view and re-create it. Do not use ALTER INDEX REBUILD to try to remove the differences between the stored and the computed view, because ALTER INDEX REBUILD does not recalculate the view before rebuilding the index. Then run DBCC CHECKTABLE on the View to verify no differences remain.
Кин Шах,
@Kin Я отредактировал твой комментарий. [1]Нотация не работает в замечании наценке вниз.
Аарон Бертран
Я воссоздал все. Я также позволил DBCC CHECKDB REPAIR_ALLOW_DATA_LOSS работать. Это сказало, что это восстановило представление, но затем это сообщило ту же самую ошибку.
USR
Можете ли вы показать определение вида (если здесь слишком долго, то в поле для вставки)?
Аарон Бертран

Ответы:

14

Обработчик запросов может создать неверный план выполнения для (правильного) запроса, сгенерированного 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

ONСравнение положения равенства между AdminUserIDи 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привычной манере. Это, конечно, многословно, но логика в порядке.

Ошибка в рассуждении обработчика запросов означает, что план запроса, который неправильно использует отфильтрованный индекс, может быть создан, как в приведенном ниже фрагменте плана:

Ошибочный план

Запрос DBCC принимает другой путь кода через обработчик запросов от пользовательских запросов. Этот путь кода содержит ошибку. Когда создается план с использованием отфильтрованного индекса, его нельзя использовать с USE PLANподсказкой для принудительного применения этой формы плана с помощью того же текста запроса, который был отправлен из подключения к пользовательской базе данных.

Основной путь кода оптимизатора (для пользовательских запросов) не содержит этой ошибки, поэтому он специфичен для внутренних запросов, например, сгенерированных DBCC.

Пол Уайт 9
источник
Я могу увидеть ошибочный план в событии SQL Profiler Showplan XML. Я отмечу это как ответ .; Почему DBCC строит запрос иначе, чем обычный обработчик запросов ?; Я добавлю ссылку на этот ответ на элемент подключения.
USR
2
@usr DBCC делает все, что невозможно при подключении пользователя. Я полагаю, что это работает таким образом, потому что это должно, но вы должны попросить кого-то, как Пол Рэндал, узнать реальные подробности этого. Конечно, он не может сказать, что хочет. Я знаю, что есть много вещей вне DBCC, которые делают даже более странные вещи; некоторые даже строят план выполнения, даже не проходя через оптимизатор!
Пол Уайт 9
6

Дальнейшее расследование показывает, что это ошибка в DBCC CHECKDB. Была открыта ошибка Microsoft Connect: нефиксированная ошибка DBCC CHECKDB (которая также является ложно-положительной и в других отношениях странной) . К счастью, мне удалось создать репродукцию, чтобы ошибка была найдена и исправлена.

Ошибка может быть скрыта, играя со схемой базы данных. Удаление несвязанного отфильтрованного индекса или удаление фильтра скрывает ошибку. Для получения подробной информации, пожалуйста, смотрите пункт подключения.

Элемент подключения также содержит внутренний запрос, который DBCC CHECKDB использует для проверки содержимого представления. Он не возвращает результатов, показывая, что это ошибка.

Ошибка была исправлена ​​в некоторых выпусках. Я больше не могу воспроизвести его в SQL Server 2014 SP2 CU 5.

USR
источник
Для воспроизведения ошибки потребовалось много (производственных) данных (что является еще одним доказательством того, что причиной может быть изменение плана). Мне неудобно публиковать данные, хотя я смог удалить все, кроме двух столбцов из каждой таблицы. Проблема, с которой вы связаны, требует повреждения представления. Я воссоздаю представление, поэтому причиной может быть отсутствие искажения из-за DML .; Известно ли вам что-нибудь, что может вызвать другой план, если запрос выполняется под управлением DBCC CHECKDB, а не в обычном окне запроса?
USR
Анонимная база данных была только что загружена. Вот скрипт, который перестраивает все индексы и воссоздает представление: pastebin.com/jPEALeEw (полезно для сброса всего и проверки правильности физической структуры). Другие полезные скрипты: pastebin.com/KxNSwm2J Скрипты должны просто запуститься, и проблема должна быть немедленно воспроизведена.
USR
Зеркало .bak
usr
11.0.3349 с -T272,4199,3604. Исправлено 4199 исправлений процессора запросов. Я только что удалил этот TF .; Может быть, нам нужно выработать правильный план запроса. Теперь я установил 1 ГБ ОЗУ и перезапустил экземпляр (было 8 ГБ). Это изменило одно из двух слияний, которые я видел в NLJ. Все еще репр .; Чтобы попробовать некоторые варианты плана, я добавил и удалил строки: pastebin.com/y972Sx4d Ошибка, по-видимому, срабатывает, если я получаю соединение слиянием или хэш в части запроса «левый анти-полусоединение». Попробуйте это: добавьте 100 тысяч строк в Users. Это надежно дает (параллельное) хеш-соединение для меня.
USR
Я только что загрузил "plan.zip "в элемент подключения, который содержит различные планы выполнения для запроса DBCC CHECKDB. С разным количеством строк в университетах я могу создать как минимум три разных плана. Только с планом соединений цикла проблема не возникает. При объединении слиянием и хэшем ошибка воспроизводима.
USR