Smart DBCC CHECKDB Scheduling with AI

DBCC CHECKDB is the integrity check every DBA knows they should run and many struggle to schedule. The problem is straightforward: you have a 4 TB database, CHECKDB takes 6 hours, your maintenance window is 4 hours, and you have 40 other databases that also need checking. Something has to give — and too often what gives is that CHECKDB stops running and nobody notices until an audit or an actual corruption event.

I wanted an intelligent scheduling solution that could fit all my integrity checks into the available maintenance windows without manual spreadsheet planning. Not a replacement for Ola Hallengren’s maintenance solution — that’s still the gold standard for the execution side. This is the scheduling layer that decides when each database gets checked and what kind of check it gets.

Here’s the prompt I started with:

What the Agent Produced

The agent built a two-function solution: New-CheckDbSchedule for planning and Invoke-CheckDbSchedule for execution. The planning function was the interesting part.

It started by pulling database sizes from sys.master_files (summing data and log file sizes) and joining that against the history table. The duration estimation used a weighted average of the last three runs, falling back to the 1 hour per 100 GB heuristic for databases with no history.

What it got right:

  • The VLDB threshold was parameterized. Not hardcoded at 500 GB — a $VldbThresholdGB parameter defaulting to 500.
  • Weekend window detection used [DayOfWeek] comparison, so the wider Saturday/Sunday windows were automatically available.
  • Priority ordering sorted by “days since last CHECKDB” descending, so the most overdue databases got scheduled first.

What needed fixing:

  • It didn’t account for concurrent checks. The initial version packed databases sequentially into the window. In practice, I can run 2–3 CHECKDBs concurrently if they’re on different databases and the I/O subsystem can handle it.
  • The history table schema was assumed. I needed to define the actual table and the logging mechanism, not just read from it.
  • No alerting on corruption. Running CHECKDB without checking the results is worse than not running it — it gives you false confidence.

The Iteration

The Final Solution

The history table creation (run once per instance):

What I Validated

Before deploying this across the fleet, I ran New-CheckDbSchedule with -ReportOnly to inspect the plan:

  1. VLDB scheduling works. A 1.2 TB database was scheduled for PHYSICAL_ONLY on Wednesday night (estimated 40 minutes) and a full check on Saturday (estimated 6 hours). The weeknight check catches physical corruption fast; the weekend check catches logical corruption.
  2. Priority ordering is correct. Databases that hadn’t been checked in 30+ days were scheduled first. A small 2 GB database with no history got a DaysSinceCheck of 999 and went to the front of the queue.
  3. The 1 hour per 100 GB estimate is conservative. For databases with SSD storage, actual CHECKDB runs faster. After a few weeks of history, the averages calibrate to your actual hardware. Until then, the conservative estimate avoids overbooking.

One thing to note: this solution schedules the work, but the execution depends on a SQL Agent job or scheduled task that calls Invoke-CheckDbSchedule nightly. I have a single agent job that runs New-CheckDbSchedule | Invoke-CheckDbSchedule at the start of each maintenance window.

For production environments, I’d still recommend Ola Hallengren’s maintenance solution for the CHECKDB execution itself — it handles edge cases like databases in standby mode, read-only filegroups, and AG secondaries better than a custom script. This scheduling layer sits on top and answers the question Ola’s solution doesn’t: which databases should I check tonight?

For more on building custom monitoring solutions, see Post 13: Building Custom Monitoring. For the PowerShell patterns used here, see Post 6: PowerShell Automation.

Try This Yourself

Start by running just the inventory and estimation piece. Pull database sizes and CHECKDB history from your instances and see what the scheduling algorithm produces. You’ll probably find databases that haven’t been checked in months — or databases where CHECKDB is running every night when weekly would be fine.

Then customize. Ask the agent to add support for DBCC CHECKDB WITH EXTENDED_LOGICAL_CHECKS for databases with indexed views or computed columns. Ask it to exclude databases in a restoring state or databases under a certain size threshold. Ask it to generate a weekly email summary showing CHECKDB coverage across the fleet — which databases were checked, which are overdue, and what the estimated backlog looks like.

The goal is to move from “CHECKDB runs when the maintenance plan gets around to it” to “every database is checked on a predictable schedule that fits within my maintenance windows.”


Part of the ALTER DBA ADD AGENT series.