Blog Post

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 ??

Rate

★ ★ ★ ★ ★ ★ ★ ★ ★ ★

You rated this post out of 5. Change rating

Share

Share

Rate

★ ★ ★ ★ ★ ★ ★ ★ ★ ★

You rated this post out of 5. Change rating