{"id":658,"date":"2009-08-28T00:00:00","date_gmt":"2009-08-28T00:00:00","guid":{"rendered":"https:\/\/test.simple-talk.com\/uncategorized\/disaster-recovery-for-sql-server-databases\/"},"modified":"2021-08-24T13:40:36","modified_gmt":"2021-08-24T13:40:36","slug":"disaster-recovery-for-sql-server-databases","status":"publish","type":"post","link":"https:\/\/www.red-gate.com\/simple-talk\/databases\/sql-server\/database-administration-sql-server\/disaster-recovery-for-sql-server-databases\/","title":{"rendered":"Disaster Recovery for SQL Server Databases"},"content":{"rendered":"<\/p>\n<div id=\"pretty\">\n<p>In this article, I&#8217;ll lay out the technical details of implementing a simple Disaster Recovery Plan (DRP) for production applications running Microsoft SQL Server. My goal is to provide you with generic documentation to use as the basis of your own production system failover strategy. You will, of course, need to alter it with your own details and keep it updated any time that changes are made to your production systems, but this should give you a good departure point from which to build your own strategy.<\/p>\n<p>I&#8217;ll describe the steps to follow in the event of the failure of a database production system, and annotate the process as I go along. This is largely based on a Disaster Recovery Plan I had to design recently (all the better for you to download and personalize), so it is deliberately written in the style of a business strategy document. I&#8217;ll also explain the advantages of automatic restoration of compressed backup files from a failover server. I&#8217;ll also be the first to admit that this topic might seem a little dry, but having a DRS will make it worth the read &#8211; I promise.<\/p>\n<p>Part 1 of this article will describe the basic steps necessary to set up a &#8216;hot&#8217; standby server (the recovery method I used when drafting this DRP), and Part 2 is an annotated transcript of Disaster Recovery document, including steps to be taken in the event of a disaster, and information for the unfortunate DBA tasked with recovering from it. Here we go:<\/p>\n<hr \/>\n<h2>PART 1 &#8211; Automatic Restoration of backup files to a failover server <\/h2>\n<p class=\"buggy\">SQL Servers&#8217; <strong>norecovery<\/strong> mode keeps the database stable and ready to accept the changes you&#8217;re progressively applying as part of the backup process. This means that it&#8217;s only necessary to apply the latest differential or log backup before the database is ready to be accessed by users and applications.<\/p>\n<p>The disaster recovery method used is to have a &#8216;hot&#8217; standby server (<em>SQL2<\/em>), which is already installed, stable and, most importantly, is an <em>exact<\/em> copy of the production server&#8217;s configuration. The standby server should already have the most recent operational databases fully-restored in <strong>norecovery<\/strong> mode.<\/p>\n<h3>Implementing a Hot Standby Server<\/h3>\n<p>After SQL Server has been installed on the failover server, you need to check that <b>Robocopy<\/b> is installed in the <em>sysroot\\windows\\system32<\/em> folder. Secondly, <strong><a href=\"https:\/\/www.red-gate.com\/products\/dba\/sql-backup\/?utm_source=simpletalk&amp;utm_medium=article&amp;utm_content=drecovery200909&amp;utm_campaign=sqlbackup\">Red Gate&#8217;s SQL Backup<\/a><\/strong> software must connect to the server and be configured by clicking the small grey square next to server listing in left pane &#8211; this is for instance auto-configuration, if it has not been done already.<\/p>\n<p class=\"illustration\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/imported\/791-SQB_installingservercomponents.gif\" width=\"463\" height=\"335\" alt=\"791-SQB_installingservercomponents.gif\" \/><\/p>\n<p class=\"caption\"><em>Figure 1 &#8211; SQL Backup&#8217;s auto-configuration system.<\/em><\/p>\n<p class=\"buggy\"><b>Robocopy<\/b> is much better than ( the soon-to-be-discontinued) <b>Xcopy<\/b>, by the way. And since Windows Server 2003, <b>Robocopy<\/b> has been the recommended \/ future-proofed tool of choice. As far as I know, <b>Xcopy<\/b> will no longer be available in future versions of Windows Server. <\/p>\n<p>Next, for the stored procedures that execute <b>Robocopy<\/b> (we place these procedures in a local database on each server called DBA_tools), you need to allow the advanced option <code>xp_cmdshell <\/code>to run:<\/p>\n<pre class=\"theme:ssms2012 lang:tsql\">-- To allow advanced options to be changed. \r\n\r\nEXEC&#160;sp_configure&#160;'show&#160;advanced&#160;options',&#160;1 \r\nGO \r\n-- To update the currently configured value \r\n-- for advanced options.\r\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;\r\nRECONFIGURE \r\n\r\nGO\r\n\r\n-- To enable the feature.\r\n\r\nEXEC&#160;sp_configure&#160;'xp_cmdshell',&#160;1 \r\nGO \r\n\r\n-- To update the currently configured value for this feature.\r\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;\r\nRECONFIGURE \r\n\r\nGO \r\n\r\n\r\n<\/pre>\n<p>In order to copy the backup files, each database on the standby server needs a database-specific SQL Server Agent job running <b>Robocopy<\/b> at the required interval to copy full and differential backups from the production server to the standby server. These jobs can be run at whatever frequency needed, be it daily, hourly or even more often if your operations require it.<\/p>\n<p><b>Robocopy<\/b> is the first step in all automated restore jobs, unless you want to add validation steps prior to the backup file copy. The following example copies all differential database backups from a production server to a DRP server:<\/p>\n<pre class=\"theme:ssms2012 lang:tsql\">EXEC&#160;dbo.usp_RoboCopy&#160;'\\\\PRODserver\\drive$\\ProdServerBackupShare\\Diff',&#160; '\\\\DRPserver\\Drive$\\ProdServerDbBackupFolder\\Diff',&#160;'database1_*&#160;database2_*' \r\n\r\n-- This case just handles the differential folder, so we're assuming you'll also have \r\n-- a Tlog and Full folder.  \r\n<\/pre>\n<p>A database-specific SQL Server Job will restore these backups daily to the hot standby server (DRP) using stored procedures specifically created for this setup, such as:<\/p>\n<ul>\n<li><code>usp_DB_Restore_Master or usp_DB_Restore_Master_Multi<\/code>  <\/li>\n<li><code>usp_DB_Restore<\/code>  <\/li>\n<li><code>usp_DB_Restore_NoRecovery<\/code>  <\/li>\n<li><code>usp_DB_Restore_differential<\/code>  <\/li>\n<li><code>usp_DB_Restore_Log <\/code><\/li>\n<\/ul>\n<h3>A consideration for the DBA regarding the level of database recovery <\/h3>\n<p class=\"buggy\">If you are currently in Simple Recovery mode, and provided there are regular Transaction Log and differential backups (as in, several times a day), you can switch your recovery model over to Bulk-Logged in production to restore up to a specific point in time. This will naturally minimize the amount of data lost from the work session prior to any downtime.<\/p>\n<p>Full Recovery mode is recommended for critical databases that require auditing compliance.<\/p>\n<p>In the event of failure, the most recent log or differential backup is ready to be applied to the standby database sitting in <b>norecovery<\/b> mode, and you&#8217;re up and running quickly with minimal down-time.<\/p>\n<p>An alternative method for a much smaller database, where the total restore time is below five minutes, is to apply the complete restore every hour to the failover server, in which case you don&#8217;t need to worry about <b>norecovery<\/b> mode.<\/p>\n<h2>PART 2 &#8211; Instructions to follow in the event of a disaster to the production system<\/h2>\n<ol>\n<li>If you haven&#8217;t heard from them directly already, please contact <strong>FIRST LINE DBA SUPPORT<\/strong> at [<em>INSERT NUMBER<\/em>] or <strong>SECONDARY DBA<\/strong> at [<em>INSERT NUMBER<\/em>]  <\/li>\n<li>After the production\/original data publisher server failure (SQL1), the restore \/ backup-subscriber server (SQL2) will be used as the primary database server (a.k.a. DRP server). Inform everyone in the department by E-mail <span class=\"pullout\">(It&#8217;s also worth thinking about who will inform internal\/external clients)<\/span>.  <\/li>\n<li>Once the switch occurs to the DRP server and the downtime of SQL1 actually happens, all application connection strings need to be changed to access SQL2. The CGI should handle this step automatically.  <\/li>\n<li>Disable Automatic Restore SQL Agents on SQL2.  <\/li>\n<li>Disable all SQL Agent jobs on failed server SQL1 if possible.  <\/li>\n<li>Enable all maintenance and backup jobs on newly active server SQL2 <\/li>\n<\/ol>\n<p>Please note that restoring a log backup is not possible if the production database recovery model is set to Simple. For fine-grained restoration, the database needs to have been using the Full recovery model &#8211; <span class=\"pullout\">Thankfully, the default setting <strong>is<\/strong> the Full Recovery model<\/span>. If point in time recoveries are requested by management on a regular basis, then we can also change the database recovery level to Bulk-Logged, if space is an issue, and Full otherwise &#8211; <span class=\"pullout\">Perhaps with deserved hesitation from the side of the Database Administrators, as Bulk-logged recovery is <em>much<\/em> more space efficient.<\/span><\/p>\n<p class=\"buggy\"><strong><em>How the automation of the restore from compressed backup is benefitial to your production environment. <\/em><\/strong>Ideally you should keep two full backups, one on the Test server and one on the DRP server. Having this second copy of the production databases will allow you to do some useful and intensive work which you don&#8217;t want to have to run on live databases, such as a full DBCC CheckDB &#8211; console commands that can check the integrity of your exact database restore copy.<\/p>\n<p>A log of what has been restored shall be placed in the following directory:<\/p>\n<p><strong><em>\\\\DatabaseServerName\\drive$\\prodBackupDir\\DBlog\\<\/em><\/strong><strong><\/strong><\/p>\n<p>As soon as a restore is completed, we should have an automatic purge of old backups &#8211; done perhaps every week (<span class=\"pullout\">maximum 14 days manually, or automatically in a SQL Server Maintenance Plan<\/span>), and which can be automated using a batch file or PowerShell Script.<\/p>\n<p>To ensure a smooth restore process, we should read the restore parameters directly from the backup log system tables &#8211; such as BackupHistory, BackupSet, BackupFile or<\/p>\n<p>Backuplog &#8211; unless a backuplog table is explicitly created in a local database or exists in msdb. This is to ensure that the essential restore parameters (such as the backup file name and position) are immediately available.<\/p>\n<p class=\"buggy\">As I often set them, the SQL Agent Restore jobs have their parameters manually set during testing and are usually left that way &#8211; but of course it&#8217;s best to pull the meta-data directly from the system in case you move files around and forget to update the restore scripts.<\/p>\n<h3>SQL1 &amp; SQL2 (Prod. &amp; DRP) Server Hardware Configuration<\/h3>\n<p class=\"buggy\">This is the configuration for the servers this document was originally written for (I don&#8217;t remember the System Models for that setup, but that&#8217;s not to say you shouldn&#8217;t record yours) Update the following with your own server properties. <\/p>\n<h4>SQL1 (production instance)<\/h4>\n<table>\n<tbody>\n<tr>\n<td>\n<p>1.1<\/p>\n<\/td>\n<td>\n<p>Server Type<\/p>\n<\/td>\n<td valign=\"top\">\n<p>Windows 2008 (standard x64 edition)<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p>1.2<\/p>\n<\/td>\n<td valign=\"top\">\n<p>System Model<\/p>\n<\/td>\n<td valign=\"top\">\n<p><em>[Server Model Number, Product Type]<\/em><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p>1.3<\/p>\n<\/td>\n<td valign=\"top\">\n<p>RAM Memory<\/p>\n<\/td>\n<td valign=\"top\">\n<p>8 Gig<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p>1.4<\/p>\n<\/td>\n<td valign=\"top\">\n<p>No. of CPU&#8217;s<\/p>\n<\/td>\n<td valign=\"top\">\n<p>2<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p>1.5<\/p>\n<\/td>\n<td valign=\"top\">\n<p>CPU &amp; Speed<\/p>\n<\/td>\n<td valign=\"top\">\n<p>AMD (x64)<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p>Drives<\/p>\n<\/td>\n<td valign=\"top\">\n<p>Hard Disk Space<\/p>\n<\/td>\n<td valign=\"top\">\n<p>C(#G);D(#G)<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<div>\n<div><\/div>\n<\/div>\n<h4>SQL2 (storage replication partner \/ hot standby restore-subscriber) <\/h4>\n<table>\n<tbody>\n<tr>\n<td valign=\"top\">\n<p>1.1<\/p>\n<\/td>\n<td valign=\"top\">\n<p>Server Type<\/p>\n<\/td>\n<td valign=\"top\">\n<p>Windows 2008 ( standard x64 edition )<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p>1.2<\/p>\n<\/td>\n<td valign=\"top\">\n<p>System Model<\/p>\n<\/td>\n<td valign=\"top\">\n<p><em>[Server Model Number, Product Type] &#8211; Same as SQL1<\/em><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p>1.3<\/p>\n<\/td>\n<td valign=\"top\">\n<p>RAM Memory<\/p>\n<\/td>\n<td valign=\"top\">\n<p>9 Gig<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p>1.4<\/p>\n<\/td>\n<td valign=\"top\">\n<p>No. of CPU&#8217;s<\/p>\n<\/td>\n<td valign=\"top\">\n<p>2<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p>1.5<\/p>\n<\/td>\n<td valign=\"top\">\n<p>CPU &amp; Speed<\/p>\n<\/td>\n<td valign=\"top\">\n<p>AMD (x64) Opteron Processor 280<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p>Drives<\/p>\n<\/td>\n<td valign=\"top\">\n<p>Hard Disk Space<\/p>\n<\/td>\n<td valign=\"top\">\n<p>C(#G); D(#G); F(2TB); G(250GB); H (1.5TB); Z(20GB)<\/p>\n<p><span class=\"pullout\">This server should have terabytes and terabytes of space, depending on your archiving needs.<\/span><\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h3>SQL Server Configuration<\/h3>\n<p class=\"buggy\">For a previous client our production build of SQL Server was 9.0.3152, so naturally the DRP server had to be the exact same build &#8211; both systems must be as identical as possible.<\/p>\n<p>Our servers are using 64-bit versions of the SQL Database Engine 2005\/8, with at least service pack 2 (2005), cu3 (2008) installed, and the collation type is Latin1_General_CI_AS (accent sensitive is recommended).&#160;&#160; It is preferable to have at least Cumulative Rollup package 8 or SP3 for SQL Server 2005, and it&#8217;s important to do an update to production build levels of SQL on a regular basis.<\/p>\n<p>Detailed information for the server and databases is included in the compiled help file located on both servers SQL1 and SQL2 <\/p>\n<p><strong>D:\\DRP\\ServerName.chm <\/strong><span class=\"pullout\">(i.e. make it very easy to find DRP info) <\/span><\/p>\n<h3>Critical SQL Server User Database Details <\/h3>\n<h4>1. List of databases<\/h4>\n<p class=\"indented\"><strong>Database1<\/strong><\/p>\n<p class=\"indented\"><strong>Database2<\/strong><\/p>\n<p class=\"indented\"><strong>&#8230;<\/strong><\/p>\n<p class=\"indented\"><em>NB: &#160;We will not be doing master, msdb, model or temp &#8211; these are backed up on a regular basis and will be copied by <b>Robocopy<\/b> although not restored onto the database restore replication subscriber directly.<\/em><\/p>\n<h4>&#160;2. Database Maintenance Plan and Auto-Restore.<\/h4>\n<p class=\"indented\">In general, our database restore plan will reflect exactly the backup schedule and wait for backups to finish by querying the metadata from the production server. The restore jobs will check to see if the days&#8217; full backup has completed (or daily diff.) using the backupset.backup_finish_date&#160; column.&#160; Once we see that Full backup has been completed on the production server, we copy the backupfile over to the hot standby server. In the second step of the job, we continue to execute the code from the appropriate usp_DB_restore combined with the metadata extraction from the system tables.<\/p>\n<h4>3. Database Backup schedule in production<strong> <\/strong><\/h4>\n<table>\n<tbody>\n<tr>\n<td valign=\"top\">\n<p><strong>Maintenance Job Name <\/strong><\/p>\n<\/td>\n<td valign=\"top\">\n<p><strong>Maintenance Job Description <\/strong><\/p>\n<\/td>\n<td valign=\"top\">\n<p><strong>Freq <\/strong><\/p>\n<\/td>\n<td valign=\"top\">\n<p><strong>Time to Run <\/strong><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p>BackupFull_Database1<\/p>\n<\/td>\n<td valign=\"top\">\n<p>Full Database backup Database1<\/p>\n<\/td>\n<td valign=\"top\">\n<p>W<\/p>\n<\/td>\n<td>\n<p>Sunday 6:00<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p>BackupFull_Database2<\/p>\n<\/td>\n<td valign=\"top\">\n<p>Full Database backup Database2<\/p>\n<\/td>\n<td valign=\"top\">\n<p>W<\/p>\n<\/td>\n<td>\n<p>Sunday 6:30<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p>&#8230;<\/p>\n<\/td>\n<td valign=\"top\">\n<p>&#8230;<\/p>\n<\/td>\n<td valign=\"top\">\n<p>&#8230;<\/p>\n<\/td>\n<td>\n<p>&#8230;<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h4>4. Restore jobs on DRP server<\/h4>\n<table>\n<tbody>\n<tr>\n<td valign=\"top\">\n<p><strong>Maintenance Job Name<\/strong><\/p>\n<\/td>\n<td valign=\"top\">\n<p><strong>Maintenance Job Description <\/strong><\/p>\n<\/td>\n<td valign=\"top\">\n<p><strong>Freq <\/strong><\/p>\n<\/td>\n<td valign=\"top\">\n<p><strong>Time to Run <\/strong><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p>BackupFull_Database1<\/p>\n<\/td>\n<td valign=\"top\">\n<p>Full Database backup Database1<\/p>\n<\/td>\n<td valign=\"top\">\n<p>W<\/p>\n<\/td>\n<td>\n<p>Sunday 6:00<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p>BackupFull_Database2<\/p>\n<\/td>\n<td valign=\"top\">\n<p>Full Database backup Database2<\/p>\n<\/td>\n<td valign=\"top\">\n<p>W<\/p>\n<\/td>\n<td>\n<p>Sunday 6:30<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p>&#8230;<\/p>\n<\/td>\n<td valign=\"top\">\n<p>&#8230;<\/p>\n<\/td>\n<td valign=\"top\">\n<p>&#8230;<\/p>\n<\/td>\n<td>\n<p>&#8230;<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h3>Critical Scripts, Procedures and Programs related to disaster recovery<\/h3>\n<p>Following is a list of all the code used for the DRP process from SQL1 to SQL2:<\/p>\n<h4>usp_DB_Restore_Master<\/h4>\n<pre class=\"theme:ssms2012 lang:tsql\">CREATE&#160;PROC&#160;usp_DB_Restore_MasterRecovery \r\n\r\n--&#160;Add&#160;the&#160;database&#160;name&#160;and&#160;input&#160;variables,&#160;instead&#160;of&#160;setting&#160;them&#160;on lines 40-23.\r\n&#160;&#160;&#160;&#160;&#160;&#160;\r\nAS \r\nDECLARE&#160;\r\n@filename&#160;&#160;&#160;&#160;&#160;VARCHAR(255) \r\n&#160;&#160;&#160;&#160;&#160;&#160;,\r\n@cmd&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;VARCHAR(500) \r\n&#160;&#160;&#160;&#160;&#160;&#160;,\r\n@cmd2&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;VARCHAR(500) \r\n&#160;&#160;&#160;&#160;&#160;&#160;,\r\n@dbNameSource&#160;sysname&#160;--&#160;This&#160;is&#160;an&#160;input&#160;parameter,&#160;unless&#160;you&#160;are testing. \r\n&#160;&#160;&#160;&#160;&#160;&#160;\r\n,@dbNameTarget&#160;sysname&#160;--&#160;This&#160;is&#160;an&#160;input&#160;parameter,&#160;unless&#160;you&#160;are testing.\r\n&#160;&#160;&#160;&#160;&#160;&#160;\r\n,@FullRestoreFolder&#160;NVARCHAR(MAX)--&#160;This&#160;is&#160;an&#160;input&#160;parameter,&#160;unless&#160;you \r\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;-- are testing.\r\n&#160;&#160;&#160;&#160;&#160;&#160;\r\n,@dbNameStatement&#160;NVARCHAR(MAX) \r\n&#160;&#160;&#160;&#160;&#160;&#160;,\r\n@dbNameStatementDiff&#160;NVARCHAR(MAX) \r\n&#160;&#160;&#160;&#160;&#160;&#160;,\r\n@LogicalName&#160;&#160;&#160;&#160;&#160;&#160;VARCHAR(255) \r\n&#160;&#160;&#160;&#160;&#160;&#160;,\r\n@PhysicalName&#160;&#160;&#160;&#160;&#160;VARCHAR(255) \r\n&#160;&#160;&#160;&#160;&#160;&#160;,\r\n@Type&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;VARCHAR(20)--&#160;Useful if reading the backup headers, no? &#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;-- As part of&#160;the&#160;validation&#160;perhaps.\r\n&#160;&#160;&#160;&#160;&#160;&#160;\r\n,@FileGroupName&#160;&#160;&#160;&#160;VARCHAR(255) \r\n&#160;&#160;&#160;&#160;&#160;&#160;,\r\n@Size&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;VARCHAR(20) \r\n&#160;&#160;&#160;&#160;&#160;&#160;,\r\n@MaxSize&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;VARCHAR(20) --&#160;Check&#160;what&#160;I&#160;do&#160;above&#160;and use&#160;what's&#160;below &#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;-- if relevant.\r\n&#160;&#160;&#160;&#160;&#160;&#160;\r\n,@filelistStatmt1&#160;VARCHAR(MAX) \r\n&#160;&#160;&#160;&#160;&#160;&#160;,\r\n@filelistStatmtDiff&#160;VARCHAR(MAX) \r\n\r\n\r\n\/* The following&#160;variables are&#160;set&#160;up&#160;for&#160;testing&#160;and&#160;may&#160;be&#160;taken&#160;off&#160;when&#160;sp&#160;is used afterwards (if&#160;we&#160;cannot&#160;get&#160;them&#160;reliably&#160;from&#160;sys&#160;databases&#160;automatically). *\/\r\n\r\n&#160;&#160;&#160;&#160;&#160;&#160;\r\n,@backupFile&#160;sysname&#160;&#160;--&#160;will&#160;grab&#160;from&#160;local&#160;test&#160;.sqb&#160;files&#160;first. \r\n&#160;&#160;&#160;&#160;&#160;&#160;\r\n,@logicalDataFile&#160;sysname\r\n\r\n\r\n\/* I&#160;am&#160;developing&#160;this&#160;code&#160;first&#160;assuming&#160;that&#160;we will only have one data file and logical file for each database. Later we'll add support for multiple logical and physical files&#160;(there&#160;may&#160;be,&#160;in Database1's case, more than one row for dbo.sysfiles\r\nwhere&#160;fileid=1&#160;and&#160;groupid=1). *\/\r\n\r\n&#160;&#160;&#160;&#160;&#160;&#160;\r\n,@logicalDataStmt1&#160;NVARCHAR(MAX) \r\n&#160;&#160;&#160;&#160;&#160;&#160;,\r\n@logicalDataStmt2&#160;NVARCHAR(MAX) \r\n&#160;&#160;&#160;&#160;&#160;&#160;,\r\n@logicalDataStmt3&#160;NVARCHAR(MAX) \r\n&#160;&#160;&#160;&#160;&#160;&#160;,\r\n@logicalLogFile&#160;sysname&#160;&#160;--&#160;Returned&#160;and&#160;verified.\r\n&#160;&#160;&#160;&#160;&#160;&#160;\r\n,@logicalLogStmt1&#160;NVARCHAR(MAX)&#160;--&#160;So&#160;many&#160;annoying&#160;variables. If I could just&#160; \r\n&#160;&#160;&#160;&#160;&#160;&#160;\r\n,@logicalLogStmt2&#160;NVARCHAR(MAX)&#160;--&#160;read&#160;the&#160;header&#160;it'd be easier in the future.\r\n&#160;&#160;&#160;&#160;&#160;&#160;\r\n,@logicalLogStmt3&#160;NVARCHAR(MAX)\r\n&#160;&#160;&#160;&#160;&#160;&#160;,\r\n@physicalDataFile&#160;sysname&#160;--&#160;Easy&#160;to&#160;grab&#160;since&#160;it&#160;was&#160;in&#160;master. \r\n&#160;&#160;&#160;&#160;&#160;&#160;\r\n,@physicalLogFile&#160;sysname&#160;--&#160;Need&#160;two&#160;variables.\r\n&#160;&#160;&#160;&#160;&#160;&#160;\r\n,@physicalLogFileStmt1&#160;NVARCHAR(MAX) \r\n&#160;&#160;&#160;&#160;&#160;&#160;,\r\n@physicalLogFileStmt2&#160;NVARCHAR(MAX) \r\n&#160;&#160;&#160;&#160;&#160;&#160;,\r\n@physicalLogFileStmt3&#160;NVARCHAR(MAX) \r\n&#160;&#160;\r\nSET&#160;NOCOUNT&#160;ON --&#160;Following&#160;best&#160;practices,&#160;although&#160;we're&#160;not&#160;throwing&#160;around big \r\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;-- counts&#160;anyway.\r\n\r\n-- Parameters&#160;and&#160;variables&#160;set&#160;by&#160;Hugo&#160;for&#160;testing.\r\n&#160;&#160;\r\nSET&#160;@FullRestoreFolder='\\\\testServer\\Drive$\\ProdServerBackupFolder\\full\\' \r\n&#160;&#160;\r\nSET&#160;@dbNameSource&#160;='Database1' \r\n&#160;&#160;\r\nSET&#160;@dbNameTarget&#160;='Database1'&#160;&#160;--&#160;Sometimes&#160;we&#160;want&#160;to&#160;over-write another database \r\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;-- (e.g.&#160;in&#160;the&#160;case&#160;of&#160;importpdm_tst).\r\n&#160;&#160;\r\nSET&#160;@physicalDatafile=( \r\n&#160;&#160;\r\nSELECT&#160;filename&#160; \r\n&#160;&#160;&#160;&#160;\r\nFROM&#160;MASTER.dbo.sysdatabases&#160; \r\n&#160;&#160;&#160;&#160;\r\nWHERE&#160;NAME=@dbnameTarget) \r\n&#160;&#160;&#160;&#160;&#160;&#160;\r\nPRINT&#160;'The&#160;physical&#160;data&#160;FILE&#160;TO&#160;RESTORE&#160;IS&#160;'+@physicalDatafile \r\n&#160;&#160;\r\nSET&#160;@logicalDataStmt1='select&#160;top&#160;1&#160;name&#160;from&#160;[' \r\n&#160;&#160;\r\nSET&#160;@logicalDataStmt2='].dbo.sysfiles&#160;where&#160;fileid=1&#160;and&#160;groupid=1' \r\n&#160;&#160;\r\nSET&#160;@logicalDataStmt3&#160;=&#160;(@logicalDataStmt1+@dbNameTarget+@logicalDataStmt2)\r\n\r\n\r\n\/* I&#160;wanted&#160;to&#160;do&#160;an&#160;execute&#160;at&#160;this&#160;line&#160;but&#160;I&#160;kept&#160;thinking&#160;that&#160;if&#160;it&#160;was within a single&#160;statement,&#160;my&#160;set&#160;statement&#160;@logicalDataFile&#160;would&#160;take&#160;the result of the&#160;query (select&#160;top&#160;1&#160;name&#160;from&#160;PROD_PASRAA.dbo.sysfiles&#160;where&#160;fileid)=1 and groupid=1. *\/\r\n\r\n\r\nCREATE&#160;TABLE&#160;#logicalDataFile&#160;&#160;--&#160;Drop&#160;table&#160;#logicaldatafile.\r\n&#160;&#160;\r\n( \r\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;\r\nlogicalDataFile&#160;sysname \r\n&#160;&#160;\r\n) \r\n\r\nINSERT&#160;INTO&#160;[#logicalDataFile] \r\n&#160;&#160;\r\n( \r\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;\r\nlogicalDataFile \r\n&#160;&#160;\r\n) \r\n&#160;&#160;\r\nEXEC&#160;(@logicalDataStmt3) \r\n\r\n--&#160;Now&#160;set&#160;the&#160;variable&#160;from&#160;the&#160;temp&#160;finally.\r\n&#160;&#160;\r\nSET&#160;@logicalDataFile=( \r\n&#160;&#160;\r\nSELECT&#160;*&#160; \r\n&#160;&#160;&#160;&#160;\r\nFROM&#160;#logicalDataFile) \r\n\r\n--&#160;Check&#160;out&#160;a&#160;temp&#160;table&#160;method...need&#160;the&#160;result&#160;set&#160;from.\r\n&#160;&#160;&#160;&#160;&#160;&#160;\r\nPRINT&#160;'the&#160;logical&#160;data&#160;file&#160;is&#160;'+@logicalDataFile \r\n&#160;&#160;\r\nSET&#160;@logicalLogStmt1='select&#160;top&#160;1&#160;name&#160;from&#160;[' \r\n&#160;&#160;\r\nSET&#160;@logicalLogStmt2='].dbo.sysfiles&#160;where&#160;fileid=2&#160;and&#160;groupid=0' \r\n\r\n--&#160;Put&#160;the&#160;statement&#160;together.\r\n&#160;&#160;\r\nSET&#160;@logicalLogStmt3&#160;=&#160;(@logicalLogStmt1+@dbNameTarget+@logicalLogStmt2)&#160;&#160;\r\n\r\nCREATE&#160;TABLE&#160;#logicalLogfile&#160;&#160;--&#160;Drop&#160;table&#160;#logicalLogfile.\r\n&#160;&#160;\r\n( \r\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;\r\nlogicalLogFile&#160;sysname \r\n&#160;&#160;\r\n) \r\n\r\nINSERT&#160;INTO&#160;[#logicalLogFile] \r\n&#160;&#160;\r\n( \r\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;\r\nlogicallogFile \r\n&#160;&#160;\r\n) \r\n&#160;&#160;\r\nEXEC&#160;(@logicallogStmt3) \r\n\r\n--&#160;Now&#160;set&#160;the&#160;variable&#160;from&#160;the&#160;temp&#160;finally.\r\n&#160;&#160;\r\nSET&#160;@logicalLogFile=( \r\n&#160;&#160;\r\nSELECT&#160;*&#160; \r\n&#160;&#160;&#160;&#160;\r\nFROM&#160;#logicalLogFile) \r\n&#160;&#160;&#160;&#160;&#160;&#160;\r\nPRINT&#160;'the&#160;logical&#160;log&#160;file&#160;is&#160;'+@logicalLogFile&#160;--&#160;Has&#160;the&#160;right&#160;value&#160;&#160; \r\n--&#160;grab&#160;the&#160;last&#160;db&#160;file&#160;related&#160;variable&#160;from&#160;sysfiles.\r\n&#160;&#160;\r\nSET&#160;@physicalLogFileStmt1='select&#160;filename&#160;from&#160;[' \r\n&#160;&#160;\r\nSET&#160;@physicalLogFileStmt2='].dbo.sysfiles&#160;where&#160;fileid=2&#160;and&#160;groupid=0' \r\n&#160;&#160;\r\nSET&#160;@physicalLogFileStmt3&#160;=&#160;(@physicalLogFileStmt1+@dbNameTarget+@physicalLogFileStmt2) \r\n\r\nCREATE&#160;TABLE&#160;#physicalLogFile&#160;&#160;--&#160;Drop&#160;table&#160;#physicalLogFilefile.\r\n&#160;&#160;\r\n( \r\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;\r\nphysicalLogFile&#160;sysname \r\n&#160;&#160;\r\n) \r\n\r\nINSERT&#160;INTO&#160;[#physicalLogFile] \r\n&#160;&#160;\r\n( \r\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;\r\nphysicalLogFile \r\n&#160;&#160;\r\n) \r\n&#160;&#160;\r\nEXEC&#160;(@physicalLogFileStmt3) \r\n\r\n--&#160;Now&#160;set&#160;the&#160;variable&#160;from&#160;the&#160;temp&#160;finally.\r\n&#160;&#160;\r\nSET&#160;@physicalLogFile=( \r\n&#160;&#160;\r\nSELECT&#160;*&#160; \r\n&#160;&#160;&#160;&#160;\r\nFROM&#160;#physicalLogFile) \r\n&#160;&#160;&#160;&#160;&#160;&#160;\r\nPRINT&#160;'the&#160;physical&#160;log&#160;file&#160;is&#160;'+@physicalLogFile\r\n\r\n\r\n\/*&#160;All&#160;verified&#160;up&#160;to&#160;here,&#160;and&#160;we're&#160;ready&#160;for&#160;some&#160;backup&#160;logic.Grab&#160;the\r\ncorresponding file&#160;for the&#160;restore&#160;folder, and&#160;according&#160;to&#160;following&#160;inputs\r\nfull restore&#160;file. *\/\r\n\r\n\r\nCREATE&#160;TABLE&#160;#dirList&#160;(filename&#160;NVARCHAR(MAX)) \r\n\r\nCREATE&#160;TABLE&#160;#filelist&#160;( \r\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;\r\nLogicalName&#160;NVARCHAR(MAX) \r\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;,\r\nPhysicalName&#160;NVARCHAR(MAX) \r\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;,\r\n[Type]&#160;VARCHAR(20) \r\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;,\r\nFileGroupName&#160;VARCHAR(50) \r\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;,\r\nSize&#160;VARCHAR(20) \r\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;,\r\nMaxSize&#160;VARCHAR(20) \r\n&#160;&#160;) \r\n\r\n-- Get&#160;the&#160;list&#160;of&#160;database&#160;backups&#160;that&#160;are&#160;in&#160;the&#160;restoreFromDir&#160;directory.\r\n\r\nIF&#160;@dbNameSource&#160;IS&#160;NULL -- If @OneDBName is null.\r\n--&#160;For&#160;our&#160;purposes&#160;we&#160;are&#160;only&#160;using&#160;one&#160;database&#160;name&#160;for this.\r\n&#160;&#160;\r\nSELECT&#160;@cmd&#160;=&#160;'dir&#160;\/b&#160;\/on&#160;\"'&#160;+@FullRestoreFolder+&#160;'\"' -- Select @cmd&#160;=&#160;'dir&#160;\/b&#160;\/on '\r\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;-- +@restoreFromDir+\r\n\r\nELSE \r\nSELECT&#160;\r\n@cmd&#160;=&#160;'dir&#160;\/b&#160;\/o-d&#160;\/o-g&#160;\"'&#160;+@FullRestoreFolder+&#160;'\"' \r\n\r\nINSERT&#160;#dirList&#160; \r\n&#160;&#160;\r\nEXEC&#160;master..xp_cmdshell&#160;@cmd \r\n\r\n--&#160;Select&#160;filename&#160;from&#160;#dirlist&#160;whose&#160;list&#160;of&#160;files&#160;is&#160;good.\r\n\r\nSELECT&#160;@dbNameStatement=&#160;'full%_'+@dbnameSource+'_200%_%.sqb'&#160;&#160;-&#160;SQB&#160;IS&#160;FOR&#160;SQLBACKUP \r\n&#160;&#160;\r\nSET&#160;@filelistStatmt1=( \r\n&#160;&#160;\r\nSELECT&#160;TOP&#160;1&#160;filename&#160; \r\n&#160;&#160;&#160;&#160;\r\nFROM&#160;#dirList&#160; \r\n&#160;&#160;&#160;&#160;\r\nWHERE&#160;filename&#160;LIKE&#160;@dbNameStatement) \r\n&#160;&#160;&#160;&#160;&#160;&#160;\r\nPRINT&#160;'This&#160;is&#160;the&#160;full&#160;backup&#160;file&#160;to&#160;be&#160;restored&#160;'+@filelistStatmt1 \r\n&#160;&#160;\r\nSET&#160;@backupfile=(@FullRestoreFolder)+(@filelistStatmt1) \r\n&#160;&#160;&#160;&#160;&#160;&#160;\r\nPRINT&#160;'this&#160;is&#160;the&#160;full&#160;path&#160;to&#160;the&#160;full&#160;restore&#160;file&#160;that&#160;will&#160;be&#160;restored&#160;'\r\n+\r\n@backupfile \r\n\r\nEXEC&#160;DBA_Tools.dbo.[usp_DB_Restore]&#160;&#160;@backupfile,&#160;@dbnameTarget,&#160;&#160;@logicalDataFile,\r\n\r\n@logicalLogFile,&#160;@physicalDataFile,&#160;@physicalLogFile\r\n<\/pre>\n<h4>usp_DB_Backup &amp; usp_DB_Restore<\/h4>\n<pre class=\"theme:ssms2012 lang:tsql\">--&#160;=============================================&#160; \r\n--&#160;Description:&#160;Restore&#160;Database&#160; \r\n--&#160;Parameter1:&#160;Restore&#160;File&#160;Name&#160; \r\n--&#160;Parameter2:&#160;Full&#160;path&#160;of&#160;file&#160;location&#160;i.e.&#160;'DriveName:\\BackupShare\\'&#160; \r\n--&#160;Parameter3:&#160;RestoreType&#160; \r\n--&#160;FDN=full&#160;or&#160;differential&#160;no&#160;recovery,&#160;FDR&#160;=&#160;full&#160;or&#160;differential&#160;with&#160;recovery,&#160; \r\n--&#160;LN=log&#160;no&#160;recovery,&#160;LR=log&#160;with&#160;recovery&#160; \r\n--&#160;File&#160;Extensions:&#160;Full&#160;=&#160;*.bak&#160;,&#160;Differential=&#160;*.dif,&#160;T- Log= *.trn , *.sqb\r\n-- (SQLBackup)\r\n--&#160;=============================================&#160; \r\n\r\nCREATE&#160;PROCEDURE&#160;[dbo].[usp_DB_restore] \r\n&#160;&#160;\r\n@RestoreFileName&#160;SYSNAME, \r\n&#160;&#160;\r\n@LogicalNameData&#160;SYSNAME, \r\n&#160;&#160;\r\n@LogicalNameLog&#160;SYSNAME, \r\n&#160;&#160;\r\n@RestorePathData&#160;SYSNAME, \r\n&#160;&#160;\r\n@RestorePathLog&#160;SYSNAME, \r\n&#160;&#160;\r\n@ResoreType&#160;CHAR(1) \r\n&#160;&#160;&#160;&#160;&#160;&#160;\r\nAS \r\n&#160;&#160;BEGIN \r\n&#160;&#160;SET&#160;\r\nNOCOUNT&#160;ON&#160;; \r\n&#160;&#160;\r\nDECLARE&#160;@SqlCmd&#160;NVARCHAR(2000) \r\n&#160;&#160;\r\nDECLARE&#160;@DateTime&#160;SYSNAME \r\n&#160;&#160;\r\nDECLARE&#160;@BakupFile&#160;NVARCHAR(1400), \r\n&#160;&#160;&#160;&#160;\r\n@DiffFile&#160;NVARCHAR(1400), \r\n&#160;&#160;&#160;&#160;\r\n@LogFile&#160;NVARCHAR(1400) \r\n&#160;&#160;\r\nIF&#160;@ResoreType&#160;=&#160;'FDN' \r\n&#160;&#160;&#160;&#160;\r\nSET&#160;@SqlCmd&#160;=&#160;'RESTORE&#160;DATABASE&#160;'&#160;+&#160;QUOTENAME(@DBName) \r\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;+&#160;\r\n'&#160;TO&#160;DISK&#160;=&#160;'&#160;+&#160;@Bakupfile&#160;+&#160;'WITH&#160;INIT' \r\n&#160;&#160;&#160;&#160;\r\nIF&#160;@ResoreType&#160;=&#160;'FDR' \r\n&#160;&#160;&#160;&#160;\r\nSET&#160;@SqlCmd&#160;=&#160;'RESTORE&#160;DATABASE&#160;'&#160;+&#160;QUOTENAME(@DBName) \r\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;+&#160;\r\n'&#160;TO&#160;DISK&#160;=&#160;'&#160;+&#160;@Bakupfile&#160;+&#160;'WITH&#160;INIT' \r\n&#160;&#160;&#160;&#160;\r\nIF&#160;@ResoreType&#160;=&#160;'LN' \r\n&#160;&#160;&#160;&#160;\r\nSET&#160;@SqlCmd&#160;=&#160;'RESTORE&#160;LOG&#160;'&#160;+&#160;QUOTENAME(@DBName) \r\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;+&#160;\r\n'&#160;TO&#160;DISK&#160;=&#160;'&#160;+&#160;@LogFile&#160;+ \r\n&#160;&#160;&#160;&#160;\r\nIF&#160;@ResoreType&#160;=&#160;'LR' \r\n&#160;&#160;&#160;&#160;\r\nSET&#160;@SqlCmd&#160;=&#160;'RESTORE&#160;LOG&#160;'&#160;+&#160;QUOTENAME(@DBName) \r\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;+&#160;\r\n'&#160;TO&#160;DISK&#160;=&#160;'&#160;+&#160;@LogFile&#160;+ \r\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;\r\nPRINT&#160;@SqlCmd \r\n&#160;&#160;&#160;&#160;\r\nEXECUTE&#160;sp_executesql&#160;@SqlCmd \r\n&#160;&#160;\r\nEND \r\n\r\n\r\n<\/pre>\n<h4>usp_DB_Restore_NoRecovery&#160; <\/h4>\n<p class=\"buggy\"><em>Same as above, but for databases that need to be left in no recovery mode (e.g. waiting for a log backup to be applied or differential)<\/em><\/p>\n<pre class=\"theme:ssms2012 lang:tsql\">CREATE&#160;&#160;PROC&#160;[dbo].[usp_DB_Restore_NoRecovery] &#160;--&#160;input&#160;variables&#160;when&#160;it&#160;all&#160;works&#160;below&#160; \r\n&#160;&#160;\r\n@backupfile&#160;SYSNAME, \r\n&#160;&#160;\r\n@dbName&#160;SYSNAME, \r\n&#160;&#160;\r\n@logicalDataFile&#160;SYSNAME, \r\n&#160;&#160;\r\n@logicalLogFile&#160;SYSNAME, \r\n&#160;&#160;\r\n@physicalDatafile&#160;SYSNAME, \r\n&#160;&#160;\r\n@physicalLogFile&#160;SYSNAME \r\n&#160;&#160;&#160;&#160;&#160;&#160;\r\nAS \r\nDECLARE&#160;\r\n@exitcode&#160;INT \r\nDECLARE&#160;\r\n@sqlerrorcode&#160;INT \r\nDECLARE&#160;\r\n@restoreStmt&#160;NVARCHAR(MAX) \r\n&#160;&#160;\r\nSET&#160;NOCOUNT&#160;ON \r\n\r\n--&#160;Kill&#160;any&#160;users&#160;in&#160;the&#160;database&#160;nicely?&#160;well,&#160;not&#160;really&#160;nicely.\r\n\r\nEXEC&#160;usp_KillConnections&#160;@dbName \r\n&#160;&#160;\r\nSET&#160;@restoreStmt&#160;=&#160;N'-SQL&#160;RESTORE&#160;DATABASE&#160;'&#160;+&#160;@dbName&#160;+&#160;'&#160; \r\nFROM&#160;DISK&#160;=&#160;'&#160;\r\n+&#160;@backupfile&#160;+&#160;'&#160; \r\nWITH&#160;NORECOVERY&#160; \r\n,MOVE&#160;'&#160;\r\n+&#160;@logicalDataFile&#160;+&#160;'&#160;TO&#160;'&#160;+&#160;@physicalDatafile&#160;+&#160;'&#160; \r\n,MOVE&#160;'&#160;\r\n+&#160;@logicalLogFile&#160;+&#160;'&#160;TO&#160;'&#160;+&#160;@physicalLogFile&#160;+&#160;'&#160; \r\n,REPLACE&#160; \r\n,LOGTO&#160;=&#160;\"\\\\ServerName\\Drive$\\SourceServerName\\DBlog&lt;DATABASE&gt;_&lt;TYPE&gt;_ &lt;DATETIME&#160;yyyymmddhhmss&gt;.txt\"' \r\n\r\n--PRINT&#160;@restoreStmt\r\n\r\nEXEC&#160;master..sqlbackup&#160;@restoreStmt,&#160;@exitcode&#160;OUT,&#160;@sqlerrorcode&#160;OUT \r\n\r\nIF&#160;(&#160;@exitcode&#160;&gt;=&#160;500&#160;) \r\n&#160;&#160;&#160;&#160;&#160;&#160;OR&#160;(&#160;\r\n@sqlerrorcode&#160;&lt;&gt;&#160;0&#160;) \r\n&#160;&#160;\r\nBEGIN \r\n&#160;&#160;RAISERROR&#160;\r\n(&#160;'SQL&#160;Restore&#160;failed&#160;with&#160;exit&#160;code:&#160;%d&#160;SQL&#160;error&#160;code:&#160;%d', \r\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;\r\n16,&#160;1,&#160;@exitcode,&#160;@sqlerrorcode&#160;) \r\n&#160;&#160;\r\nEND \r\n\r\n\r\n<\/pre>\n<h4>usp_DB_Restore_Differential<\/h4>\n<pre class=\"theme:ssms2012 lang:tsql\">--&#160;restore&#160;directly&#160;from&#160;our&#160;copy&#160;which&#160;is&#160;automatically&#160;brought&#160;local&#160;using&#160;robocopy&#160; \r\n--&#160;EXEC&#160;[usp_DB_restore_Differential] \r\n-- '\\\\TestServer\\Drive$\\ProductionServer\\Diff\\\r\n-- DIFF_ServerName_DB_20080301_210001.sqb', \r\n-- 'DBname',&#160;'LogicalDataFileName',&#160; 'LogicalLogFileName', \r\n-- 'Drive:\\Program&#160;Files\\Microsoft&#160;SQL&#160;Server\\MSSQL.1\\MSSQL\\Data\\Database1.mdf', \r\n-- 'Drive:\\Program&#160;Files\\Microsoft&#160;SQL&#160;Server\\MSSQL.1\\MSSQL\\Data\\\r\n-- Database1_log.ldf'&#160; \r\n--&#160;drop&#160;proc&#160;[usp_DB_restore_Differential]\r\n\r\n\r\nCREATE&#160;&#160;PROC&#160;[dbo].[usp_DB_Restore_Differential] \r\n\r\n--&#160;Input&#160;variables&#160;when&#160;it&#160;all&#160;works&#160;below.\r\n&#160;&#160;\r\n@backupfile&#160;SYSNAME, \r\n&#160;&#160;\r\n@dbName&#160;SYSNAME, \r\n&#160;&#160;\r\n@logicalDataFile&#160;SYSNAME, \r\n&#160;&#160;\r\n@logicalLogFile&#160;SYSNAME, \r\n&#160;&#160;\r\n@physicalDatafile&#160;SYSNAME, \r\n&#160;&#160;\r\n@physicalLogFile&#160;SYSNAME \r\n&#160;&#160;&#160;&#160;&#160;&#160;\r\nAS \r\nDECLARE&#160;\r\n@exitcode&#160;INT \r\nDECLARE&#160;\r\n@sqlerrorcode&#160;INT \r\nDECLARE&#160;\r\n@restoreStmt&#160;NVARCHAR(MAX) \r\n&#160;&#160;\r\nSET&#160;NOCOUNT&#160;ON \r\n\r\nEXEC&#160;usp_KillConnections&#160;@dbName \r\n\r\n--&#160;WITH&#160;RECOVERY&#160;is&#160;used&#160;after&#160;a&#160;full restore&#160;is&#160;done&#160;already, and a final \r\n-- differential is applied to&#160;it&#160;(restore&#160;differential&#160;should&#160;be&#160;on&#160;a db in\r\n-- NORECOVERY MODE).\r\n&#160;&#160;\r\nSET&#160;@restoreStmt&#160;=&#160;N'-SQL&#160;RESTORE&#160;DATABASE&#160;'&#160;+&#160;@dbName&#160;+&#160;'&#160; \r\nFROM&#160;DISK&#160;=&#160;'&#160;\r\n+&#160;@backupfile&#160;+&#160;'&#160; \r\nWITH&#160;NORECOVERY&#160; \r\n,MOVE&#160;'&#160;\r\n+&#160;@logicalDataFile&#160;+&#160;'&#160;TO&#160;'&#160;+&#160;@physicalDatafile&#160;+&#160;' \r\n,MOVE&#160;'&#160;\r\n+&#160;@logicalLogFile&#160;+&#160;'&#160;TO&#160;'&#160;+&#160;@physicalLogFile&#160;+&#160;'&#160; \r\n,REPLACE&#160; \r\n,LOGTO&#160;=&#160;\"\\\\DRPServerName\\Drive$\\ProdServerBackupFolder\\DBlog\\&lt;DATABASE&gt;_&lt;TYPE&gt;_ &lt;DATETIME&#160;yyyymmddhhmss&gt;.txt\"' \r\n\r\n-- PRINT&#160;@restoreStmt&#160; \r\n\r\nEXEC&#160;master..sqlbackup&#160;@restoreStmt,&#160;@exitcode&#160;OUT,&#160;@sqlerrorcode&#160;OUT \r\n\r\nIF&#160;(&#160;@exitcode&#160;&gt;=&#160;500&#160;) \r\n&#160;&#160;&#160;&#160;&#160;&#160;OR&#160;(&#160;\r\n@sqlerrorcode&#160;&lt;&gt;&#160;0&#160;) \r\n&#160;&#160;\r\nBEGIN \r\n&#160;&#160;RAISERROR&#160;\r\n(&#160;'SQL&#160;Restore&#160;failed&#160;with&#160;exit&#160;code:&#160;%d&#160;SQL&#160;error&#160;code:&#160;%d', \r\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;\r\n16,&#160;1,&#160;@exitcode,&#160;@sqlerrorcode&#160;) \r\n&#160;&#160;\r\nEND \r\n\r\n\r\n<\/pre>\n<h3>usp_DB_Restore_Log <\/h3>\n<p class=\"buggy\"><em>Should allow multiple logs to be automatically applied<\/em><\/p>\n<pre class=\"theme:ssms2012 lang:tsql\">SET&#160;ANSI_NULLS&#160;ON \r\n\r\nGO \r\n\r\nSET&#160;QUOTED_IDENTIFIER&#160;ON \r\n\r\nGO \r\n\r\n\r\n\/*&#160;EXEC&#160;[usp_DB_restore_log]&#160;&#160;'\\\\ProdServer\\Drive$\\ProdServerDBbackups\\Full\\\r\nFULL_ServerName_DatabaseName1_20080217_030000.sqb', 'LogicalFileName', 'LogicalDataFile', 'LogicalLogFileName', \r\n'Drive:\\Program&#160;Files\\Microsoft&#160;SQL&#160;Server\\MSSQL.1\\MSSQL\\Data\\Database1.mdf', \r\n'Drive:\\Program&#160;Files\\Microsoft&#160;SQL&#160;Server\\MSSQL.1\\MSSQL\\Data\\Database1_log.ldf' \r\ndrop&#160;proc&#160;[usp_DB_restore_log] *\/\r\n\r\n\r\nCREATE&#160;&#160;PROC&#160;[dbo].[usp_DB_restore_log] \r\n\r\n--&#160;Input&#160;variables&#160;when&#160;it&#160;all&#160;works&#160;below.\r\n&#160;&#160;\r\n@backupfile&#160;SYSNAME, \r\n&#160;&#160;\r\n@dbName&#160;SYSNAME, \r\n&#160;&#160;\r\n@logicalDataFile&#160;SYSNAME, \r\n&#160;&#160;\r\n@logicalLogFile&#160;SYSNAME, \r\n&#160;&#160;\r\n@physicalDatafile&#160;SYSNAME, \r\n&#160;&#160;\r\n@physicalLogFile&#160;SYSNAME \r\n\r\n\r\n\/* System&#160;table&#160;backupfile&#160;on&#160;production server can give us LSN (log sequence number), logical_name, physical_drive and physical_name. If not, to grab the possible backup sets that are usable, see ms-help:\/\/MS.SQLCC.v9\/ MS.SQLSVR.v9.en\/tsqlref9\/html\/f1a7fc0a-f4b4-47eb-9138-eebf930dc9ac.htm. *\/\r\n&#160;&#160;&#160;&#160;&#160;&#160;\r\nAS \r\nDECLARE&#160;\r\n@exitcode&#160;INT \r\nDECLARE&#160;\r\n@sqlerrorcode&#160;INT \r\nDECLARE&#160;\r\n@restoreStmt&#160;NVARCHAR(MAX) \r\n&#160;&#160;\r\nSET&#160;NOCOUNT&#160;ON \r\n\r\n\r\n--&#160;We&#160;will&#160;not&#160;need&#160;to&#160;kill&#160;connections,&#160;since&#160;the&#160;database&#160;is&#160;in&#160;restoring&#160;state \r\n-- already,&#160;waiting&#160;for&#160;a&#160;log.\r\n\r\n-- EXEC&#160;usp_KillConnections&#160;@dbName\r\n\r\n--&#160;Transaction&#160;logs&#160;must&#160;be&#160;applied&#160;in&#160;sequential&#160;order. If&#160;there&#160;are&#160;multiple\r\n-- transaction logs&#160;to&#160;apply&#160;we&#160;have&#160;to&#160;leave the&#160;NORECOVERY&#160;option on. \r\n\r\n\r\n\/* In most cases, under the full or bulk- logged recovery models, SQL Server 2005 requires that you back up the tail of the log before restoring a database that is currently attached on the server instance. A tail-log backup captures the log that has not yet been backed up (the tail of the log) and is the last backup of interest in a recovery plan. Restoring a database without first backing up the tail of the log results is a mistake, unless the RESTORE statement contains either the WITH REPLACE or WITH STOPAT clause.\r\n\r\nA tail-log backup can be created independently of regular log backups by using the COPY_ONLY option. A copy-only backup does not affect the backup log chain. The transaction log is not truncated by the tail-log backup, and the log captured will be included in future normal log backups. This allows tail-log backups to be taken, for instance, to prepare for an online restore without affecting normal log backup procedures. For more information, see Copy-Only Backups (Full Recovery Model). *\/ \r\n\r\n\r\n\r\n-- Restore&#160;log&#160;info&#160;ms-help:\/\/MS.SQLCC.v9\/MS.SQLSVR.v9.en\/tsqlref9\/html\/\r\n877ecd57-3f2e-4237-890a-08f16e944ef1.htm.\r\n\r\n&#160;&#160;\r\nSET&#160;@restoreStmt&#160;=&#160;N'-SQL&#160;RESTORE&#160;Log&#160;'&#160;+&#160;@dbName&#160;+&#160;' \r\nFROM&#160;DISK&#160;=&#160;'&#160;\r\n+&#160;@backupfile&#160;+&#160;' \r\n&#160;WITH&#160;RECOVERY \r\n&#160; \r\n,MOVE&#160;'&#160;\r\n+&#160;@logicalDataFile&#160;+&#160;'&#160;TO&#160;'&#160;+&#160;@physicalDatafile&#160;+&#160;' \r\n,MOVE&#160;'&#160;\r\n+&#160;@logicalLogFile&#160;+&#160;'&#160;TO&#160;'&#160;+&#160;@physicalLogFile \r\n&#160;&#160;&#160;&#160;&#160;&#160;\r\n+&#160;' \r\n,REPLACE \r\n,LOGTO&#160;=&#160;\"\\\\TestServer\\d$\\TtestDB\\DBLog\\&lt;DATABASE&gt;_&lt;TYPE&gt;_ &lt;DATETIME&#160;yyyymmddhhmss&gt;.txt\"' \r\n\r\n--PRINT&#160;@restoreStmt \r\n\r\nEXEC&#160;master..sqlbackup&#160;@restoreStmt,&#160;@exitcode&#160;OUT,&#160;@sqlerrorcode&#160;OUT \r\n\r\nIF&#160;(&#160;@exitcode&#160;&gt;=&#160;500&#160;) \r\n&#160;&#160;&#160;&#160;&#160;&#160;OR&#160;(&#160;\r\n@sqlerrorcode&#160;&lt;&gt;&#160;0&#160;) \r\n&#160;&#160;\r\nBEGIN \r\n&#160;&#160;RAISERROR&#160;\r\n(&#160;'SQL&#160;Restore&#160;failed&#160;with&#160;exit&#160;code:&#160;%d&#160;SQL&#160;error&#160;code:&#160;%d', \r\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;\r\n16,&#160;1,&#160;@exitcode,&#160;@sqlerrorcode&#160;) \r\n&#160;&#160;\r\nEND \r\n\r\n\r\n<\/pre>\n<h4>usp_RoboCopy <\/h4>\n<pre class=\"theme:ssms2012 lang:tsql\">CREATE&#160;&#160;&#160;PROCEDURE&#160;[dbo].[usp_RoboCopy] \r\n&#160;&#160;\r\n( \r\n&#160;&#160;&#160;&#160;\r\n@srcUNC&#160;SYSNAME,&#160;&#160;&#160;&#160;--&#160;Source&#160;Server&#160;Name.\r\n&#160;&#160;&#160;&#160;\r\n@dstUNC&#160;SYSNAME,&#160;&#160;&#160;&#160;--&#160;Destination&#160;Server&#160;Name.\r\n&#160;&#160;&#160;&#160;\r\n@filelist&#160;VARCHAR(1024)&#160;&#160;--&#160;Space&#160;delimited&#160;list&#160;of&#160;files&#160;to&#160;be&#160;copied.\r\n&#160;&#160;\r\n) \r\n&#160;&#160;&#160;&#160;&#160;&#160;\r\nAS \/*****************************************************************\/\r\n--&#160;Stored&#160;Procedure :&#160;usp_RoboCopy \r\n--&#160;Creation&#160;Date :&#160;2009-02-26 \r\n--&#160;Written&#160;by :&#160;Stephen&#160;Mandeville,&#160;adapted&#160;by&#160;Hugo&#160;Shebbeare\r\n&#160;\/*************************************************************************\/ \r\nSET&#160;NOCOUNT&#160;ON \r\nDECLARE&#160;@ccmd&#160;VARCHAR(1500) \r\nDECLARE&#160;@logfile&#160;VARCHAR(25) \r\nDECLARE&#160;@retcode&#160;INT \r\n\/**************************************************************************\/\r\n--&#160;This&#160;stored&#160;procedure&#160;uses&#160;ROBOCOPY.exe,&#160;which&#160;is&#160;installed&#160;on&#160;server&#160;itself\r\n--&#160;in the sysroot\\windows\\system32&#160;folder&#160;(default&#160;on&#160;2008). \r\n-- The&#160;Source&#160;and&#160;Destination shares must&#160;exist.\r\n\/***************************************************************************\/\r\nSELECT&#160;&#160;@logfile&#160;=&#160;REPLACE(SUBSTRING((&#160;CONVERT(VARCHAR(15),&#160;GETDATE(),&#160;121)&#160;), \r\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;1,&#160;10),&#160;'-',&#160;'') \r\n&#160;&#160;&#160;&#160;&#160;&#160;+&#160;REPLACE(SUBSTRING((&#160;CONVERT(VARCHAR(30),&#160;GETDATE(),&#160;121)&#160;),&#160;12, \r\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;8),&#160;':',&#160;'') \r\nSELECT&#160;&#160;@ccmd&#160;=&#160;'ROBOCOPY&#160;'&#160;+&#160;@srcUNC&#160;+&#160;'&#160;'&#160;+&#160;@dstUNC&#160;+&#160;'&#160;'&#160;+&#160;@filelist \r\n&#160;&#160;&#160;&#160;&#160;&#160;+&#160;'&#160;\/NP&#160;\/LOG:'&#160;+&#160;@dstUNC&#160;+&#160;'\\transfer'&#160;+&#160;'_'&#160;+&#160;@logfile&#160;+&#160;'.txt' \r\n--PRINT&#160;@ccmd \r\nEXECUTE&#160;@retcode&#160;=&#160;master..xp_cmdshell&#160;@ccmd \r\n\r\n\/***************************************************************************\/\r\n--&#160;The&#160;return&#160;code&#160;(@retcode) from&#160;Robocopy&#160;(version&#160;1.74&#160;and&#160;later)&#160;is&#160;a&#160;\r\n--bit&#160;map,&#160;defined&#160;as&#160;follows:\r\n-- &#160; \r\n--&#160;Value&#160;&#160;&#160;&#160;MeaningIfSet \r\n--&#160;16&#160;&#160;&#160;&#160;&#160;&#160; Serious&#160;error.&#160;Robocopy&#160;did&#160;not&#160;copy&#160;any&#160;files.&#160;This&#160;is&#160;either a&#160;\r\n--&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; usage error or an error&#160;due&#160;to&#160;insufficient&#160;\r\n--&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; access&#160;privileges&#160;on&#160;the&#160;\r\n--&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; source&#160;or &#160;destination directories. \r\n--&#160;8 &#160;&#160;&#160;&#160;&#160;&#160;&#160;Some&#160;files&#160;or&#160;directories&#160;could&#160;not&#160;be&#160;copied&#160;\r\n--&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; (copy&#160;errors&#160;occurred&#160;and the&#160;retry&#160;limit&#160;was&#160;\r\n--&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; exceeded)Check&#160;these&#160;errors&#160;further.&#160;&#160;&#160;\r\n--&#160;4 &#160;&#160;&#160;&#160;&#160;&#160;&#160;Some&#160;Mismatched&#160;files&#160;or&#160;directories&#160;were&#160;detected.&#160;\r\n--&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; Examine&#160;the&#160;output&#160;log.&#160;\r\n-- &#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; Housekeeping&#160;is&#160;probably&#160;necessary. &#160;&#160;&#160; \r\n--&#160;2 &#160;&#160;&#160;&#160;&#160;&#160;&#160;Some&#160;Extra&#160;files&#160;or&#160;directories&#160;were&#160;detected.&#160;\r\n--&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; Examine&#160;the&#160;output&#160;log.&#160;\r\n-- &#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; &#160;Some&#160;housekeeping&#160;may&#160;be&#160;needed. &#160;&#160;&#160; \r\n--&#160;1 &#160;&#160;&#160;&#160;&#160;&#160;&#160;One&#160;or&#160;more&#160;files&#160;were&#160;copied&#160;successfully.that&#160;is,&#160;new&#160;files&#160; \r\n--&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; have arrived).\r\n--&#160;0&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; No&#160;errors&#160;occurred,&#160;and&#160;no&#160;copying&#160;was&#160;done.&#160;\r\n--&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; The&#160;source&#160;and&#160;destination\r\n--&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; directory&#160;trees&#160;are&#160;completely&#160;synchronized.\r\n\/**************************************************************************\/\r\n\r\n--&#160;Raising&#160;error&#160;only&#160;upon&#160;@retcode&#160;&gt;&#160;7.\r\nIF&#160;@retcode&#160;&gt;&#160;7 \r\n&#160;&#160;BEGIN \r\n&#160;&#160;RAISERROR&#160;(&#160;'Error&#160;occurred&#160;while&#160;executing&#160;Robocopy',&#160;16,&#160;1&#160;) \r\n&#160;&#160;RETURN&#160;(&#160;@retcode&#160;) \r\n&#160;&#160;END&#160;--IF&#160;@retcode&#160;&gt;&#160;7 \r\nELSE \r\n&#160;&#160;BEGIN \r\n&#160;&#160;RETURN&#160;(&#160;@retcode&#160;) \r\n&#160;&#160;END&#160;--ELSE \r\n\r\n<\/pre>\n<h4>usp_KillConnections<\/h4>\n<pre class=\"theme:ssms2012 lang:tsql\">IF&#160;NOT&#160;EXISTS&#160;( \r\n&#160;&#160;\r\nSELECT&#160;*&#160; \r\n&#160;&#160;&#160;&#160;\r\nFROM&#160;sys.objects&#160; \r\n&#160;&#160;&#160;&#160;\r\nWHERE&#160;OBJECT_ID&#160;=&#160;OBJECT_ID(N'[dbo].[usp_KillConnections]')&#160; \r\n&#160;&#160;&#160;&#160;&#160;&#160;AND&#160;\r\ntype&#160;IN&#160;(N'P',&#160;N'PC')) \r\n&#160;&#160;\r\nBEGIN \r\n&#160;&#160;EXEC&#160;\r\ndbo.sp_executesql&#160;@statement&#160;=&#160;N' \r\n\/*****************************************************************&#160; \r\n***&#160;Procedure:&#160;usp_KillConnections&#160; \r\n\r\n***&#160;Usage:&#160;usp_KillConnections&#160;@dbname&#160;=&#160;''Database&#160;Name''&#160; \r\n\r\n***&#160;Description:&#160;Drop&#160;all&#160;connections&#160;from&#160;a&#160;specific&#160;database&#160; \r\n\r\n***&#160;Input:&#160;@dbname&#160;-&#160;REQUIRED&#160;-&#160;Name&#160;of&#160;the&#160;database&#160; \r\n***&#160;Output:&#160;Outputs&#160;the&#160;results&#160;of&#160;the&#160;proccess&#160; \r\n\r\n***&#160;Revision:&#160;1.0&#160; \r\n***&#160;Revision&#160;History:&#160;1.0&#160;First&#160;Release&#160; \r\n***&#160;Author:&#160;Antonio&#160;Pedrosa&#160;Linares&#160; \r\n***&#160;Date:&#160;7\/25\/2007&#160; \r\n******************************************************************\/&#160; \r\n\r\n--&#160;exec&#160;usp_KillConnections&#160;''staplescpc'' \r\ncreate&#160;procedure&#160;[dbo].[usp_KillConnections]&#160; \r\n&#160;&#160;&#160;&#160;@dbname&#160;varchar(128)&#160; \r\nas&#160; \r\n&#160;&#160;&#160;&#160;declare&#160;@spid&#160;varchar(5)&#160; \r\n&#160;&#160;&#160;&#160;declare&#160;@loginname&#160;nvarchar(128)&#160; \r\n&#160;&#160;&#160;&#160;declare&#160;@intErrorCode&#160;int&#160; \r\n&#160;&#160;&#160;&#160;declare&#160;@intOk&#160;int&#160; \r\n&#160;&#160;&#160;&#160;declare&#160;@intError&#160;int&#160; \r\n&#160;&#160;&#160;&#160;declare&#160;@intTotal&#160;int&#160; \r\n\r\n&#160;&#160;&#160;&#160;set&#160;@intErrorCode&#160;=&#160;0&#160; \r\n&#160;&#160;&#160;&#160;set&#160;@intOk&#160;=&#160;0&#160; \r\n&#160;&#160;&#160;&#160;set&#160;@intError&#160;=&#160;0&#160; \r\n&#160;&#160;&#160;&#160;set&#160;@intTotal&#160;=&#160;0&#160; \r\n\r\n&#160;&#160;&#160;&#160;select&#160;@intTotal&#160;=&#160;count(sp.spid)&#160;FROM&#160;master..sysprocesses&#160;sp&#160; \r\n&#160;&#160;&#160;&#160;JOIN&#160;master..sysdatabases&#160;sd&#160;ON&#160;sp.dbid&#160;=&#160;sd.dbid&#160; \r\n&#160;&#160;&#160;&#160;WHERE&#160;sd.name&#160;=&#160;@dbname&#160; \r\n&#160;&#160;&#160;&#160;&#160; \r\n&#160;&#160;&#160;&#160;declare&#160;KILL_CONS&#160;cursor&#160;for&#160; \r\n&#160;&#160;&#160;&#160;SELECT&#160;cast(sp.spid&#160;as&#160;varchar(5)),rtrim(sp.loginame)&#160; \r\n&#160;&#160;&#160;&#160;FROM&#160;master..sysprocesses&#160;sp&#160; \r\n&#160;&#160;&#160;&#160;JOIN&#160;master..sysdatabases&#160;sd&#160;ON&#160;sp.dbid&#160;=&#160;sd.dbid&#160; \r\n&#160;&#160;&#160;&#160;WHERE&#160;sd.name&#160;=&#160;@dbname&#160; \r\n&#160;&#160;&#160;&#160;&#160; \r\n&#160;&#160;&#160;&#160;OPEN&#160;KILL_CONS&#160; \r\n\r\n&#160;&#160;&#160;&#160;FETCH&#160;NEXT&#160;FROM&#160;KILL_CONS&#160;INTO&#160;@spid,@loginname&#160; \r\n&#160;&#160;&#160;&#160;WHILE&#160;@@FETCH_STATUS&#160;=&#160;0&#160; \r\n&#160;&#160;&#160;&#160;BEGIN&#160; \r\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;EXEC(''Kill&#160;''+&#160;@spid&#160;+&#160;'''')&#160; \r\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;SELECT&#160;@intErrorCode&#160;=&#160;@@ERROR&#160; \r\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;if&#160;@intErrorCode&#160;=&#160;0&#160; \r\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;begin&#160; \r\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;set&#160;@intOk&#160;=&#160;@intOk&#160;+&#160;1&#160; \r\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;PRINT&#160;''Process&#160;''&#160;+&#160;@spid&#160;+&#160;''&#160;from&#160;login&#160;''&#160;\r\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; +&#160;@loginname&#160;+&#160;''&#160;has&#160;been&#160;ended.''&#160; \r\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;end&#160; \r\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;else&#160; \r\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;begin&#160; \r\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;set&#160;@intError&#160;=&#160;@intError&#160;+&#160;1&#160; \r\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;PRINT&#160;''Process&#160;''&#160;+&#160;@spid&#160;+&#160;''&#160;from&#160;login&#160;''&#160;\r\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; +&#160;@loginname&#160;+&#160;''&#160;could&#160;not be&#160;ended.''&#160; \r\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;end&#160; \r\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;FETCH&#160;NEXT&#160;FROM&#160;KILL_CONS&#160;INTO&#160;@spid,@loginname&#160; \r\n&#160;&#160;&#160;&#160;END&#160; \r\n&#160;&#160;&#160;&#160;CLOSE&#160;KILL_CONS&#160; \r\n&#160;&#160;&#160;&#160;DEALLOCATE&#160;KILL_CONS&#160; \r\n&#160;&#160;&#160;&#160;PRINT&#160;''Total&#160;number&#160;of&#160;processes&#160;from&#160;database&#160;''&#160;\r\n&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; +&#160;@dbname&#160;+&#160;'':&#160;''&#160;+&#160;cast (@intTotal&#160;as&#160;varchar)&#160; \r\n&#160;&#160;&#160;&#160;PRINT&#160;''Processes&#160;ended&#160;normally:&#160;''&#160;+&#160;cast(@intOk&#160;as&#160;varchar)&#160; \r\n&#160;&#160;&#160;&#160;PRINT&#160;''Processes&#160;could&#160;not&#160;be&#160;ended:&#160;''&#160;+&#160;cast(@intError&#160;as&#160;varchar)' \r\n&#160;&#160;\r\nEND  \r\n<\/pre>\n<h3>System Database Backups<\/h3>\n<p>On the DRP server itself the backups of the MSDB, DBAs databases, which are critical to this whole DRP process are located here:<\/p>\n<p><strong>\\\\DRPServerName:\\DRPbackupFolder\\Full&#160;<\/strong><\/p>\n<p class=\"buggy\">There should <strong>always<\/strong> be an alternative local backup location for system databases, such as on the Test server.<\/p>\n<p>All DBAs and system databases are backed up as well as on:<\/p>\n<p><strong>\\\\TstServerName:\\TestSrvBackupFolder\\Full<\/strong><\/p>\n<p>The following example was tested on a primary test server and exists on the restore server.&#160; The <em>usp_DB_restoreX<\/em> stored procedure takes 6 input parameters.&#160; To match up with backup log metadata, we shall match up the database name by date and then pull the relevant restore file input parameter into the appropriate <em>usp_DB_restoreX<\/em> stored procedure.&#160; The master restore procedures, divided into single file and multiple file restore procedures, use all the sub procedures to do the actual restore process. <\/p>\n<p><em>Please note that the usp_DB_RestoreX stored procedures are dependent on usp_KillConnections&#160; which will help in the restoration process by killing the existing database users (that is, unless it&#8217;s a system user however).<\/em><\/p>\n<p>e.g.<\/p>\n<pre class=\"theme:ssms2012 lang:tsql\">EXEC&#160;DBA_Tools.dbo.usp_DB_restore '\\\\TestServerName\\Drive$\\ProductionBackupFolder\\  Full\\FULL_ServerName_Database1_20080217_030000.sqb', 'DBlogicalName' 'DB_DataFile_Logicalname', 'DB_LogFileLogical_name',\r\n'DriveName:\\Program&#160;Files\\Microsoft&#160;SQL&#160;Server\\MSSQL.1\\MSSQL\\Data\\\r\nDBphysicalDataFileName.mdf',\r\n'DriveName:\\Program&#160;Files\\Microsoft&#160;SQL&#160;Server\\MSSQL.1\\MSSQL\\Data\\\r\nDBphysicalLogFileName_log.ldf'\r\n<\/pre>\n<p>The stored procedure <em>usp_DB_restore_norecovery<\/em> is the same as <em>usp_DB_restore<\/em>, only for Databases that need to be left in norecovery mode<span class=\"pullout\"> (as described earlier in the article)<\/span><\/p>\n<p>Please view the Activity History from Red Gate SQL Backup for reporting on what databases have been backed up, as the scope of this document covers the restoring process only. Although the backup information is extracted to prepare the automated restore scripts within the jobs, we are not going to create (at least at this stage) customised backup reporting information.&#160; However, do not forget that, since we are using these scripts within a SQL Server Agent job, we will have histories for each step and a log file written to the <strong>\\DBlog\\ <\/strong>folder local to the disaster recovery server running these SQL Agent Jobs.<\/p>\n<p class=\"illustration\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/imported\/791-SQB_activityhistory.gif\" width=\"640\" height=\"158\" alt=\"791-SQB_activityhistory.gif\" \/><\/p>\n<p class=\"caption\"><em>Figure 2 &#8211; SQL Backup Activity Log<\/em><\/p>\n<h3>Database Restore method when applying Differential Backups.<\/h3>\n<p>Please note that we use <em>usp_restore_db_norecovery <\/em>to load a production backup from the local copy moved over using <b>Robocopy<\/b>. Thus, if executed on the DBA database of the DRP server (<em>SERVER NAME \/ INSTANCE NAME<\/em>):<\/p>\n<pre class=\"theme:ssms2012 lang:tsql\">EXEC&#160;DBA_Tools.dbo.usp_DB_restore_norecovery&#160;  \\\\DRPserver\\InstanceName\\full\\FULL_ServerName_Database_20080217_030000.sqb', 'VSOT2', 'VSOT2_data', 'VSOT2_log,\r\n'D:\\Program&#160;Files\\Microsoft&#160;SQL&#160;Server\\MSSQL.1\\MSSQL\\Data\\VSOT2.mdf, 'D:\\Program&#160;Files\\Microsoft&#160;SQL&#160;Server\\MSSQL.1\\MSSQL\\Data\\VSOT2_log.ldf'\r\n<\/pre>\n<p>This will be the core of what runs for the second step of an automated job which leaves the database in NoRecovery mode, and thus should call the respective <em>RestoreDiff_dbx<\/em> next and, finally, apply the log files via <em>RestoreLog_dbx<\/em>.&#160;<\/p>\n<p>After the restore, make sure to run several tests that ensure the integrity of the data and that typical applications can run normal operations on the database.<\/p>\n<hr \/>\n<h2>Summary <\/h2>\n<p>Is this disaster recovery method really minimizing the manual intervention after failure? Can we make it better? Yes and yes, but there&#8217;s always room to improve. More importantly, this method certainly doesn&#8217;t suit every environment. Before you take what I&#8217;ve put together here and run with it, I strongly recommend you take a look at the High Availability Options table below to get a clear picture of what methodologies might be more appropriate for you individual needs. To make an effective choice, you&#8217;re naturally going to need a detailed understanding of each clients&#8217; needs for the restore process.<\/p>\n<h4>High Availability Options<\/h4>\n<table>\n<tbody>\n<tr>\n<td>\n<p><strong>Solution<\/strong> <\/p>\n<\/td>\n<td>\n<p><strong>Cost<\/strong> <\/p>\n<\/td>\n<td>\n<p><strong>Complexity<\/strong> <\/p>\n<\/td>\n<td>\n<p><strong>Failover<\/strong> <\/p>\n<\/td>\n<td>\n<p><strong>Failback<\/strong> <\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p>Hardware Clustering<\/p>\n<\/td>\n<td>\n<p>High<\/p>\n<\/td>\n<td>\n<p>High<\/p>\n<\/td>\n<td>\n<p>Fast<\/p>\n<\/td>\n<td>\n<p>Fast<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p>Software Clustering<\/p>\n<\/td>\n<td>\n<p>High<\/p>\n<\/td>\n<td>\n<p>High<\/p>\n<\/td>\n<td>\n<p>Fast<\/p>\n<\/td>\n<td>\n<p>Fast<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p>Replication<\/p>\n<\/td>\n<td>\n<p>Medium<\/p>\n<\/td>\n<td>\n<p>Medium<\/p>\n<\/td>\n<td>\n<p>Medium with manual<br \/>processing<\/p>\n<\/td>\n<td>\n<p>Slow with manual<br \/>processing<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p>Continuous Data<br \/>Protection<\/p>\n<\/td>\n<td>\n<p>Medium<\/p>\n<\/td>\n<td>\n<p>Medium<\/p>\n<\/td>\n<td>\n<p>Medium<\/p>\n<\/td>\n<td>\n<p>Slow<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p>Log Shipping<\/p>\n<\/td>\n<td>\n<p>Low<\/p>\n<\/td>\n<td>\n<p>Low<\/p>\n<\/td>\n<td>\n<p>Medium<\/p>\n<\/td>\n<td>\n<p>Slow<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p>Backup and<br \/>Restore<\/p>\n<\/td>\n<td>\n<p>Low<\/p>\n<\/td>\n<td>\n<p>Low<\/p>\n<\/td>\n<td>\n<p><strong>Slow<\/strong><\/p>\n<\/td>\n<td>\n<p><strong>Slow<\/strong><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p>Database Mirroring<\/p>\n<\/td>\n<td>\n<p>Low<\/p>\n<\/td>\n<td>\n<p>Low<\/p>\n<\/td>\n<td>\n<p>Fast, but only at the<br \/>database level<\/p>\n<\/td>\n<td>\n<p>Fast, but only at the<br \/>database level<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"caption\">At the time of writing, our backup-and-restore is super slow &#8211; at least<em><strong> 13 hours<\/strong><\/em> before we were live on the warm standby &#8211; but if optimization is run, we should be done in around 2 hours.<\/p>\n<p>Nobody wants to go through a disaster without being properly prepared. When I was asked to prepare a plan for Canada&#8217;s largest institutional fund manager, I took it rather seriously, hence the length of this document.&#160; We ran this through a real disaster recovery test over a weekend, and it all worked out just fine.&#160; I&#8217;ve tried to share with you exactly how you can get your own disaster recovery plan in place, so that when the time comes at least the recovery step itself is not a disaster.<\/p>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>High-Availability depends on how quickly you can recover a production system after an incident that has caused a failure. This requires planning, and documentation. If you get a Disaster Recovery Plan wrong, it can make an incident into a catastrophe for the business. Hugo Shebbeare discusses some essentials, describes a typical system, and provides sample documentation.&hellip;<\/p>\n","protected":false},"author":89116,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[143527],"tags":[4294,4248,4249,4178,4168,4932,4598,4150,4485,4151],"coauthors":[47075],"class_list":["post-658","post","type-post","status-publish","format-standard","hentry","category-database-administration-sql-server","tag-availability","tag-backup","tag-backup-and-recovery","tag-bi","tag-database","tag-disaster-recovery","tag-recovery","tag-sql","tag-sql-backup","tag-sql-server"],"acf":[],"_links":{"self":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/658","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/users\/89116"}],"replies":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/comments?post=658"}],"version-history":[{"count":5,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/658\/revisions"}],"predecessor-version":[{"id":84894,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/658\/revisions\/84894"}],"wp:attachment":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/media?parent=658"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/categories?post=658"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/tags?post=658"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/coauthors?post=658"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}