The query processor can produce an invalid execution plan for the (correct) query generated by DBCC to check that the view index produces the same rows as the underlying view query.
The plan produced by the query processor incorrectly handles NULLs
for the ImageObjectID
column. It incorrectly reasons that the view query rejects NULLs
for this column, when it does not. Thinking that NULLs
are excluded, it is able to match the filtered nonclustered index on the Users
table that filters on ImageObjectID IS NOT NULL
.
By producing a plan that uses this filtered index, it ensures that rows with NULL
in ImageObjectID
are not encountered. These rows are returned (correctly) from the view index, so it appears there is a corruption when there is not.
The view definition is:
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
The ON
clause equality comparison between AdminUserID
and ID
rejects NULLs
in those columns, but not from the ImageObjectID
column.
Part of the DBCC generated query is:
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);
This is generic code that compares values in a NULL
-aware fashion. It is certainly verbose, but the logic is fine.
The bug in the query processor's reasoning means that a query plan that incorrectly uses the filtered index may be produced, as in the example plan fragment below:
The DBCC query takes a different code path through the query processor from user queries. This code path contains the bug. When a plan using the filtered index is generated, it cannot be used with the USE PLAN
hint to force that plan shape with the same query text submitted from a user database connection.
The main optimizer code path (for user queries) does not contain this bug, so it is specific to internal queries like those generated by DBCC.
There is an internal snapshot that SQL Server will try to create when you run certain DBCC commands. There is really good information about this on the BOL reference for DBCC.
When you execute one of these DBCC commands, the Database Engine
creates a database snapshot and brings it to a transactionally
consistent state. The DBCC command then runs the checks against this
snapshot. After the DBCC command is completed, this snapshot is
dropped.
Sometimes an internal database snapshot is not required or cannot be
created. When this occurs, the DBCC command executes against the
actual database. If the database is online, the DBCC command uses
table-locking to ensure the consistency of the objects that it is
checking. This behavior is the same as if the WITH TABLOCK option were
specified.
The master database is particularly interesting with this, as if the internal snapshot can't be created then DBCC CHECKDB will fail against master. As per the above reference:
DBCC CHECKDB fails when it is run against master if an internal database snapshot cannot be created.
So, what can cause the internal snapshot creation to fail? The above reference has a nice list:
An internal database snapshot is not created when a DBCC command is
executed:
Against master, and the instance of SQL Server is running in single-user mode.
Against a database other than master, but the database has been put in single-user mode by using the ALTER DATABASE statement.
Against a read-only database.
Against a database that has been set in emergency mode by using the ALTER DATABASE statement.
Against tempdb. In this case, a database snapshot cannot be created because of internal restrictions.
Using the WITH TABLOCK option. In this case, DBCC honors the request by not creating a database snapshot.
The DBCC commands use table locks instead of the internal database
snapshots when the command is executed against the following:
My recommendation is to look at that information and find which one is applying to your situation. That should answer your question and move towards the resolution.
Best Answer
The Check Database Integrity Task provided in the maintenance plan issue DBCC CHECKDB WITH NO_INFOMSGS on the database selected. You can view its command by clicking the view-SQL in the task setup. If you doubt the generated SQL command, you can use SQL profiler to see its SQL command. If corruption was found, the agent job with this maintenance task will generate error and fail (with proper job step setup).
One thing to point out, running DBCC CHECKDB is equal as performing DBCC CHECKALLOC, DBCC CHECKTABLE, DBCC CHECKCATALOG and other validation. If you are running DBCC CHECKDB, you do not have to run them separately. Running them separately usually is to perform specific integrity check or need to spread out the integrity check to smaller task due to limited time to perform entire DBCC CHECKDB. More information can be found here on MSDN.