Server Health Report – RockyPC – 2026-05-16
🎯 Tuning Goal: Server HealthExecutive Summary
This report analyzes the overall health of RockyPC, a single-instance SQL Server 2022 Developer Edition running on a 16-core Windows 10 workstation with 32 GB RAM. The instance hosts several databases including SQLStorm, WideWorldImporters, tpch, tpch10, tpcc, CRM, and DigitsSolver.
The server has been online for less than one day, so wait statistics and I/O metrics reflect a short observation window. Despite this, several significant operational, integrity, and configuration concerns were identified. The most urgent findings are:
PWAIT_DIRECTLOGCONSUMER_GETNEXT,
QDS_PERSIST_TASK_MAIN_LOOP_SLEEP, QDS_ASYNC_QUEUE) are benign background
waits. HTMEMO (1.65%) is a normal in-memory hash join wait. No user-query blocking
patterns detected.
Top Priorities
| # | Issue | Severity | Category | Confidence |
|---|---|---|---|---|
| 1 | CHECKDB never run or overdue on all 7 user databases | Critical | Integrity | High 95% |
| 2 | No backups for most databases; FULL recovery databases have no log backups | Critical | Backup/Recovery | High 95% |
| 3 | WideWorldImporters on compatibility level 130 — missing SQL 2022 IQP features | High | Configuration | High 90% |
| 4 | optimize for ad hoc workloads disabled — plan cache pollution risk | High | Configuration | High 92% |
| 5 | Backup compression and checksum defaults disabled | Medium | Configuration | High 95% |
| 6 | WideWorldImporters last full backup is 1,316 days old | High | Backup/Recovery | High 95% |
| 7 | Query Store flush interval non-standard on WideWorldImporters (50 min) | Medium | Query Store | Medium 85% |
| 8 | tempdb metadata memory-optimized disabled — potential contention under load | Medium | Performance | Medium 80% |
| 9 | SQL Server 2022 at RTM-GDR (KB5091158) — verify patch currency | Medium | Operational | Medium 80% |
| 10 | System databases (master, msdb, model) have no backups recorded | High | Backup/Recovery | High 95% |
1. Performance Improvements
The setting tempdb metadata memory-optimized is currently disabled (0).
On SQL Server 2022 with an Enterprise engine, enabling this feature moves system table metadata
(such as sysobjects, sysrowsets, sysallocunits) to
memory-optimized tables, dramatically reducing latch contention under heavy tempdb usage.
While current tempdb I/O is very low, enabling this proactively prevents future bottlenecks as
workloads grow.
Action: Enable via sp_configure and restart the SQL Server service.
See the Scripts section for the configuration change.
The top four wait types are all normal background waits with no user query impact:
- PWAIT_DIRECTLOGCONSUMER_GETNEXT (42.56%): Query Store log consumer waiting for new log records. Normal and expected when Query Store is active.
- QDS_PERSIST_TASK_MAIN_LOOP_SLEEP (42.37%): Query Store persistence task sleeping between flush cycles. Completely benign.
- QDS_ASYNC_QUEUE (12.48%): Query Store asynchronous background queue. Normal when Query Store is enabled.
- HTMEMO (1.65%): In-memory hash join memoization wait. Normal for analytical queries (tpch/tpcc/tpch10 workloads).
No CPU pressure, memory pressure, or I/O bottleneck waits are present. The server is in excellent performance health during this short observation window.
All database files show average I/O latencies of 0–1 ms, well within best-practice thresholds
(<5 ms for SSD, <20 ms for spinning disk). Data and log files are appropriately separated
between C:\Data\ and C:\Logs\. The SQLStorm MDF file
has the highest read volume (128 MB, 1,554 reads) and remains at 1 ms average — no concern.
Observation: System database files (master, msdb, model, tempdb) are all stored
under C:\Data\MSSQL16.MSSQLSERVER\MSSQL\DATA\ on the same volume as user databases.
This is acceptable for a developer workstation but would be a concern in production.
tempdb Configuration: 8 data files (tempdev + temp2–temp8) are configured, which is excellent and matches the logical CPU count guideline (8 = max recommended for 16 cores).
max server memory is set to 23,168 MB (~71% of 32,527 MB total
RAM). Current committed memory is only 1,510 MB (committed target: 7,872 MB),
reflecting the recent restart. The 23 GB ceiling is reasonable for a developer machine that also
runs an OS and other applications. No action required unless memory pressure appears after
workloads warm up. Monitor sys.dm_os_memory_clerks after the instance has been
running for several hours.
2. Configuration Optimizations
Currently set to 0 (disabled). When disabled, the first execution of any ad hoc query stores the full compiled plan in the plan cache, consuming buffer pool memory even for single-use queries. Enabling this setting stores only a stub on first execution, dramatically reducing plan cache bloat — a universal best practice with zero negative impact.
Action: See Scripts — "Enable Optimize for Ad Hoc Workloads".
backup compression default is disabled (0). Compressed backups
are significantly smaller (typically 50–70% reduction) and complete faster due to reduced I/O.
On SQL Server 2022 with the default algorithm (VDI-based), the CPU overhead is minimal on modern
hardware. This is a best-practice setting for all editions that support it.
Action: See Scripts — "Enable Backup Compression and Checksum Defaults".
backup checksum default is disabled (0). Backup checksums validate
backup integrity during both backup creation and restore verification, catching I/O corruption
early. This is a critical reliability feature with negligible performance impact.
Action: Included in the "Enable Backup Compression and Checksum Defaults" script.
WideWorldImporters is running at compatibility level 130 (SQL Server 2016). All other user databases are at 160 (SQL Server 2022). At level 130, this database misses out on significant features available in levels 140–160:
- Adaptive Query Processing (level 140+): adaptive joins, interleaved execution, memory grant feedback
- Intelligent Query Processing (level 150+): table variable deferred compilation, scalar UDF inlining, batch mode on rowstore
- SQL Server 2022 IQP (level 160): Parameter Sensitive Plan optimization, DOP feedback, CE feedback
Action: Test the workload in a staging environment first, then upgrade. See Scripts — "Upgrade WideWorldImporters Compatibility Level".
cost threshold for parallelism is set to 30 (non-default; the
default is 5). This is already a positive improvement over the default. For mixed OLTP/analytical
workloads (tpcc + tpch), a value of 30–50 is commonly recommended. The current value of 30
appears appropriate. Monitor for excessive parallelism under concurrent load and consider
increasing to 50 if needed.
MAXDOP is set to 8 on a 16-logical-core, single-socket machine.
This follows the standard Microsoft recommendation (cap at 8 for single-socket systems). No
changes needed unless specific query performance issues arise. DOP feedback (SQL 2022 level 160)
will automatically adjust per-query DOP over time for Query Store-tracked databases.
blocked process threshold is set to 0 (disabled). Setting this
to 5–10 seconds enables the Blocked Process Report extended event, which provides automatic
detection and reporting of blocking chains. Recommended for any active OLTP workload.
Action: See Scripts — "Enable Blocked Process Threshold".
3. Index Optimization Opportunities
The missing index DMV analysis across all databases returned no high-impact missing indexes. This is consistent with the server having been online for less than one day — the DMVs reset on service restart and insufficient query volume has accumulated to generate meaningful recommendations.
Recommendation: Re-run the Index Tuning goal analysis after the server has been under representative workload for at least 24–72 hours. At that point, the missing index DMVs will reflect actual query patterns. The Index Tuning goal in AI SQL Tuner Studio can be applied per-database (SQLStorm, tpcc, tpch, tpch10, WideWorldImporters, CRM, DigitsSolver) for a comprehensive analysis.
The presence of tpch and tpch10 (TPC-H benchmarks) indicates
analytical/reporting workloads with large table scans. These workloads are ideal candidates for
clustered or non-clustered columnstore indexes, which are available on the
Enterprise engine and can deliver 10–100x performance improvements for analytical queries.
The HTMEMO wait (hash join memoization) on these databases is consistent with
this workload profile.
Action: After running the Index Tuning goal for tpch and tpch10, specifically evaluate columnstore index opportunities on large fact tables.
4. Query Store Optimization
WideWorldImporters has a non-standard Query Store configuration compared to all other databases on this instance:
- Flush Interval: 50 minutes vs. 15 minutes (all others) — data loss window is 3× larger
- Stats Collection Interval: 15 minutes vs. 60 minutes (all others) — higher granularity is fine but inconsistent
- Max Plans Per Query: 1,000 vs. 200 (all others) — increases storage overhead
- Max Storage: 500 MB vs. 1,000 MB (all others) — may cap earlier under active workloads
Recommendation: Standardize to the consistent settings used by other databases: 15-min flush, 60-min stats interval, 200 max plans, 1,000 MB storage, and upgrade compatibility to 160. See Scripts — "Standardize WideWorldImporters Query Store Settings".
All six non-WWI databases have Query Store in READ_WRITE mode with AUTO cleanup and capture, 1,000 MB storage limit, 15-minute flush interval, and 30-day stale query threshold. Storage utilization is minimal (0.1–0.8%), indicating Query Store is functioning correctly and not under pressure.
Positive: All databases are at compatibility level 160, enabling full SQL Server 2022 IQP features including DOP Feedback, CE Feedback, and Parameter Sensitive Plan optimization — which rely on Query Store data to function.
Optional Enhancement: Consider enabling QUERY_CAPTURE_MODE = CUSTOM
with appropriate thresholds to filter out low-frequency, low-resource queries from storage,
keeping Query Store data focused on meaningful workloads. This is optional for a developer
environment but good practice.
5. Database Integrity Maintenance (CHECKDB)
sys.databases.create_date
integrity tracking. This must be rectified immediately.
The following databases have never had CHECKDB recorded (showing 1900-01-01):
- WideWorldImporters
- SQLStorm
- tpcc
- tpch
- CRM
- tpch10
DigitsSolver was last checked on April 12, 2026 (33 days ago — marginally overdue per the 30-day threshold). Re-run is recommended.
Recommended Schedule:
- Immediately: Run
DBCC CHECKDBwithPHYSICAL_ONLYon all databases to quickly detect physical corruption. - Weekly (or bi-weekly): Full
DBCC CHECKDB(all checks) on smaller databases (CRM, DigitsSolver, master, msdb, model). - Monthly: Full
DBCC CHECKDBon larger databases (WideWorldImporters, SQLStorm, tpcc, tpch, tpch10).
Note: This is a developer workstation — on production systems, CHECKDB should run at minimum weekly. Since this is Developer Edition, schedule via SQL Agent jobs or Windows Task Scheduler.
See Scripts — "Run DBCC CHECKDB on All User Databases" and "Schedule Weekly CHECKDB via SQL Agent".
6. Security Enhancements
All 10 active user sessions are encrypted. The one unencrypted session
(Session 67) belongs to NT SERVICE\SQLTELEMETRY running the SQL Server CEIP
(Customer Experience Improvement Program) via shared memory. As explicitly
noted in the analysis instructions, shared memory connections are local-only and carry
minimal risk — no user data is exposed. No action required.
Note: All connections use NTLM Windows Authentication over shared memory (local machine only). If remote TCP connections are added in future, ensure TLS is configured via SQL Server Configuration Manager with a valid certificate.
Only one sysadmin member exists: RockyPC\k_a_f (the local
Windows account, expected for a developer workstation). This follows least-privilege principles.
No built-in sa account or additional privileged accounts detected.
Best Practice Reminder: Periodically audit sysadmin membership with the script in the Scripts section. Ensure that if this machine is ever connected to a domain or shared environment, unnecessary principals are not added.
Integrated Security Only = No indicates the server is configured for Mixed
Mode Authentication (both Windows and SQL Server logins accepted). For a local developer
workstation this is common and acceptable, but it introduces the risk of weak SQL logins if any
are created.
Recommendation: Audit existing SQL Server logins and ensure the sa
account is disabled or has a strong password. See Scripts — "Audit SQL Logins and sa Account Status".
7. Concurrency Issues
No deadlocks were detected in the system health session extended events over the past 7 days. No deadlock analysis or Fix Deadlocks goal engagement is needed at this time.
As noted in Configuration (#7), blocked process threshold is currently 0.
Setting it to 5 seconds enables automatic blocked process report events that can be captured
via Extended Events or the system health session, making it easy to detect blocking chains
before they escalate.
8. Backup & Recovery
The following databases have no recorded full backup:
- CRM (FULL recovery — log growing, no protection)
- DigitsSolver (FULL recovery — log growing, no protection)
- tpcc (FULL recovery — log growing, no protection)
- tpch10 (FULL recovery — log growing, no protection)
- master, msdb, model (system databases)
WideWorldImporters: Last full backup was October 7, 2022 (1,316 days ago). This database is running in SIMPLE recovery, so no log backup is needed, but the backup is dangerously stale and must be refreshed.
SQLStorm: Last full backup was October 25, 2025 (202 days ago) in SIMPLE recovery. While somewhat stale, it is functional. Consider refreshing.
tpch: Last full backup was November 22, 2025 (174 days ago) in FULL recovery with no log backups. The transaction log has been growing unchecked since then — this can cause disk space exhaustion. Immediate action needed: either switch to SIMPLE recovery or implement log backup jobs.
See Scripts — "Full Backup All User Databases" and "Fix Full Recovery Log Backup Gap".
The following databases are in FULL recovery model but have no log backups. In this state, the transaction log cannot be truncated and will grow until disk space is exhausted. Additionally, point-in-time recovery (the primary benefit of FULL recovery) is not achievable without a continuous log backup chain:
- model (system DB — FULL recovery, no log backups)
- DigitsSolver
- CRM
- tpcc
- tpch
- tpch10
Decision Required: For each database, choose one of:
- Switch to SIMPLE recovery if point-in-time recovery is not needed (appropriate for most developer databases).
- Implement regular log backups (every 15–60 minutes) to enable point-in-time recovery and allow log truncation.
See Scripts — "Fix Full Recovery Log Backup Gap".
9. Operational Best Practices
The instance is running 16.0.1180.1 (RTM-GDR, KB5091158) dated April 16, 2026. This appears to be a very recent GDR patch. Verify that this is the latest available GDR or CU by checking the SQL Server 2022 build history. GDR releases contain critical security fixes only; Cumulative Updates (CUs) also include bug fixes and performance improvements. For a developer environment, tracking the latest CU is recommended.
Note: Product Update Level shows "(unknown)" — this may indicate the patch
level property is not set in this GDR. Confirm actual patch level via
SELECT @@VERSION.
SQL Server Agent is running (Agent XPs = 1). Leverage it to automate:
- DBCC CHECKDB — weekly/monthly schedule per database size
- Index maintenance — rebuild/reorganize based on fragmentation thresholds (Ola Hallengren's maintenance solution is recommended)
- Statistics update — weekly or after large data loads
- Database backups — daily full + log backups where applicable
- Query Store cleanup — Query Store AUTO cleanup is enabled but manual purge scripts can help after bulk test runs
This server runs Developer Edition, which has full Enterprise feature parity but is licensed for development and testing only. If any of these databases are used in a production capacity or are accessed by end users, the instance must be re-licensed with an appropriate Standard or Enterprise edition.
Database Mail XPs is disabled. Enabling Database Mail and configuring SQL Agent
alerts for severity 16+ errors, 823/824/825 I/O errors, and maintenance job failures provides
proactive notification of critical issues without requiring manual monitoring.
When running benchmark workloads (tpcc, tpch) that stress tempdb, enabling
tempdb metadata memory-optimized will prevent tempdb system table latch contention
under high concurrency. This requires a service restart but is non-disruptive on a developer machine.
Wait Analysis Reference
| Wait Type | % Total | Tasks | Total Wait | Max Wait | Assessment |
|---|---|---|---|---|---|
PWAIT_DIRECTLOGCONSUMER_GETNEXT |
42.56% | 1,096 | 90.41 min | 5,030 ms | Benign — QDS log consumer |
QDS_PERSIST_TASK_MAIN_LOOP_SLEEP |
42.37% | 91 | 90.02 min | 60,029 ms | Benign — QDS flush sleep |
QDS_ASYNC_QUEUE |
12.48% | 14 | 26.51 min | 408,799 ms | Benign — QDS background queue |
HTMEMO |
1.65% | 112 | 3.51 min | 15,732 ms | Benign — Hash join memoization |
Interpretation: The top three waits are entirely driven by Query Store background activity. The overwhelming dominance of QDS waits (97%) in the wait statistics immediately after server startup is expected — QDS initializes its log consumer threads, persistence tasks, and async queues on startup. These waits will normalize as user workload accumulates. No performance bottleneck is indicated.
I/O Analysis Reference
| Database | File | Reads | Read MB | Writes | Write MB | Avg Latency | Status |
|---|---|---|---|---|---|---|---|
| SQLStorm | ROWS | 1,554 | 128 | 102 | 37 | 1 ms | Excellent |
| WideWorldImporters | LOG | 571 | 258 | 18 | 0 | 1 ms | Excellent |
| tpch | ROWS | 425 | 26 | 1 | 0 | 0 ms | Excellent |
| SQLStorm | LOG | 32 | 1 | 232 | 7 | 0 ms | Excellent |
| tpch10 | ROWS | 426 | 26 | 1 | 0 | 0 ms | Excellent |
| All others | — | All showing 0 ms average latency — no concerns. | Excellent | ||||
All files are performing at sub-millisecond to 1 ms latency, consistent with NVMe SSD or high-performance local storage. No I/O tuning is required.
Scripts
The following scripts implement the recommendations in this report. Each script is a complete, runnable T-SQL batch. Review and test in a non-production environment before execution. Scripts that require a service restart are clearly marked.
1. Enable Optimize for Ad Hoc Workloads (Recommendation 2.1)
-- Enable 'optimize for ad hoc workloads' to reduce single-use plan cache bloat.
-- This is a dynamic setting; no service restart required.
-- Applies to: RockyPC (server-wide)
USE master;
GO
EXEC sp_configure 'show advanced options', 1;
RECONFIGURE WITH OVERRIDE;
GO
EXEC sp_configure 'optimize for ad hoc workloads', 1;
RECONFIGURE WITH OVERRIDE;
GO
-- Verify
SELECT name, value, value_in_use, description
FROM sys.configurations
WHERE name = 'optimize for ad hoc workloads';
GO
2. Enable Backup Compression and Checksum Defaults (Recommendations 2.2 and 2.3)
-- Enable backup compression default and backup checksum default.
-- These are dynamic settings; no service restart required.
-- Applies to: RockyPC (server-wide)
USE master;
GO
EXEC sp_configure 'show advanced options', 1;
RECONFIGURE WITH OVERRIDE;
GO
-- Enable backup compression (reduces backup size by ~50-70%)
EXEC sp_configure 'backup compression default', 1;
RECONFIGURE WITH OVERRIDE;
GO
-- Enable backup checksum (validates integrity during backup and restore)
EXEC sp_configure 'backup checksum default', 1;
RECONFIGURE WITH OVERRIDE;
GO
-- Verify
SELECT name, value, value_in_use, description
FROM sys.configurations
WHERE name IN ('backup compression default', 'backup checksum default');
GO
3. Enable Blocked Process Threshold (Recommendations 2.7 / 7.2)
-- Enable blocked process threshold monitoring (fires when blocking exceeds 5 seconds).
-- This is a dynamic setting; no service restart required.
-- Captures blocked process reports via system health extended event session.
-- Applies to: RockyPC (server-wide)
USE master;
GO
EXEC sp_configure 'show advanced options', 1;
RECONFIGURE WITH OVERRIDE;
GO
EXEC sp_configure 'blocked process threshold (s)', 5;
RECONFIGURE WITH OVERRIDE;
GO
-- Verify
SELECT name, value, value_in_use, description
FROM sys.configurations
WHERE name = 'blocked process threshold (s)';
GO
4. Enable Memory-Optimized TempDB Metadata (Recommendation 1.1)
-- Enable memory-optimized tempdb metadata to reduce system table latch contention.
-- *** IMPORTANT: Requires SQL Server service restart to take effect. ***
-- Plan a maintenance window before applying this change.
-- Applies to: RockyPC (server-wide, SQL Server 2019+ Enterprise/Developer)
USE master;
GO
EXEC sp_configure 'show advanced options', 1;
RECONFIGURE WITH OVERRIDE;
GO
EXEC sp_configure 'tempdb metadata memory-optimized', 1;
RECONFIGURE WITH OVERRIDE;
GO
-- Verify current setting (change takes effect after service restart)
SELECT name, value, value_in_use, description
FROM sys.configurations
WHERE name = 'tempdb metadata memory-optimized';
GO
-- After restarting the SQL Server service, verify with:
-- SELECT CASE WHEN OBJECTPROPERTY(OBJECT_ID('tempdb.sys.sysrowsets'),'TableIsMemoryOptimized') = 1
-- THEN 'Memory-Optimized TempDB Metadata: ENABLED'
-- ELSE 'Memory-Optimized TempDB Metadata: DISABLED'
-- END AS status;
GO
5. Upgrade WideWorldImporters Compatibility Level to 160 (Recommendation 2.4)
-- Upgrade WideWorldImporters database compatibility level from 130 to 160.
-- This enables SQL Server 2022 Intelligent Query Processing features.
-- *** TEST IN STAGING FIRST — query plan changes may affect performance. ***
-- Uses Query Store to capture plan regressions (Query Store is already enabled).
USE master;
GO
-- Step 1: Ensure Query Store is capturing baseline plans BEFORE upgrade
-- (already READ_WRITE, so baseline is being captured)
-- Step 2: Upgrade compatibility level
ALTER DATABASE [WideWorldImporters]
SET COMPATIBILITY_LEVEL = 160;
GO
-- Step 3: Verify
SELECT name, compatibility_level
FROM sys.databases
WHERE name = 'WideWorldImporters';
GO
-- Step 4: If plan regressions occur after upgrade, use Query Store
-- to force the old plan while investigating:
-- USE [WideWorldImporters];
-- EXEC sp_query_store_force_plan @query_id = , @plan_id = ;
GO
6. Standardize WideWorldImporters Query Store Settings (Recommendation 4.1)
-- Standardize WideWorldImporters Query Store configuration to match other databases.
-- Changes flush interval to 15 min, stats interval to 60 min,
-- max plans per query to 200, and max storage to 1000 MB.
USE master;
GO
ALTER DATABASE [WideWorldImporters]
SET QUERY_STORE = ON
(
OPERATION_MODE = READ_WRITE,
CLEANUP_POLICY = (STALE_QUERY_THRESHOLD_DAYS = 30),
DATA_FLUSH_INTERVAL_SECONDS = 900, -- 15 minutes (was 50 min = 3000 sec)
INTERVAL_LENGTH_MINUTES = 60, -- Stats collection interval (was 15 min)
MAX_STORAGE_SIZE_MB = 1000, -- Storage ceiling (was 500 MB)
QUERY_CAPTURE_MODE = AUTO,
MAX_PLANS_PER_QUERY = 200, -- Max plans (was 1000)
SIZE_BASED_CLEANUP_MODE = AUTO
);
GO
-- Verify
SELECT
d.name,
qs.desired_state_desc,
qs.actual_state_desc,
qs.data_flush_interval_seconds / 60.0 AS flush_interval_min,
qs.query_capture_mode_desc,
qs.max_storage_size_mb,
qs.stale_query_threshold_days,
qs.max_plans_per_query
FROM sys.databases d
JOIN sys.database_query_store_options qs ON d.database_id = qs.database_id
WHERE d.name = 'WideWorldImporters';
GO
7. Run DBCC CHECKDB on All User Databases (Recommendation 5.1 — Quick Pass)
-- Run DBCC CHECKDB with PHYSICAL_ONLY on all user databases for rapid corruption detection.
-- PHYSICAL_ONLY reduces duration significantly on large databases.
-- Run the full check (without PHYSICAL_ONLY) during off-hours on a schedule.
-- *** This may take several minutes per database depending on size. ***
USE master;
GO
PRINT '== Starting DBCC CHECKDB (PHYSICAL_ONLY) for all user databases ==';
PRINT 'Start time: ' + CONVERT(VARCHAR, GETDATE(), 120);
GO
DBCC CHECKDB ([WideWorldImporters]) WITH PHYSICAL_ONLY, NO_INFOMSGS;
GO
PRINT 'WideWorldImporters: CHECKDB complete at ' + CONVERT(VARCHAR, GETDATE(), 120);
GO
DBCC CHECKDB ([SQLStorm]) WITH PHYSICAL_ONLY, NO_INFOMSGS;
GO
PRINT 'SQLStorm: CHECKDB complete at ' + CONVERT(VARCHAR, GETDATE(), 120);
GO
DBCC CHECKDB ([tpcc]) WITH PHYSICAL_ONLY, NO_INFOMSGS;
GO
PRINT 'tpcc: CHECKDB complete at ' + CONVERT(VARCHAR, GETDATE(), 120);
GO
DBCC CHECKDB ([tpch]) WITH PHYSICAL_ONLY, NO_INFOMSGS;
GO
PRINT 'tpch: CHECKDB complete at ' + CONVERT(VARCHAR, GETDATE(), 120);
GO
DBCC CHECKDB ([CRM]) WITH PHYSICAL_ONLY, NO_INFOMSGS;
GO
PRINT 'CRM: CHECKDB complete at ' + CONVERT(VARCHAR, GETDATE(), 120);
GO
DBCC CHECKDB ([tpch10]) WITH PHYSICAL_ONLY, NO_INFOMSGS;
GO
PRINT 'tpch10: CHECKDB complete at ' + CONVERT(VARCHAR, GETDATE(), 120);
GO
DBCC CHECKDB ([DigitsSolver]) WITH PHYSICAL_ONLY, NO_INFOMSGS;
GO
PRINT 'DigitsSolver: CHECKDB complete at ' + CONVERT(VARCHAR, GETDATE(), 120);
GO
-- System databases
DBCC CHECKDB ([master]) WITH PHYSICAL_ONLY, NO_INFOMSGS;
DBCC CHECKDB ([model]) WITH PHYSICAL_ONLY, NO_INFOMSGS;
DBCC CHECKDB ([msdb]) WITH PHYSICAL_ONLY, NO_INFOMSGS;
GO
PRINT 'System databases: CHECKDB complete at ' + CONVERT(VARCHAR, GETDATE(), 120);
GO
-- Verify last CHECKDB dates after completion
SELECT
d.name AS database_name,
DATABASEPROPERTYEX(d.name, 'LastGoodCheckDbTime') AS last_good_checkdb,
DATEDIFF(DAY, CAST(DATABASEPROPERTYEX(d.name, 'LastGoodCheckDbTime') AS DATETIME), GETDATE()) AS days_since_checkdb
FROM sys.databases d
WHERE d.database_id > 0
AND d.state_desc = 'ONLINE'
ORDER BY days_since_checkdb DESC;
GO
8. Schedule Weekly CHECKDB via SQL Agent Job (Recommendation 5.1)
-- Create a SQL Server Agent job to run full DBCC CHECKDB on all user databases weekly.
-- Scheduled for Sunday at 2:00 AM (adjust as needed).
-- Requires SQL Server Agent to be running.
USE msdb;
GO
-- Drop existing job if it exists (for re-runability)
IF EXISTS (SELECT 1 FROM msdb.dbo.sysjobs WHERE name = N'Weekly DBCC CHECKDB - All Databases')
EXEC msdb.dbo.sp_delete_job @job_name = N'Weekly DBCC CHECKDB - All Databases';
GO
EXEC msdb.dbo.sp_add_job
@job_name = N'Weekly DBCC CHECKDB - All Databases',
@enabled = 1,
@description = N'Runs DBCC CHECKDB on all online user databases weekly to verify integrity.',
@category_name = N'Database Maintenance';
GO
EXEC msdb.dbo.sp_add_jobstep
@job_name = N'Weekly DBCC CHECKDB - All Databases',
@step_name = N'Run CHECKDB on all databases',
@subsystem = N'TSQL',
@command = N'
DECLARE @dbname SYSNAME;
DECLARE @sql NVARCHAR(500);
DECLARE db_cursor CURSOR FOR
SELECT name
FROM sys.databases
WHERE state_desc = ''ONLINE''
AND name NOT IN (''tempdb'')
ORDER BY name;
OPEN db_cursor;
FETCH NEXT FROM db_cursor INTO @dbname;
WHILE @@FETCH_STATUS = 0
BEGIN
SET @sql = N''DBCC CHECKDB(['' + @dbname + N'']) WITH NO_INFOMSGS, ALL_ERRORMSGS'';
PRINT ''Running CHECKDB on: '' + @dbname + '' at '' + CONVERT(VARCHAR, GETDATE(), 120);
EXEC sp_executesql @sql;
FETCH NEXT FROM db_cursor INTO @dbname;
END;
CLOSE db_cursor;
DEALLOCATE db_cursor;
',
@on_success_action = 1,
@on_fail_action = 2;
GO
-- Schedule: Sunday at 02:00 AM
EXEC msdb.dbo.sp_add_schedule
@schedule_name = N'Weekly Sunday 2AM',
@freq_type = 8, -- Weekly
@freq_interval = 1, -- Sunday
@freq_recurrence_factor = 1,
@active_start_time = 20000; -- 02:00:00
GO
EXEC msdb.dbo.sp_attach_schedule
@job_name = N'Weekly DBCC CHECKDB - All Databases',
@schedule_name = N'Weekly Sunday 2AM';
GO
EXEC msdb.dbo.sp_add_jobserver
@job_name = N'Weekly DBCC CHECKDB - All Databases',
@server_name = N'(local)';
GO
PRINT 'Weekly DBCC CHECKDB job created successfully.';
GO
9. Full Backup All User Databases (Recommendation 8.1)
-- Perform immediate full backups of all user databases that lack recent backups.
-- Adjust @backup_path to a valid backup directory on your system.
-- Backups use WITH COMPRESSION and CHECKSUM (enabled by previous sp_configure scripts).
USE master;
GO
DECLARE @backup_path NVARCHAR(256) = N'C:\SQLBackups\'; -- *** CHANGE THIS PATH ***
-- Ensure backup directory exists (run in Windows or use xp_cmdshell if enabled)
-- EXEC xp_cmdshell 'mkdir C:\SQLBackups';
DECLARE @sql NVARCHAR(1000);
DECLARE @dbname SYSNAME;
DECLARE @filename NVARCHAR(500);
DECLARE @timestamp VARCHAR(20);
SET @timestamp = REPLACE(REPLACE(CONVERT(VARCHAR, GETDATE(), 120), '-', ''), ':', '');
SET @timestamp = REPLACE(@timestamp, ' ', '_');
-- Backup each user database
DECLARE backup_cursor CURSOR FOR
SELECT name
FROM sys.databases
WHERE name NOT IN ('tempdb')
AND state_desc = 'ONLINE'
ORDER BY name;
OPEN backup_cursor;
FETCH NEXT FROM backup_cursor INTO @dbname;
WHILE @@FETCH_STATUS = 0
BEGIN
SET @filename = @backup_path + @dbname + N'_Full_' + @timestamp + N'.bak';
SET @sql = N'BACKUP DATABASE [' + @dbname + N']
TO DISK = N''' + @filename + N'''
WITH COMPRESSION,
CHECKSUM,
STATS = 10,
FORMAT,
INIT,
NAME = N''' + @dbname + N' Full Backup'';';
PRINT 'Backing up: ' + @dbname + ' -> ' + @filename;
EXEC sp_executesql @sql;
FETCH NEXT FROM backup_cursor INTO @dbname;
END;
CLOSE backup_cursor;
DEALLOCATE backup_cursor;
GO
-- Verify backups recorded in msdb
SELECT
d.name AS database_name,
bs.type AS backup_type,
bs.backup_finish_date,
CAST(bs.backup_size / 1024.0 / 1024.0 AS DECIMAL(10,2)) AS backup_size_mb,
bs.is_compressed
FROM sys.databases d
LEFT JOIN msdb.dbo.backupset bs
ON d.name = bs.database_name
AND bs.backup_finish_date >= DATEADD(HOUR, -2, GETDATE())
ORDER BY d.name, bs.backup_finish_date DESC;
GO
10. Fix Full Recovery Databases Without Log Backups (Recommendation 8.2)
-- For developer databases where point-in-time recovery is NOT required,
-- switch from FULL to SIMPLE recovery model to prevent log file growth.
-- *** REVIEW EACH DATABASE BEFORE RUNNING — choose SIMPLE only if PITR is not needed. ***
-- After switching to SIMPLE, take a new full backup to establish a new backup chain.
USE master;
GO
-- Option A: Switch to SIMPLE recovery (recommended for developer/test databases
-- where point-in-time recovery is not required)
-- Uncomment the databases you want to switch to SIMPLE:
-- ALTER DATABASE [tpcc] SET RECOVERY SIMPLE WITH NO_WAIT;
-- ALTER DATABASE [tpch] SET RECOVERY SIMPLE WITH NO_WAIT;
-- ALTER DATABASE [tpch10] SET RECOVERY SIMPLE WITH NO_WAIT;
-- ALTER DATABASE [CRM] SET RECOVERY SIMPLE WITH NO_WAIT;
-- ALTER DATABASE [DigitsSolver] SET RECOVERY SIMPLE WITH NO_WAIT;
-- ALTER DATABASE [model] SET RECOVERY SIMPLE WITH NO_WAIT; -- system DB caution
-- After switching to SIMPLE, shrink the log to reclaim space (one-time operation only):
-- DBCC SHRINKFILE (N'', 64); -- Shrink to 64 MB
-- Option B: If FULL recovery IS required, create a log backup job.
-- First take a full backup (run Script #9 above), then create log backups.
-- Example for tpch (has an existing full backup from 2025-11-22):
-- BACKUP LOG [tpch]
-- TO DISK = N'C:\SQLBackups\tpch_Log_' + REPLACE(REPLACE(CONVERT(VARCHAR,GETDATE(),120),'-',''),':','') + '.trn'
-- WITH COMPRESSION, CHECKSUM, STATS = 10;
-- Verify recovery models after changes
SELECT name, recovery_model_desc, log_reuse_wait_desc
FROM sys.databases
WHERE name NOT IN ('tempdb', 'master', 'msdb')
ORDER BY name;
GO
11. Audit SQL Logins and sa Account Status (Recommendation 6.3)
-- Audit all SQL Server logins, sysadmin members, and sa account status.
-- Review output for unexpected privileged accounts.
USE master;
GO
-- List all server logins with their roles and status
SELECT
sp.name AS login_name,
sp.type_desc AS login_type,
sp.is_disabled,
sp.is_policy_checked,
sp.is_expiration_checked,
CASE WHEN srm.role_principal_id IS NOT NULL THEN 'YES' ELSE 'NO' END AS is_sysadmin,
sp.create_date,
sp.modify_date,
sp.default_database_name
FROM sys.server_principals sp
LEFT JOIN sys.server_role_members srm
ON sp.principal_id = srm.member_principal_id
AND srm.role_principal_id = SUSER_ID('sysadmin')
WHERE sp.type IN ('S', 'U', 'G') -- SQL, Windows user, Windows group
AND sp.name NOT LIKE '##%' -- Exclude internal accounts
ORDER BY is_sysadmin DESC, sp.name;
GO
-- Check sa account specifically
SELECT
name,
is_disabled,
LOGINPROPERTY('sa', 'PasswordHash') AS has_password,
LOGINPROPERTY('sa', 'BadPasswordCount') AS bad_password_count,
LOGINPROPERTY('sa', 'IsLocked') AS is_locked
FROM sys.server_principals
WHERE name = 'sa';
GO
-- List all sysadmin members explicitly
SELECT
ssp.name AS sysadmin_member,
ssp.type_desc,
ssp.is_disabled,
ssp.create_date
FROM sys.server_role_members srm
JOIN sys.server_principals ssp ON srm.member_principal_id = ssp.principal_id
JOIN sys.server_principals srp ON srm.role_principal_id = srp.principal_id
WHERE srp.name = 'sysadmin'
ORDER BY ssp.name;
GO
12. Apply All Safe Server Configuration Changes in One Batch (Combined Script)
-- Combined script: Apply all safe, dynamic sp_configure recommendations at once.
-- No service restart required for these settings.
-- Settings included:
-- 1. optimize for ad hoc workloads = 1
-- 2. backup compression default = 1
-- 3. backup checksum default = 1
-- 4. blocked process threshold = 5 seconds
-- Note: 'tempdb metadata memory-optimized' requires restart — see Script #4.
USE master;
GO
EXEC sp_configure 'show advanced options', 1;
RECONFIGURE WITH OVERRIDE;
GO
EXEC sp_configure 'optimize for ad hoc workloads', 1;
EXEC sp_configure 'backup compression default', 1;
EXEC sp_configure 'backup checksum default', 1;
EXEC sp_configure 'blocked process threshold (s)', 5;
GO
RECONFIGURE WITH OVERRIDE;
GO
-- Verify all changes
SELECT name, value, value_in_use, description
FROM sys.configurations
WHERE name IN (
'optimize for ad hoc workloads',
'backup compression default',
'backup checksum default',
'blocked process threshold (s)'
)
ORDER BY name;
GO
PRINT 'All configuration changes applied successfully at ' + CONVERT(VARCHAR, GETDATE(), 120);
GO