Suspected Corruption

What this check looks for

Rows in msdb..suspect_pages with a last_update_date in the last 7 days, reported per database.

suspect_pages is written by SQL Server itself whenever a page fails verification during normal operation. It is not a report that something might be wrong. It is a record that something was wrong, at a specific page, at a specific time.

Why it matters

This is the engine telling you it could not read your data correctly, and it is the single most urgent thing the Quick Scan can report.

The event_type column says what happened, and every value is serious:

event_type Meaning
1 An 823 error: the operating system refused the read or write outright.
2 An 824 error: the read succeeded but the page was wrong, a bad checksum or a torn page.
3 A restore failed.
4 A repair, from DBCC CHECKDB with a repair option.
5 The page was deallocated by a repair.
7 The page was restored or repaired successfully.

Error 825 deserves separate mention because it is the one that arrives before the others. It means a read failed and then succeeded on a retry. SQL Server carries on, the query returns, nobody is told, and the only trace is a line in the error log. Storage that needs retries is storage that is about to stop retrying successfully. There is a separate check for the 800 series errors in the error log for exactly this reason.

The clock that matters here is your backup retention. Every backup taken after the damage contains the damage, so the window in which you still hold a clean copy is closing on a schedule nobody is watching.

Corruption is almost always a storage problem. The database is the thing that noticed, not the thing that caused it.

How to confirm it yourself

SELECT db.[name]                AS [database_name],
       sp.[file_id],
       sp.[page_id],
       sp.[event_type],
       CASE sp.[event_type]
            WHEN 1 THEN '823 error, read or write failed'
            WHEN 2 THEN '824 error, bad checksum or torn page'
            WHEN 3 THEN 'restore failed'
            WHEN 4 THEN 'repaired by DBCC'
            WHEN 5 THEN 'deallocated by DBCC repair'
            WHEN 7 THEN 'restored or repaired successfully'
            ELSE 'unknown' END  AS [meaning],
       sp.[error_count],
       sp.[last_update_date]
  FROM msdb..suspect_pages AS sp WITH (NOLOCK)
  LEFT JOIN sys.databases AS db WITH (NOLOCK)
         ON db.[database_id] = sp.[database_id]
 ORDER BY sp.[last_update_date] DESC;

Find out which object each page belongs to, which decides how much you lose:

DBCC TRACEON (3604);
DBCC PAGE ('YourDatabase', 1, 123456, 0);   -- file_id and page_id from above

And run the full check, which finds everything rather than only what has been read:

DBCC CHECKDB ('YourDatabase') WITH NO_INFOMSGS, ALL_ERRORMSGS;

Then establish your deadline:

DBCC DBINFO ('YourDatabase') WITH TABLERESULTS;   -- read dbi_dbccLastKnownGood

How to fix it

Do not restart SQL Server, do not detach the database, and do not reach for a repair option. All three are instincts and all three can turn a recoverable situation into an unrecoverable one.

  1. Find your last clean backup. Restore it elsewhere and run DBCC CHECKDB against the copy. Work backwards until one comes back clean. This is the step with the deadline.
  2. Read the CHECKDB output properly. It names the object and the index. Damage confined to a nonclustered index costs nothing: rebuild that index and it is gone. Index id 0 is a heap and index id 1 is the clustered index, and both of those are the data itself.
  3. Use page level restore where you can. With a few damaged pages, a full recovery database and an unbroken log chain, this recovers just those pages with the database online. It is an Enterprise edition feature:
RESTORE DATABASE [YourDatabase] PAGE = '1:123456'
  FROM DISK = N'D:\Backups\YourDatabase_full.bak' WITH NORECOVERY;
-- then the log backups since, then RESTORE LOG ... WITH RECOVERY
  1. Restore rather than repair. REPAIR_ALLOW_DATA_LOSS does what its name says: it deallocates what it cannot fix. It is the option when there is no backup, not the first move.
  2. Find the storage fault. Check the Windows system event log for disk errors and talk to whoever owns the storage. Hardware that corrupted once will do it again.

If you are not confident, this is the point to get help. Stedman Solutions does this work: databasehealth.com.

How long it takes

Four hours for a typical investigation. A full restore takes as long as the database is large, and finding the storage fault is separate work.


Report Why you would go there
Suspect Pages Every recorded page, with when and how, rather than the last 7 days.
Last DBCC CheckDB Known Good by Database When each database was last clean, which sets your deadline.
Backup Status What you have to restore from.
Recovery Exposure What restoring would cost in data and time.
Error Log The 823, 824 and 825 entries around the event.
I/O by Drive The storage underneath, which is usually the cause.
Check
DBCC CHECKDB Corruption Errors Found The same damage found by a scheduled check rather than by a read.
SQL Server errors 823, 824 and 825 The error log side, including the 825 warning that comes first.
DBCC CheckDB never run Instances where nothing would ever find this.
Page verify option Whether SQL Server would notice the next one.
Dangerous SQL Server Version Builds where the engine itself can cause this.

Frequently asked questions

CHECKDB passes now. Was it a false alarm? Almost never. A transient fault can produce a real error that a later read does not reproduce, but the page was still wrong when it was read. Treat it as a storage warning.

Can I just clear the suspect_pages table? You can delete from it, and it is the wrong instinct. It is the record you would need, and page level restore reads it. Rows are cleaned up automatically once a page is fixed.

The corruption is in tempdb. Restart SQL Server, which rebuilds tempdb, then investigate the storage under it. Not a data loss event, the same hardware signal.

Why seven days? So the check reports something current rather than a repair from three years ago. The query above shows the whole table.