By: Joe Gavin | Updated: 2020-07-06 | Comments (32) | Related: > Restore
Problem
I frequently get requests to refresh a test / dev SQL Server database and need a way to test database backups from the latest full backup of a production database. It’s easy enough to do this with SQL Server Management Studio, but I would rather have an easy to configure script that’s less prone to making a mistake that I could run on demand and / or easily automate rather than do this as a manual process.
Solution
We previously saw how to accomplish this in this tip: Automating a SQL Server Database Refresh, with a little PowerShell, a little T-SQL and SQL Server Agent. In the interest of keeping the configuration simpler and only changed in one script the following is a revised way to do it with just PowerShell and optionally SQL Server Agent.
The following versions were used writing this tip:
- SQL Server 2017 CU19 Developer Edition
- SQL Server Management Objects (SMO) installed SQL Server Management Studio 18.4
- PowerShell 5.1.17763.1007
Configure PowerShell Script
To get started, copy the following PowerShell into the editor of your choice and configuring the variables for your purposes.
# restores a full database backup to another database from source's latest full backup file in specified directory # begin script configuration here $TargetSqlServerInstance = "JGAVIN-L\SQL2017" # target server instance $TargetDb = "RefreshTest" # target database $BackupDir = "\\jgavin-l\SQL2017_Backup\RefreshProd" # directory / share where backups are stored $SourceLogicalDataFileName = "RefreshProd" # logical data file name of source db $SourceLogicalLogFileName = "RefreshProd_log" # logical log file name of source db $TargetLogicalDataFileName = "RefreshTest" # logical name you want to change logical data file on target db to $TargetLogicalLogFileName = "RefreshTest_log" # logical name you want to change logical log file on target db to $TargetPhysicalDataFileName = "C:\Program Files\Microsoft SQL Server\MSSQL14.SQL2017\MSSQL\DATA\RefreshTest.mdf" # full path\file of target db physical data file $TargetPhysicalLogFileName = "C:\Program Files\Microsoft SQL Server\MSSQL14.SQL2017\MSSQL\DATA\RefreshTest_log.mdf" # full path\file of target db physical log file $CompatLevel = 140 # compatibility level to set target database to (2019=150, 2017=140, 2016=130, 2014=120, 2012=110, 2008/2008R2=100, 2005=90, 2000=80, 7=70) # end script configuration here # import sqlserver module Import-Module sqlserver # latest full backup file name is dynamically determined and appended to backup directory $LatestFullBackupFile = Get-ChildItem -Path $BackupDir -Filter *.bak | Sort-Object LastAccessTime -Descending | Select-Object -First 1 $FileToRestore = $BackupDir + '\' + $LatestFullBackupFile # kill any connections in target database $KillConnectionsSql= " USE master GO ALTER DATABASE $TargetDb SET SINGLE_USER WITH ROLLBACK IMMEDIATE GO USE master GO ALTER DATABASE $TargetDb SET MULTI_USER GO USE master GO " Invoke-Sqlcmd -ServerInstance $TargetSqlServerInstance -Query $KillConnectionsSql # import sqlserver module Import-Module sqlserver # restore $RelocateData = New-Object Microsoft.SqlServer.Management.Smo.RelocateFile("$SourceLogicalDataFileName", "$TargetPhysicalDataFileName") $RelocateLog = New-Object Microsoft.SqlServer.Management.Smo.RelocateFile("$SourceLogicalLogFileName", "$TargetPhysicalLogFileName") Restore-SqlDatabase -ServerInstance $TargetSqlServerInstance -Database $TargetDb -BackupFile $FileToRestore -RelocateFile @($RelocateData,$RelocateLog) -ReplaceDatabase # end restore # set db owner to sa Invoke-Sqlcmd -ServerInstance $TargetSqlServerInstance -Database $TargetDb -Query "EXEC sp_changedbowner sa" # set compatibility level Invoke-Sqlcmd -ServerInstance $TargetSqlServerInstance -Query "ALTER DATABASE $($TargetDb) SET COMPATIBILITY_LEVEL =$($CompatLevel)" # set recovery model to simple Invoke-Sqlcmd -ServerInstance $TargetSqlServerInstance -Query "ALTER DATABASE $($TargetDb) SET RECOVERY SIMPLE WITH NO_WAIT" # rename logical files Invoke-Sqlcmd -ServerInstance $TargetSqlServerInstance -Query "ALTER DATABASE $TargetDb MODIFY FILE (NAME='$SourceLogicalDataFileName', NEWNAME='$TargetLogicalDataFileName')" Invoke-Sqlcmd -ServerInstance $TargetSqlServerInstance -Query "ALTER DATABASE $TargetDb MODIFY FILE (NAME='$SourceLogicalLogFileName', NEWNAME='$TargetLogicalLogFileName')" # dbcccheckdb Invoke-Sqlcmd -ServerInstance $TargetSqlServerInstance -Query "DBCC checkdb ($TargetDb) --WITH NO_INFOMSGS" # display sp_helpdb Invoke-Sqlcmd -ServerInstance $TargetSqlServerInstance -Query "EXEC sp_helpdb $($TargetDb)"
The following table lists each configurable variable and its description:
Variable | Description |
---|---|
$TargetSqlServerInstance | Name of SQL Server with database we’re restoring to in the form: MachineName or MachineName\InstanceName |
$TargetDb | Name of target database we’re restoring to |
$BackupDir | Path to local drive or UNC share with backup files in the form: \\ServerName\ShareName or DriveLetter:\DirectoryName |
$SourceLogicalDataFileName | Logical data file name of source database |
$SourceLogicalLogFileName | Logical log file name of source database |
$TargetLogicalDataFileName | Logical data file name of target database |
$TargetLogicalLogFileName | Logical data file name of target database |
$TargetPhysicalDataFileName | Fully qualified path to physical data file of target database |
$SourceLogicalLogFileName | Fully qualified path to physical log file of target database |
$CompatLevel | Compatibility Level we want for target database |
To obtain the logical and physical file names for the source and target databases:
From Object Explorer in SQL Server Management Studio:
- Right click on source database
- Properties
- Files
Repeat for the target database.
- Right click on source database
- Properties
- Files
Our source database full and transaction log backups are saved in the C:\Program Files\Microsoft SQL Server\MSSQL14.SQL2017\MSSQL\Backup\RefreshProd directory. Looking at the time and date stamps, we see that RefreshProd_full_backup_2.bak is the latest full backup, and that’s the one we want to refresh the test database with. The fully qualified name is determined by the string in $BackupDir appended with the dynamically generated string in $LatestFullBackupFile.
Execute Script
After configuring the variables, I’ve saved the PowerShell as C:\DbaScripts\RefreshTestDb.ps1. Execute it and we see the output of sp_helpdb on our newly refreshed database.
The SQL Server errorlog shows us:
- C:\Program Files\Microsoft SQL Server\MSSQL14.SQL2017\MSSQL\Backup\RefreshProd, RefreshProd_full_backup_2.bak was restored
- Restore completed
- Compatibility Level was set
- Database recovery model set to SIMPLE
- DBCC CHECKDB ran without errors
Automate Script in a SQL Server Agent Job
Now, we’ll automate it in a SQL Server Agent Job.
- Expand SQL Server Agent dropdown
- Right click Jobs
- New Job…
- Name Job
- Set Job owner
- Enter Job description
- Steps
- New
- Name Step
- Choose Operating System (CmdExec) in Type dropdown
- Enter PowerShell executable name with a -File=FullyQualifiedScriptName
- OK
- Schedules
- New
- Name Job Schedule
- Choose Frequency
- OK
- OK
- Expand Jobs
- Right click on new Job
- Start Job at Step…
Look for Success in Status column.
- Right click on Job
- View History
Next Steps
Here are links to tips with a wealth of information on the following related topics:
- Database backups: SQL Server Backup Tips
- Database restores: SQL Server Restore Tips
- PowerShell: SQL Server PowerShell Tips
- SQL Agent: SQL Server Agent Tips
- Renaming database logical files: Rename logical database file name for a SQL Server database
About the author
This author pledges the content of this article is based on professional experience and not AI generated.
View all my tips
Article Last Updated: 2020-07-06