UNDERCOVER TOOLBOX: sp_Snapshot – The Easy Way To Create Database Snapshots for One or Many Database

woman-photographer-1245761_960_720

sp_Snapshot is a procedure that we’ve written to quickly and easily create a database snapshot of one or multiple databases.

Prerequisites

sp_Snapshot uses STRING_SPLIT, if you’re running on a version of SQL Server prior to 2016 or have databases with a compatibility level under 130 you will need to install fn_SplitString

The procedure takes two parameters,

@DatabaseList – a comma delimited string of database names, allows wildcards
@ListOnly – 1- a list of affected databases will be displayed but snapshots aren’t created.
0- Snapshots are created automatically DEFAULT

Example

Create a snapshot of AdventureWorks and all databases with names beginning with ‘SQL’


sp_Snapshot @DatabaseList = 'AdventureWorks,SQL%'

sp_Snapshot


--Author: David Fowler
--Date: 19 September 2017
--(c) SQLUndercover 2017
--sqlundercover.com

USE master
GO

CREATE PROCEDURE sp_Snapshot
(@DatabaseList NVARCHAR(4000),
@ListOnly BIT = 0)

AS 

BEGIN

SET NOCOUNT ON

IF OBJECT_ID('tempdb..#DatabaseList') IS NOT NULL
	DROP TABLE #DatabaseList

CREATE TABLE #DatabaseList (name NVARCHAR(4000))

IF OBJECT_ID('tempdb..#DatabasesFinal') IS NOT NULL
	DROP TABLE #DatabasesFinal

--set compatibility mode
DECLARE @compatibility BIT

--set compatibility to 1 if server version includes STRING_SPLIT
SELECT	@compatibility = CASE
			WHEN SERVERPROPERTY ('productversion') >= '13.0.4001.0' AND Compatibility_Level >= 130 THEN 1
			ELSE 0
		END
FROM sys.databases
WHERE name = DB_NAME()

--select the database list into a temp table so that we can work with it
IF @compatibility = 1 --if compatibility = 1 then use STRING_SPLIT otherwise use fn_SplitString
	INSERT INTO #DatabaseList
	SELECT value
	FROM STRING_SPLIT(@DatabaseList,',')
ELSE
	INSERT INTO #DatabaseList
	SELECT StringElement AS name
	FROM master..fn_SplitString(@DatabaseList,',')

--get list of databases, including those covered by any wildcards
SELECT QUOTENAME(name) AS name
INTO #DatabasesFinal
FROM sys.databases databases
WHERE EXISTS
		(SELECT name
		FROM #DatabaseList
		WHERE databases.name LIKE #DatabaseList.name)	

IF @ListOnly = 1 --if @listonly set then only print the affected databases
SELECT name
FROM #DatabasesFinal
ELSE
BEGIN

	DECLARE @Databases VARCHAR(128)

	------------------------------------------------------------------------------------------------------
	--Loop through each database creating snapshots

	DECLARE databases_curr CURSOR
	FOR SELECT name
		FROM #DatabasesFinal

	OPEN databases_curr

	FETCH NEXT FROM databases_curr
	INTO @Databases

	WHILE @@FETCH_STATUS = 0
	BEGIN

		--create snapshots

		EXEC ('USE ' + @Databases +
			'DECLARE @DatabaseName VARCHAR(128)
			DECLARE @SnapshotName VARCHAR(128)
			SET @DatabaseName = DB_NAME()
			SET @SnapshotName = DB_NAME() + ''_snapshot'' 

			--table variable to hold file list
			DECLARE @DatabaseFiles TABLE (id INT identity(1,1),name VARCHAR(128), physical_name VARCHAR(400)) 

			--populate table variable with file information
			INSERT INTO @DatabaseFiles (name, physical_name)
			SELECT name, physical_name
			FROM sys.database_files
			WHERE type != 1 

			--begin building snapshot script
			DECLARE @SnapshotScript VARCHAR(1000)
			SET @SnapshotScript = ''CREATE DATABASE '' + QUOTENAME(@SnapshotName) + '' ON '' 

			--loop through datafile table variable
			DECLARE @LoopCounter INT = 0 

			DECLARE @FileCount INT
			SELECT @FileCount = COUNT(*)
			FROM @DatabaseFiles 

			WHILE @LoopCounter < @FileCount
			BEGIN
			SET @LoopCounter = @LoopCounter + 1
			SELECT @SnapshotScript = @SnapshotScript + ''(NAME = '' + QUOTENAME(name) + '', FILENAME = '''''' + physical_name + ''.ss''''),''
			FROM @DatabaseFiles
			WHERE id = @LoopCounter
			END 

			--loop will have added an unwanted comma at the end of the script, delete this comma
			SET @SnapshotScript = LEFT(@snapshotscript, LEN(@snapshotscript) -1) 

			--add AS SNAPSHOT to script
			SET @SnapshotScript = @SnapshotScript + '' AS SNAPSHOT OF ['' + @DatabaseName + '']'' 

			--Generate the snapshot
			PRINT ''Creating Snapshot for ' + @Databases + '''
			EXEC (@SnapshotScript)')

		FETCH NEXT FROM databases_curr
		INTO @Databases
	END

	CLOSE databases_curr
	DEALLOCATE databases_curr
END
END

I hope you find this useful and thanks for reading 🙂

One thought on “UNDERCOVER TOOLBOX: sp_Snapshot – The Easy Way To Create Database Snapshots for One or Many Database

Add yours

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

Create a website or blog at WordPress.com

Up ↑

%d bloggers like this: