The first time you run SqlMonitorUI, it creates a file called sql-checks.json in the same folder as the executable.
If running from Visual Studio (F5):
SqlMonitorUI\bin\Debug\net8.0-windows\sql-checks.json
If running from command line (dotnet run):
SqlMonitorUI\sql-checks.json
After publishing:
(wherever you published)\sql-checks.json
This creates the sql-checks.json file with 12 default checks.
Open sql-checks.json in any text editor (Notepad, VS Code, Visual Studio, etc.)
See examples below!
Your changes will be loaded automatically.
Change backup age from 7 days to 3 days:
Find this check in sql-checks.json:
{
"id": "BACKUP_001",
"name": "Full Backup Recency",
"description": "Checks if any database hasn't had a full backup in the last 7 days",
"category": "Backup",
"severity": "Critical",
"sqlQuery": "SELECT CASE WHEN EXISTS (SELECT 1 FROM sys.databases d LEFT JOIN msdb.dbo.backupset b ON d.name = b.database_name AND b.type = 'D' WHERE d.database_id > 4 AND d.state = 0 AND (b.backup_finish_date IS NULL OR b.backup_finish_date < DATEADD(DAY, -7, GETDATE()))) THEN 1 ELSE 0 END",
"expectedValue": 0,
"enabled": true,
"recommendedAction": "Schedule full backups for databases that haven't been backed up in 7+ days"
}Change -7 to -3:
"sqlQuery": "SELECT CASE WHEN EXISTS (SELECT 1 FROM sys.databases d LEFT JOIN msdb.dbo.backupset b ON d.name = b.database_name AND b.type = 'D' WHERE d.database_id > 4 AND d.state = 0 AND (b.backup_finish_date IS NULL OR b.backup_finish_date < DATEADD(DAY, -3, GETDATE()))) THEN 1 ELSE 0 END",Also update the description:
"description": "Checks if any database hasn't had a full backup in the last 3 days",Add this to the array in sql-checks.json:
{
"id": "CUSTOM_001",
"name": "Page Verify CHECKSUM",
"description": "Ensures all databases use PAGE_VERIFY CHECKSUM for corruption detection",
"category": "Reliability",
"severity": "Warning",
"sqlQuery": "SELECT CASE WHEN EXISTS (SELECT 1 FROM sys.databases WHERE page_verify_option <> 2 AND database_id > 4) THEN 1 ELSE 0 END",
"expectedValue": 0,
"enabled": true,
"recommendedAction": "Set PAGE_VERIFY = CHECKSUM: ALTER DATABASE [DbName] SET PAGE_VERIFY CHECKSUM"
}Important: Don't forget the comma between checks!
Set enabled to false:
{
"id": "SECURITY_001",
"name": "SA Account Enabled",
"enabled": false, <-- Change this
...
}The check will still be in the file but won't run.
Make a check more or less important:
{
"id": "CONFIG_001",
"name": "Auto Close Enabled",
"severity": "Critical", <-- Change from "Warning" to "Critical"
...
}Valid severities: Critical, Warning, Info
Template:
SELECT CASE
WHEN [your condition that indicates a problem]
THEN 1 -- Failed
ELSE 0 -- Passed
END1. Check for Databases Without Regular Backups:
{
"id": "BACKUP_003",
"name": "Database Never Backed Up",
"description": "Finds databases that have NEVER had a backup",
"category": "Backup",
"severity": "Critical",
"sqlQuery": "SELECT CASE WHEN EXISTS (SELECT 1 FROM sys.databases d WHERE d.database_id > 4 AND d.state = 0 AND NOT EXISTS (SELECT 1 FROM msdb.dbo.backupset b WHERE b.database_name = d.name)) THEN 1 ELSE 0 END",
"expectedValue": 0,
"enabled": true,
"recommendedAction": "Perform a full backup immediately"
}2. Check for Databases in Simple Recovery Mode:
{
"id": "RECOVERY_001",
"name": "Production DB in Simple Recovery",
"description": "Production databases should use FULL recovery model",
"category": "Backup",
"severity": "Critical",
"sqlQuery": "SELECT CASE WHEN EXISTS (SELECT 1 FROM sys.databases WHERE recovery_model = 3 AND name LIKE '%prod%' AND database_id > 4) THEN 1 ELSE 0 END",
"expectedValue": 0,
"enabled": true,
"recommendedAction": "ALTER DATABASE [DbName] SET RECOVERY FULL"
}3. Check for Old Statistics:
{
"id": "STATS_001",
"name": "Statistics Not Updated",
"description": "Finds statistics not updated in 7+ days",
"category": "Performance",
"severity": "Warning",
"sqlQuery": "SELECT CASE WHEN EXISTS (SELECT 1 FROM sys.stats s CROSS APPLY sys.dm_db_stats_properties(s.object_id, s.stats_id) sp WHERE sp.last_updated < DATEADD(DAY, -7, GETDATE()) AND sp.rows > 10000) THEN 1 ELSE 0 END",
"expectedValue": 0,
"enabled": true,
"recommendedAction": "Update statistics: UPDATE STATISTICS [TableName]"
}4. Check for Blocking:
{
"id": "BLOCKING_001",
"name": "Active Blocking",
"description": "Detects sessions blocking others for 30+ seconds",
"category": "Performance",
"severity": "Critical",
"sqlQuery": "SELECT CASE WHEN EXISTS (SELECT 1 FROM sys.dm_exec_requests WHERE blocking_session_id <> 0 AND wait_time > 30000) THEN 1 ELSE 0 END",
"expectedValue": 0,
"enabled": true,
"recommendedAction": "Investigate with sp_WhoIsActive or Activity Monitor"
}5. Check for Unlimited File Growth:
{
"id": "GROWTH_002",
"name": "Unlimited File Growth",
"description": "Database files should have max size limits",
"category": "Configuration",
"severity": "Warning",
"sqlQuery": "SELECT CASE WHEN EXISTS (SELECT 1 FROM sys.master_files WHERE max_size = -1 AND database_id > 4 AND type = 0) THEN 1 ELSE 0 END",
"expectedValue": 0,
"enabled": true,
"recommendedAction": "Set MAXSIZE on data files to prevent disk space issues"
}[
{
"id": "UNIQUE_ID",
"name": "Check Display Name",
"description": "What this check does",
"category": "Backup|Security|Performance|Configuration|Integrity",
"severity": "Critical|Warning|Info",
"sqlQuery": "SELECT CASE WHEN ... THEN 1 ELSE 0 END",
"expectedValue": 0,
"enabled": true,
"recommendedAction": "What to do if it fails"
},
{
"id": "NEXT_CHECK",
...
}
]- App starts β Reads
sql-checks.json - You click "Run Checks" β Runs all
enabled: truechecks - Results display β Shows check name, category, severity
Changes to the JSON file take effect immediately the next time you click "Run Checks". No need to recompile!
Before adding to the JSON, test in SQL Server Management Studio:
-- Your check SQL here
SELECT CASE
WHEN EXISTS (...)
THEN 1 -- Problem found
ELSE 0 -- All good
ENDShould return either 0 or 1.
Create custom categories:
"category": "Compliance",
"category": "Custom",
"category": "Daily Checks",They'll show up in the sidebar automatically!
Use a naming pattern:
BACKUP_001,BACKUP_002, etc.CUSTOM_001,CUSTOM_002, etc.PERF_001,PERF_002, etc.
Copy sql-checks.json somewhere safe before making major changes!
{
"id": "CHECK_001",
...
} <-- Missing comma here!
{
"id": "CHECK_002",-- Bad - returns multiple columns
SELECT name, state FROM sys.databases
-- Good - returns single value
SELECT CASE WHEN EXISTS (...) THEN 1 ELSE 0 ENDUse a JSON validator if unsure: https://jsonlint.com/
Test in SSMS first!
- Decide what you want to check
- Write SQL in SSMS that returns 0/1
- Copy SQL into a new check in
sql-checks.json - Save the file
- Run the app and click "Run Checks"
- Verify it works!
YourProject\SqlMonitorUI\bin\Debug\net8.0-windows\sql-checks.json
Put this in your user folder and point to it:
C:\Users\YourName\.sqlmonitor\sql-checks.json
Then modify CheckRepository.cs to look there first.
Want different checks for different servers? You can:
- Create
sql-checks-prod.json,sql-checks-dev.json, etc. - Modify the code to accept a parameter for which file to load
- Or just swap files before running
Happy monitoring! π