Writes, optimizes, and debugs T-SQL queries. Explains SQL Server internals, troubleshoots performance issues, and guides database administration tasks including backup/restore, high availability, security, and index design. Use when the user asks about T-SQL syntax, SQL Server administration, query performance, stored procedures, indexes, locking, transactions, backup/restore, high availability, security, or any MSSQL-related topic — even without saying 'SQL Server' explicitly. Also trigger on terms like SSMS, tempdb, bcp, sqlcmd, MSSQL, sp_executesql, NOLOCK, columnstore, Hekaton, RCSI, param sniffing, or execution plan.
100
100%
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Passed
No known issues
Use database snapshots for:
| Scenario | Snapshot role |
|---|---|
| Pre-upgrade or pre-migration safety net | Take snapshot → apply change → revert if needed |
| Consistent point-in-time reporting copy | Readers query snapshot; OLTP continues on source |
| Fast dev/test environment reset | Reset to known-good state in seconds (no restore) |
| Offline CHECKDB without impacting production | DBCC CHECKDB runs against snapshot, not source |
| Rapid undo for batch jobs or schema changes | Revert after bad deployment instead of running undo scripts |
Do not use snapshots as a backup strategy — they are dependent on the source database and are destroyed if the source is lost or dropped.
A snapshot is a read-only, point-in-time copy of a database that uses copy-on-write (CoW) sparse files on NTFS.
This means:
[!WARNING] Linux limitation Sparse files require an underlying filesystem that supports them (ext4, XFS, Btrfs). SQL Server on Linux supports database snapshots but the filesystem must support sparse files. NTFS is not available on Linux; ext4 and XFS both support sparse files. Verify with
lsattrorstat --file-system.
-- Minimal syntax: one sparse file per source data file
CREATE DATABASE AdventureWorks_SS_20260317
ON
(
NAME = AdventureWorks_Data, -- logical file name from source
FILENAME = 'D:\Snapshots\AW_SS_20260317.ss' -- snapshot sparse file path
)
AS SNAPSHOT OF AdventureWorks;
GOFor a multi-file database, you must specify one snapshot file per source data file (not log files — snapshots do not include log files):
-- Multi-file database snapshot
CREATE DATABASE AdventureWorks_SS_20260317
ON
(
NAME = AdventureWorks_Data,
FILENAME = 'D:\Snapshots\AW_data_SS.ss'
),
(
NAME = AdventureWorks_FG2,
FILENAME = 'D:\Snapshots\AW_fg2_SS.ss'
)
AS SNAPSHOT OF AdventureWorks;
GOGet the logical file names for a database:
SELECT name, physical_name, type_desc
FROM sys.master_files
WHERE database_id = DB_ID('AdventureWorks')
AND type = 0; -- data files only (type=1 is log)Include the source database name, date, and optionally a tag:
<SourceDB>_SS_<YYYYMMDD>[_<tag>]Example: AdventureWorks_SS_20260317_preupgrade
-- Connect to the snapshot directly
USE AdventureWorks_SS_20260317;
GO
SELECT TOP 10 * FROM Sales.SalesOrderHeader
ORDER BY OrderDate DESC;Or use a four-part name / USE statement and redirect:
-- From a different database context, specify the snapshot DB name
SELECT COUNT(*) FROM AdventureWorks_SS_20260317.Sales.SalesOrderHeader;Snapshots are read-only. Any DML (INSERT/UPDATE/DELETE) or DDL will fail:
Msg 3906, Level 16: Failed to update database "AdventureWorks_SS_20260317"
because the database is read-only.Instead of querying a busy OLTP database, point a report to the snapshot. Snapshot reads never block writers on the source and writers never block snapshot reads:
-- In the report connection string, use the snapshot database name:
-- Server=.;Database=AdventureWorks_SS_20260317;Trusted_Connection=Yes;[!NOTE] SQL Server 2022 Snapshots work with contained Availability Groups. See 43-high-availability.md for contained AG details.
SQL Server automatically creates an internal snapshot when you run DBCC CHECKDB against an online database — but that internal snapshot is destroyed when CHECKDB finishes and cannot be reused.
Running CHECKDB against a user-created snapshot avoids creating a second internal snapshot and lets you re-run CHECKDB as many times as needed against the same consistent point-in-time view:
-- Run CHECKDB against a named snapshot
DBCC CHECKDB (AdventureWorks_SS_20260317) WITH NO_INFOMSGS, ALL_ERRORMSGS;Benefits:
[!WARNING] Snapshot for CHECKDB only — do not defer snapshots too long If the snapshot is old, many pages will have changed in the source and the snapshot file will be large. CHECKDB reads the snapshot's version of each page, so a stale snapshot reflects old data. For integrity checking, create a fresh snapshot before running CHECKDB.
Reverting rolls the source database back to exactly the state it was in when the snapshot was taken. This is destructive and irreversible — all changes to the source since the snapshot was taken are lost.
-- Prerequisites:
-- 1. Only ONE snapshot of this database may exist when reverting
-- 2. Source database must have no other connections (switch to SINGLE_USER)
-- 3. SQL Server Agent or scheduled jobs should be stopped if they touch the DB
USE master;
GO
-- Kick all connections
ALTER DATABASE AdventureWorks SET SINGLE_USER WITH ROLLBACK IMMEDIATE;
GO
-- Revert
RESTORE DATABASE AdventureWorks FROM DATABASE_SNAPSHOT = 'AdventureWorks_SS_20260317';
GO
-- Re-open
ALTER DATABASE AdventureWorks SET MULTI_USER;
GO[!WARNING] Single snapshot required If multiple snapshots exist for the same source database,
RESTORE DATABASE ... FROM DATABASE_SNAPSHOTwill fail with:Msg 3137: The database cannot be reverted. Either the primary or the snapshot names are improper, or there is more than one snapshot for the primary database.Drop all other snapshots first.
[!WARNING] Log chain broken after revert After reverting, the database log backup chain is broken. Take a full backup immediately before re-enabling any differential or log backup jobs.
-- Snapshots are dropped like any database
DROP DATABASE AdventureWorks_SS_20260317;
GOWhen the source database is dropped, all its snapshots are also dropped automatically.
Snapshots cannot be detached, backed up, or restored. They can only be created and dropped.
Snapshot disk usage grows as source pages are modified. Monitor with:
-- Snapshot sparse file sizes (disk used vs allocated)
SELECT
DB_NAME(database_id) AS snapshot_name,
name AS logical_file,
physical_name,
size * 8 / 1024 AS allocated_mb,
FILEPROPERTY(name, 'SpaceUsed') * 8 / 1024 AS used_mb
FROM sys.master_files
WHERE database_id IN (
SELECT database_id FROM sys.databases WHERE source_database_id IS NOT NULL
);For a simpler view using DMVs:
-- All snapshots with their source DB
SELECT
s.name AS snapshot_db,
d.name AS source_db,
s.create_date,
s.state_desc
FROM sys.databases s
JOIN sys.databases d ON s.source_database_id = d.database_id;| Source activity | Snapshot growth |
|---|---|
| Low write rate (mostly reads) | Slow growth; snapshot stays small |
| High write rate (OLTP) | Rapid growth; snapshot can reach source DB size |
| Bulk loads, index rebuilds | Very fast growth; can exhaust disk quickly |
| Full table rewrite (UPDATE all rows) | Snapshot grows to roughly source data size |
Rule of thumb: For a busy OLTP database, plan for snapshot sparse files to potentially reach 50–100% of source data size if the snapshot lives for more than a few hours. For short-lived pre-deployment snapshots (minutes), growth is typically < 5%.
-- Remove from AG before revert
ALTER AVAILABILITY GROUP [MyAG] REMOVE DATABASE AdventureWorks;
GO
-- Perform revert (as shown above)
-- Re-add to AG after revert (requires full backup + restore on each secondary)
ALTER AVAILABILITY GROUP [MyAG] ADD DATABASE AdventureWorks;
GO[!WARNING] AG and revert Reverting an AG database requires rejoining all secondaries — effectively a full database re-seeding. For large databases this is very disruptive. Use snapshots as a safety net only when you expect a very low probability of needing to revert.
| Capability | Snapshot | Backup/Restore | CDC | Temporal Table |
|---|---|---|---|---|
| Creation speed | Near-instant | Proportional to DB size | Continuous | Always active |
| Recovery granularity | Full DB to snapshot point | Any point in log chain | Table-level, row-level changes | Per-row, any point in time |
| Space cost | Sparse (grows with changes) | Full copy | Change log only | History table (rows) |
| Reads source on unchanged pages | Yes (I/O dependency) | No (independent copy) | N/A | N/A |
| Survives source database loss | No | Yes | Partial (change log survives) | No (in same DB) |
| Revert / undo capability | Full DB revert to snapshot point | Restore to any point | Replay changes | Query history; manual undo |
| Row-level time travel | No | No | Yes (by LSN) | Yes (by AS OF) |
| Azure SQL Database support | No | Yes | Yes | Yes |
| Azure SQL Managed Instance | Yes | Yes | Yes | Yes |
| On-prem SQL Server | Yes | Yes | Yes | Yes |
| Use for compliance auditing | No (not tamper-evident) | No | Partial | Partial |
Decision rule:
SELECT
s.name AS snapshot_name,
d.name AS source_db,
s.create_date,
s.state_desc,
s.is_read_only,
s.snapshot_isolation_state_desc
FROM sys.databases s
LEFT JOIN sys.databases d ON s.source_database_id = d.database_id
WHERE s.source_database_id IS NOT NULL
ORDER BY s.create_date;SELECT
DB_NAME(mf.database_id) AS snapshot_db,
mf.name AS logical_file,
mf.physical_name,
mf.size * 8 / 1024 AS size_mb,
mf.max_size,
mf.growth
FROM sys.master_files mf
JOIN sys.databases d ON mf.database_id = d.database_id
WHERE d.source_database_id IS NOT NULL
ORDER BY DB_NAME(mf.database_id), mf.file_id;SELECT
d.name AS source_db,
COUNT(s.database_id) AS snapshot_count,
MIN(s.create_date) AS oldest_snapshot,
MAX(s.create_date) AS newest_snapshot
FROM sys.databases d
LEFT JOIN sys.databases s ON s.source_database_id = d.database_id
WHERE s.source_database_id IS NOT NULL
GROUP BY d.name
ORDER BY d.name;-- 1. Take snapshot before applying changes
CREATE DATABASE AppDB_SS_preupgrade
ON (NAME = AppDB_Data, FILENAME = 'D:\Snapshots\AppDB_preupgrade.ss')
AS SNAPSHOT OF AppDB;
GO
-- 2. Apply schema changes / data migrations
USE AppDB;
-- ... run upgrade scripts ...
-- 3a. If upgrade succeeded: drop snapshot
DROP DATABASE AppDB_SS_preupgrade;
-- 3b. If upgrade failed: revert
ALTER DATABASE AppDB SET SINGLE_USER WITH ROLLBACK IMMEDIATE;
RESTORE DATABASE AppDB FROM DATABASE_SNAPSHOT = 'AppDB_SS_preupgrade';
ALTER DATABASE AppDB SET MULTI_USER;
DROP DATABASE AppDB_SS_preupgrade; -- must drop after revert-- Run via SQL Agent daily job
DECLARE @snap_name NVARCHAR(128) = 'ReportDB_SS_' + CONVERT(CHAR(8), GETDATE(), 112);
DECLARE @file_path NVARCHAR(512) = 'D:\Snapshots\ReportDB_' + CONVERT(CHAR(8), GETDATE(), 112) + '.ss';
DECLARE @sql NVARCHAR(MAX);
-- Drop yesterday's snapshot (find by naming convention)
SELECT @sql = 'DROP DATABASE ' + QUOTENAME(name)
FROM sys.databases
WHERE source_database_id = DB_ID('ReportDB')
AND name <> @snap_name;
IF @sql IS NOT NULL EXEC sp_executesql @sql;
-- Create today's snapshot
SET @sql = N'CREATE DATABASE ' + QUOTENAME(@snap_name)
+ N' ON (NAME = ReportDB_Data, FILENAME = ''' + @file_path + N''')'
+ N' AS SNAPSHOT OF ReportDB;';
EXEC sp_executesql @sql;Point the reporting tool at the snapshot database instead of the OLTP source.
-- Once: take a snapshot of a known-good dev database
CREATE DATABASE DevDB_SS_clean
ON (NAME = DevDB_Data, FILENAME = 'D:\Snapshots\DevDB_clean.ss')
AS SNAPSHOT OF DevDB;
GO
-- After each dev session: reset to clean state
ALTER DATABASE DevDB SET SINGLE_USER WITH ROLLBACK IMMEDIATE;
RESTORE DATABASE DevDB FROM DATABASE_SNAPSHOT = 'DevDB_SS_clean';
ALTER DATABASE DevDB SET MULTI_USER;[!NOTE] Snapshot lifespan for dev reset The "clean" snapshot grows over time as the dev database accumulates changes. For an active dev database, the snapshot may grow very large if kept for weeks. Drop and recreate the snapshot periodically (e.g., after each sprint reset) to keep disk usage manageable.
-- Create snapshot at a low-traffic time
CREATE DATABASE AdventureWorks_SS_checkdb
ON (NAME = AdventureWorks_Data, FILENAME = 'D:\Snapshots\AW_checkdb.ss')
AS SNAPSHOT OF AdventureWorks;
GO
-- Run CHECKDB against the snapshot — reads go to snapshot, not source
DBCC CHECKDB (AdventureWorks_SS_checkdb) WITH NO_INFOMSGS, ALL_ERRORMSGS;
GO
-- Drop when done
DROP DATABASE AdventureWorks_SS_checkdb;
GO| Limitation | Detail |
|---|---|
| Read-only | No DML or DDL against the snapshot |
| NTFS/supported FS required | Sparse files need NTFS on Windows, ext4/XFS on Linux |
| No backup of snapshots | BACKUP DATABASE against a snapshot fails |
| No detach/attach | Cannot detach a snapshot |
| No log files | Snapshots do not include transaction log |
| Maximum one revert source | Only one snapshot can exist at revert time |
| Primary replica only (AG) | Cannot create on readable secondaries |
| No Azure SQL Database | Not supported (Azure SQL Managed Instance: yes) |
| Source dependency | Snapshot fails if source database is unavailable |
| FILESTREAM databases | Not supported if source has FILESTREAM filegroups |
| Memory-optimized filegroups | Not supported if source has In-Memory OLTP data |
| Mirroring (deprecated) | Source database in a mirroring partnership supports snapshots only on the principal |
| Full-text indexes | Included in snapshot but cannot be updated |
[!WARNING] Azure SQL Database Database snapshots are not available in Azure SQL Database (the PaaS offering). Use Azure SQL Database's built-in geo-redundant backups, point-in-time restore, or temporal tables instead. Azure SQL Managed Instance supports snapshots.
Snapshots grow silently. A snapshot on a busy OLTP database can consume hundreds of GBs within hours. Monitor disk space and set alerts; there is no automatic size limit — the snapshot will exhaust all available disk space and then fail with I/O errors on the source database if the volume fills up.
A full volume kills both source and snapshot. If the snapshot sparse file and the source database files share a volume and that volume fills up, the source database will throw I/O errors. Put snapshot files on a separate volume from source data files.
Reverting breaks the log backup chain. After RESTORE DATABASE ... FROM DATABASE_SNAPSHOT, the LSN chain is invalid. Take a full backup immediately before re-enabling log backup jobs.
Only one snapshot at revert time. You must drop all other snapshots of the source before reverting. Plan your snapshot naming convention so you can identify which snapshots to clean up.
FILESTREAM and In-Memory OLTP are unsupported. CREATE DATABASE ... AS SNAPSHOT OF will fail with an error if the source has FILESTREAM or memory-optimized filegroups. This is a hard limitation — there is no workaround.
No cross-instance snapshots. A snapshot must reside on the same SQL Server instance as the source. You cannot use a snapshot as a remote read-only copy on a different server.
CHECKDB auto-snapshot vs user snapshot. When DBCC CHECKDB runs against the source database (not a named snapshot), SQL Server creates an internal snapshot internally. If there is insufficient disk space for the internal snapshot, CHECKDB falls back to locking-based consistency checks — potentially blocking writers. Pre-creating a user snapshot and targeting it gives you control over timing and disk usage.
Snapshot create_date is in UTC server time. Use GETUTCDATE() in snapshot naming scripts, or account for timezone when correlating snapshot creation times with application logs.
sys.databases.source_database_id is the key. This column is NULL for regular databases and non-NULL for snapshots. Filter on it in all queries that need to distinguish snapshots from regular databases.
Snapshot read I/O depends on source database. Snapshot readers may need to read unmodified pages from the source database. If the source database is offline or in a suspect state, snapshot reads of unmodified pages will fail. This is unlike a backup/restore copy which is fully independent.
No statistics updates on snapshot. Query plans against a snapshot are based on the snapshot's statistics (copied from the source at snapshot creation time). Old or stale statistics are not updated since the snapshot is read-only.
Snapshots are not encrypted independently. If TDE is enabled on the source, the snapshot inherits TDE. The snapshot uses the same DEK as the source — there is no way to apply different encryption to a snapshot.
references