<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Taryn Pratt - Pivots and other SQL fun</title><link>https://tarynpivots.com/</link><description>Recent content on Taryn Pratt - Pivots and other SQL fun</description><generator>Hugo -- gohugo.io</generator><language>en</language><lastBuildDate>Mon, 19 Apr 2021 06:00:00 +0000</lastBuildDate><atom:link href="https://tarynpivots.com/index.xml" rel="self" type="application/rss+xml"/><item><title>Testing Backups and Offloading CheckDB</title><link>https://tarynpivots.com/post/2021/testing-backups-offload-dbcc-checkdb/</link><pubDate>Mon, 19 Apr 2021 06:00:00 +0000</pubDate><guid>https://tarynpivots.com/post/2021/testing-backups-offload-dbcc-checkdb/</guid><description>&lt;p>While every DBA knows they need to backup all their databases, not all may realize the importance of testing those backups. Performing backups is pointless, if you&amp;rsquo;re unable to restore them.&lt;/p>
&lt;p>I wanted to restore our backups on a regular basis, so I set up a process to test them automatically, on a nightly basis.&lt;/p>
&lt;h2 id="background">Background&lt;/h2>
&lt;p>Early on when I started working on the SQL Servers at Stack Overflow, we were taking daily backups. We had a handful of databases that were being restored for other processes, but the majority weren&amp;rsquo;t actively tested to ensure the backups were good. Since you never want to be in a situation where you need to restore a database and find it doesn&amp;rsquo;t work, my goal was to create a process to automatically restore our backups to a separate server, and then run &lt;code>DBCC CHECKDB&lt;/code> on it.&lt;/p>
&lt;p>Now, you might be wondering why I&amp;rsquo;d run &lt;code>CHECKDB&lt;/code> on the backup after I restore it and asking yourself &amp;ldquo;aren&amp;rsquo;t you supposed to run &lt;code>CHECKDB&lt;/code> on the SQL Server you take a backup on?&amp;rdquo;&lt;/p>
&lt;p>Yes, technically many people suggest you do that, but we don&amp;rsquo;t, because while we take full backups on the primary replica in our &lt;a href="https://docs.microsoft.com/en-us/sql/database-engine/availability-groups/windows/always-on-availability-groups-sql-server?view=sql-server-ver15">Always On Availability Groups&lt;/a>, we don&amp;rsquo;t have maintenance windows, and running &lt;code>CHECKDB&lt;/code> on a very busy production SQL Server like we have for Stack Overflow can cause issues (ask me how I know). We can&amp;rsquo;t easily run a full &lt;code>CHECKDB&lt;/code> in production, so since I was going to be restoring the backups to make sure they worked, I might as well run &lt;code>CHECKDB &lt;/code>on it to verify everything.&lt;/p>
&lt;h2 id="automating-the-testing">Automating the Testing&lt;/h2>
&lt;p>Before I jumped into creating a new process there were some questions I needed to answer for this project:&lt;/p>
&lt;ol>
&lt;li>What databases do we want or need to test the backups for?&lt;/li>
&lt;li>Where can we perform this testing? Do we have a server available for use?&lt;/li>
&lt;li>How often do we want to test the backups? Nightly? Weekly? What would the cadence be for this process?&lt;/li>
&lt;/ol>
&lt;p>The answer to the first question was pretty clear, since we can&amp;rsquo;t easily run &lt;code>DBCC CHECKDB&lt;/code> in production basically, any database that was in an Always On Availability Group was on the list to have the backup restored and tested.&lt;/p>
&lt;p>Next, I needed a place to perform the testing. Thankfully, we had a separate server where we were already restoring some backups, so it was the perfect candidate for this. The only issue was lack of disk space for some of the large databases. We resolved the lack of space issue by purchasing a couple of new NVMEs which gave us a 14TB drive to use for the restores.&lt;/p>
&lt;p>Lastly, it was figuring out how often we wanted to test our restores. Since we take full backups nightly, we decided we would restore all databases daily, then run &lt;code>DBCC CHECKDB&lt;/code> against it. Is that a little much? Maybe, but it also gives us confidence that our backups are working.&lt;/p>
&lt;p>Now that I had the answer to the questions above, it was time to create a process that would do the following:&lt;/p>
&lt;ol>
&lt;li>Automatically add or remove databases that need testing&lt;/li>
&lt;li>Restore the our backups on a separate server&lt;/li>
&lt;li>Run &lt;a href="https://docs.microsoft.com/en-us/sql/t-sql/database-console-commands/dbcc-checkdb-transact-sql?redirectedfrom=MSDN&amp;amp;view=sql-server-ver15">&lt;code>DBCC CHECKDB&lt;/code>&lt;/a> on the backup after being restored&lt;/li>
&lt;/ol>
&lt;h3 id="maintaining-a-list-of-databases-to-test">Maintaining a List of Databases to Test&lt;/h3>
&lt;p>I knew the initial list of databases we wanted to test, but what if that list changed? What would happen if we added new databases, or removed databases, and no longer had backups? I didn&amp;rsquo;t want the nightly process to fail, and even though I typically know when we add or remove databases, I didn&amp;rsquo;t want to hand-hold and edit the list manually, I wanted it to be dynamic.&lt;/p>
&lt;p>To do this, I had to save the list of databases to restore. I did this by first creating a table in the &lt;code>master&lt;/code> database on the server we were using for backup and restore testing. The table structure is:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;span class="lnt">8
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-sql" data-lang="sql">&lt;span class="k">CREATE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">TABLE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">dbo&lt;/span>&lt;span class="p">].&lt;/span>&lt;span class="n">DatabasesToRestore&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">Id&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nb">int&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">IDENTITY&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NOT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NULL&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">Name&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">sysname&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NOT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NULL&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">AvailabilityGroup&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">nvarchar&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">100&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NULL&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">IsActive&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">bit&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NOT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NULL&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">DEFAULT&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">CONSTRAINT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">PK_DatabasesToRestore&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">PRIMARY&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">KEY&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">CLUSTERED&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">([&lt;/span>&lt;span class="n">Id&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">ASC&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">ON&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="k">PRIMARY&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">GO&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;p>This table stores basic information about each database, including name, but also the name of the &lt;code>AvailabilityGroup&lt;/code> that the database is in. I purposely stored the AG name because of the way we save our backups. Our backup directory structure uses the Availability Group name instead of the name of the SQL Server in the file path because it allows the backup locations to stay consistent no matter what server is the primary. The file share for backups uses this directory structure:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">|--- server
|--- Backups
|--- SQL
|--- AG-name1
|--- Database1
--- database1_20210101.bak
--- database1_20210102.bak
|--- Database2
--- database2_20210101.bak
|--- AG-name2
|--- Database1
|--- Database2&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;p>By storing the name of the Availability Group for each database, it allowed me to dynamically create the path to the backup for testing. It also made the process to find additions and deletions for each availability group more efficient, so I didn&amp;rsquo;t have to manually adjust the list of databases that would need nightly testing. All that was needed was the initial population of the databases to restore, and then the stored procedure would keep the list up to date. The stored procedure that I wrote is:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;span class="lnt">36
&lt;/span>&lt;span class="lnt">37
&lt;/span>&lt;span class="lnt">38
&lt;/span>&lt;span class="lnt">39
&lt;/span>&lt;span class="lnt">40
&lt;/span>&lt;span class="lnt">41
&lt;/span>&lt;span class="lnt">42
&lt;/span>&lt;span class="lnt">43
&lt;/span>&lt;span class="lnt">44
&lt;/span>&lt;span class="lnt">45
&lt;/span>&lt;span class="lnt">46
&lt;/span>&lt;span class="lnt">47
&lt;/span>&lt;span class="lnt">48
&lt;/span>&lt;span class="lnt">49
&lt;/span>&lt;span class="lnt">50
&lt;/span>&lt;span class="lnt">51
&lt;/span>&lt;span class="lnt">52
&lt;/span>&lt;span class="lnt">53
&lt;/span>&lt;span class="lnt">54
&lt;/span>&lt;span class="lnt">55
&lt;/span>&lt;span class="lnt">56
&lt;/span>&lt;span class="lnt">57
&lt;/span>&lt;span class="lnt">58
&lt;/span>&lt;span class="lnt">59
&lt;/span>&lt;span class="lnt">60
&lt;/span>&lt;span class="lnt">61
&lt;/span>&lt;span class="lnt">62
&lt;/span>&lt;span class="lnt">63
&lt;/span>&lt;span class="lnt">64
&lt;/span>&lt;span class="lnt">65
&lt;/span>&lt;span class="lnt">66
&lt;/span>&lt;span class="lnt">67
&lt;/span>&lt;span class="lnt">68
&lt;/span>&lt;span class="lnt">69
&lt;/span>&lt;span class="lnt">70
&lt;/span>&lt;span class="lnt">71
&lt;/span>&lt;span class="lnt">72
&lt;/span>&lt;span class="lnt">73
&lt;/span>&lt;span class="lnt">74
&lt;/span>&lt;span class="lnt">75
&lt;/span>&lt;span class="lnt">76
&lt;/span>&lt;span class="lnt">77
&lt;/span>&lt;span class="lnt">78
&lt;/span>&lt;span class="lnt">79
&lt;/span>&lt;span class="lnt">80
&lt;/span>&lt;span class="lnt">81
&lt;/span>&lt;span class="lnt">82
&lt;/span>&lt;span class="lnt">83
&lt;/span>&lt;span class="lnt">84
&lt;/span>&lt;span class="lnt">85
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-sql" data-lang="sql">&lt;span class="k">CREATE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">OR&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">ALTER&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">Procedure&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">dbo&lt;/span>&lt;span class="p">].[&lt;/span>&lt;span class="n">spUpdateDatabasesToRestore&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">AS&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">BEGIN&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">DECLARE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">fileSearch&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">nvarchar&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">500&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">backupPath&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">NVARCHAR&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">500&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">error&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">nvarchar&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">600&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">DECLARE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">ag_name&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">nvarchar&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">100&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">DECLARE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ag_cursor&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">CURSOR&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">FOR&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">SELECT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">DISTINCT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">AvailabilityGroup&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">FROM&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">dbo&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">DatabasesToRestore&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">OPEN&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ag_cursor&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">FETCH&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NEXT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">FROM&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ag_cursor&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">INTO&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">ag_name&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">WHILE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@@&lt;/span>&lt;span class="n">FETCH_STATUS&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">BEGIN&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">PRINT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;Getting databases for AG: &amp;#39;&lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">ag_name&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">-- go through the list of availability groups and their backup path
&lt;/span>&lt;span class="c1">&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">-- update the list of databases to restore
&lt;/span>&lt;span class="c1">&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">-- adding new ones and deleting old ones that are no longer active databases
&lt;/span>&lt;span class="c1">&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">SET&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">backupPath&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">concat&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;\\fileserver\Backups\SQL\&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">ag_name&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;\&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">SET&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">fileSearch&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;DIR /S /b/a-d/od/t:c &amp;#39;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">backupPath&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">DECLARE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">files&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">table&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">ID&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">int&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">IDENTITY&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">FileName&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">varchar&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">max&lt;/span>&lt;span class="p">),&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">DBName&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">sysname&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">null&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">FileDate&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">date&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">null&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">INSERT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">INTO&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">files&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">FileName&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">EXEC&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">master&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">sys&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">xp_cmdshell&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">fileSearch&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">UPDATE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">files&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">SET&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">DBName&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">Left&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">replace&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">FileName&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">backupPath&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;&amp;#39;&lt;/span>&lt;span class="p">),&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">charindex&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;\&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">replace&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">FileName&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">backupPath&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;&amp;#39;&lt;/span>&lt;span class="p">))&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">),&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">FileDate&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">cast&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">substring&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">right&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">replace&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">FileName&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">backuppath&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;&amp;#39;&lt;/span>&lt;span class="p">),&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">len&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">replace&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">FileName&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">backuppath&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;&amp;#39;&lt;/span>&lt;span class="p">))&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">charindex&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;\&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">replace&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">FileName&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">backuppath&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;&amp;#39;&lt;/span>&lt;span class="p">))),&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">charindex&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;_FULL_&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">right&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">replace&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">FileName&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">backuppath&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;&amp;#39;&lt;/span>&lt;span class="p">),&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">len&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">replace&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">FileName&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">backuppath&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;&amp;#39;&lt;/span>&lt;span class="p">))&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">charindex&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;\&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">replace&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">FileName&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">backuppath&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;&amp;#39;&lt;/span>&lt;span class="p">))))&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">6&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">8&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">as&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">date&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">WHERE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">FileName&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">like&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;%.bak%&amp;#39;&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">PRINT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;inserting new databases for AG: &amp;#39;&lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">ag_name&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">-- Insert new databases into the Databases table
&lt;/span>&lt;span class="c1">&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">INSERT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">INTO&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">DatabasesToRestore&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Name&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">AvailabilityGroup&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">SELECT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">DBName&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">AGName&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">ag_name&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">FROM&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">SELECT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">DBName&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">rn&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">row_number&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">over&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">partition&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">by&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">DBName&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">order&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">by&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">FileDate&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">desc&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">FROM&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">files&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">WHERE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">FileDate&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">&amp;gt;=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">GETDATE&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">7&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">AND&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">FileName&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">LIKE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;%&amp;#39;&lt;/span>&lt;span class="o">+@&lt;/span>&lt;span class="n">ag_name&lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="s1">&amp;#39;%&amp;#39;&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">f&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">WHERE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">rn&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">AND&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NOT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">EXISTS&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">SELECT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">FROM&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">dbo&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">DatabasesToRestore&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">d&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">WHERE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">f&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">DBName&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">d&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">Name&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">AND&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">d&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">AvailabilityGroup&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">ag_name&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">AND&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">d&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">IsActive&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">PRINT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;Removing any old databases for AG: &amp;#39;&lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">ag_name&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">-- mark any Databases that are no longer producing backups as IsActive = 0
&lt;/span>&lt;span class="c1">&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">UPDATE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">d&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">SET&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">d&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">IsActive&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">FROM&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">dbo&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">DatabasesToRestore&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">d&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">LEFT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">JOIN&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">SELECT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">DISTINCT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">DBName&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">AGName&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">ag_name&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">FROM&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">files&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">WHERE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">FileDate&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">&amp;gt;=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">GETDATE&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">7&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">AND&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">DBName&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">IS&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NOT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NULL&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">AND&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">FileName&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">LIKE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;%&amp;#39;&lt;/span>&lt;span class="o">+@&lt;/span>&lt;span class="n">ag_name&lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="s1">&amp;#39;%&amp;#39;&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">f&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">ON&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">d&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">Name&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">f&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">DBName&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">AND&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">d&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">AvailabilityGroup&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">f&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">AGName&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">WHERE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">d&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">IsActive&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">AND&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">d&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">AvailabilityGroup&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">ag_name&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">AND&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">f&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">DBName&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">IS&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NULL&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">FETCH&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NEXT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">FROM&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ag_cursor&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">INTO&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">ag_name&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">END&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">CLOSE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ag_cursor&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">DEALLOCATE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ag_cursor&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">END&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;p>This procedure uses &lt;a href="https://docs.microsoft.com/en-us/sql/relational-databases/system-stored-procedures/xp-cmdshell-transact-sql?view=sql-server-ver15">xp_cmdshell&lt;/a> to get the list of files in the backup directory, then adds new databases that are missing from the current list, and removes databases that haven&amp;rsquo;t had a new .bak file in the last seven days. Finally, we have a scheduled job that executes this stored procedure nightly to keep the table of &lt;code>DatabasesToRestore&lt;/code> up to date.&lt;/p>
&lt;h3 id="restoring-database-backups">Restoring Database Backups&lt;/h3>
&lt;p>Once I had a list of the databases to restore, and a way to maintain that list on its own, it was time to get the process in place to restore the backups for testing nightly.&lt;/p>
&lt;p>In addition to restoring the backups, I also wanted to keep a record of what was being restored, as a reference, in the event there were issues or failures with the nightly job. To do this, I created another table which I put in the &lt;code>master&lt;/code> database.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-sql" data-lang="sql">&lt;span class="k">CREATE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">TABLE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">dbo&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">DatabaseRestoreLog&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">ID&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nb">int&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">IDENTITY&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NOT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NULL&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">DatabaseId&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nb">int&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NOT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NULL&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">RestoredName&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">sysname&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NULL&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">RestoredDate&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nb">date&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NOT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NULL&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">RestoreStartTime&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">datetime&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NULL&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">RestoreEndTime&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">datetime&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NULL&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">DBCCStartTime&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">datetime&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NULL&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">DBCCEndTime&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">datetime&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NULL&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">PassedDBCCChecks&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nb">bit&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NOT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NULL&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">DEFAULT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">CONSTRAINT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">PK_DatabaseRestoreLog&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">PRIMARY&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">KEY&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">CLUSTERED&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">([&lt;/span>&lt;span class="n">ID&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">ASC&lt;/span>&lt;span class="p">),&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">CONSTRAINT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">FK_DatabaseRestoreLog_DatabasesToRestore&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">FOREIGN&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">KEY&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">([&lt;/span>&lt;span class="n">DatabaseId&lt;/span>&lt;span class="p">])&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">REFERENCES&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">dbo&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">DatabasesToRestore&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">([&lt;/span>&lt;span class="n">Id&lt;/span>&lt;span class="p">])&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="p">);&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;p>This table allows the job to capture critical details, like how long the restore took, it also tracked whether or not the backup passed the DBCC checks (more on that later).&lt;/p>
&lt;p>As I mentioned earlier, we were restoring some databases in another process, so instead of inventing something from scratch, I decided to modify the existing stored procedure and expand it for the restore testing. Again, this procedure utilizes &lt;code>xp_cmdshell&lt;/code> and loops through all of the databases in the &lt;code>DatabasesToRestore&lt;/code> table and looks for the oldest databases that haven&amp;rsquo;t been restored recently, creates the path to the backup, and then grabs the most recent .bak file to restore for testing.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt"> 10
&lt;/span>&lt;span class="lnt"> 11
&lt;/span>&lt;span class="lnt"> 12
&lt;/span>&lt;span class="lnt"> 13
&lt;/span>&lt;span class="lnt"> 14
&lt;/span>&lt;span class="lnt"> 15
&lt;/span>&lt;span class="lnt"> 16
&lt;/span>&lt;span class="lnt"> 17
&lt;/span>&lt;span class="lnt"> 18
&lt;/span>&lt;span class="lnt"> 19
&lt;/span>&lt;span class="lnt"> 20
&lt;/span>&lt;span class="lnt"> 21
&lt;/span>&lt;span class="lnt"> 22
&lt;/span>&lt;span class="lnt"> 23
&lt;/span>&lt;span class="lnt"> 24
&lt;/span>&lt;span class="lnt"> 25
&lt;/span>&lt;span class="lnt"> 26
&lt;/span>&lt;span class="lnt"> 27
&lt;/span>&lt;span class="lnt"> 28
&lt;/span>&lt;span class="lnt"> 29
&lt;/span>&lt;span class="lnt"> 30
&lt;/span>&lt;span class="lnt"> 31
&lt;/span>&lt;span class="lnt"> 32
&lt;/span>&lt;span class="lnt"> 33
&lt;/span>&lt;span class="lnt"> 34
&lt;/span>&lt;span class="lnt"> 35
&lt;/span>&lt;span class="lnt"> 36
&lt;/span>&lt;span class="lnt"> 37
&lt;/span>&lt;span class="lnt"> 38
&lt;/span>&lt;span class="lnt"> 39
&lt;/span>&lt;span class="lnt"> 40
&lt;/span>&lt;span class="lnt"> 41
&lt;/span>&lt;span class="lnt"> 42
&lt;/span>&lt;span class="lnt"> 43
&lt;/span>&lt;span class="lnt"> 44
&lt;/span>&lt;span class="lnt"> 45
&lt;/span>&lt;span class="lnt"> 46
&lt;/span>&lt;span class="lnt"> 47
&lt;/span>&lt;span class="lnt"> 48
&lt;/span>&lt;span class="lnt"> 49
&lt;/span>&lt;span class="lnt"> 50
&lt;/span>&lt;span class="lnt"> 51
&lt;/span>&lt;span class="lnt"> 52
&lt;/span>&lt;span class="lnt"> 53
&lt;/span>&lt;span class="lnt"> 54
&lt;/span>&lt;span class="lnt"> 55
&lt;/span>&lt;span class="lnt"> 56
&lt;/span>&lt;span class="lnt"> 57
&lt;/span>&lt;span class="lnt"> 58
&lt;/span>&lt;span class="lnt"> 59
&lt;/span>&lt;span class="lnt"> 60
&lt;/span>&lt;span class="lnt"> 61
&lt;/span>&lt;span class="lnt"> 62
&lt;/span>&lt;span class="lnt"> 63
&lt;/span>&lt;span class="lnt"> 64
&lt;/span>&lt;span class="lnt"> 65
&lt;/span>&lt;span class="lnt"> 66
&lt;/span>&lt;span class="lnt"> 67
&lt;/span>&lt;span class="lnt"> 68
&lt;/span>&lt;span class="lnt"> 69
&lt;/span>&lt;span class="lnt"> 70
&lt;/span>&lt;span class="lnt"> 71
&lt;/span>&lt;span class="lnt"> 72
&lt;/span>&lt;span class="lnt"> 73
&lt;/span>&lt;span class="lnt"> 74
&lt;/span>&lt;span class="lnt"> 75
&lt;/span>&lt;span class="lnt"> 76
&lt;/span>&lt;span class="lnt"> 77
&lt;/span>&lt;span class="lnt"> 78
&lt;/span>&lt;span class="lnt"> 79
&lt;/span>&lt;span class="lnt"> 80
&lt;/span>&lt;span class="lnt"> 81
&lt;/span>&lt;span class="lnt"> 82
&lt;/span>&lt;span class="lnt"> 83
&lt;/span>&lt;span class="lnt"> 84
&lt;/span>&lt;span class="lnt"> 85
&lt;/span>&lt;span class="lnt"> 86
&lt;/span>&lt;span class="lnt"> 87
&lt;/span>&lt;span class="lnt"> 88
&lt;/span>&lt;span class="lnt"> 89
&lt;/span>&lt;span class="lnt"> 90
&lt;/span>&lt;span class="lnt"> 91
&lt;/span>&lt;span class="lnt"> 92
&lt;/span>&lt;span class="lnt"> 93
&lt;/span>&lt;span class="lnt"> 94
&lt;/span>&lt;span class="lnt"> 95
&lt;/span>&lt;span class="lnt"> 96
&lt;/span>&lt;span class="lnt"> 97
&lt;/span>&lt;span class="lnt"> 98
&lt;/span>&lt;span class="lnt"> 99
&lt;/span>&lt;span class="lnt">100
&lt;/span>&lt;span class="lnt">101
&lt;/span>&lt;span class="lnt">102
&lt;/span>&lt;span class="lnt">103
&lt;/span>&lt;span class="lnt">104
&lt;/span>&lt;span class="lnt">105
&lt;/span>&lt;span class="lnt">106
&lt;/span>&lt;span class="lnt">107
&lt;/span>&lt;span class="lnt">108
&lt;/span>&lt;span class="lnt">109
&lt;/span>&lt;span class="lnt">110
&lt;/span>&lt;span class="lnt">111
&lt;/span>&lt;span class="lnt">112
&lt;/span>&lt;span class="lnt">113
&lt;/span>&lt;span class="lnt">114
&lt;/span>&lt;span class="lnt">115
&lt;/span>&lt;span class="lnt">116
&lt;/span>&lt;span class="lnt">117
&lt;/span>&lt;span class="lnt">118
&lt;/span>&lt;span class="lnt">119
&lt;/span>&lt;span class="lnt">120
&lt;/span>&lt;span class="lnt">121
&lt;/span>&lt;span class="lnt">122
&lt;/span>&lt;span class="lnt">123
&lt;/span>&lt;span class="lnt">124
&lt;/span>&lt;span class="lnt">125
&lt;/span>&lt;span class="lnt">126
&lt;/span>&lt;span class="lnt">127
&lt;/span>&lt;span class="lnt">128
&lt;/span>&lt;span class="lnt">129
&lt;/span>&lt;span class="lnt">130
&lt;/span>&lt;span class="lnt">131
&lt;/span>&lt;span class="lnt">132
&lt;/span>&lt;span class="lnt">133
&lt;/span>&lt;span class="lnt">134
&lt;/span>&lt;span class="lnt">135
&lt;/span>&lt;span class="lnt">136
&lt;/span>&lt;span class="lnt">137
&lt;/span>&lt;span class="lnt">138
&lt;/span>&lt;span class="lnt">139
&lt;/span>&lt;span class="lnt">140
&lt;/span>&lt;span class="lnt">141
&lt;/span>&lt;span class="lnt">142
&lt;/span>&lt;span class="lnt">143
&lt;/span>&lt;span class="lnt">144
&lt;/span>&lt;span class="lnt">145
&lt;/span>&lt;span class="lnt">146
&lt;/span>&lt;span class="lnt">147
&lt;/span>&lt;span class="lnt">148
&lt;/span>&lt;span class="lnt">149
&lt;/span>&lt;span class="lnt">150
&lt;/span>&lt;span class="lnt">151
&lt;/span>&lt;span class="lnt">152
&lt;/span>&lt;span class="lnt">153
&lt;/span>&lt;span class="lnt">154
&lt;/span>&lt;span class="lnt">155
&lt;/span>&lt;span class="lnt">156
&lt;/span>&lt;span class="lnt">157
&lt;/span>&lt;span class="lnt">158
&lt;/span>&lt;span class="lnt">159
&lt;/span>&lt;span class="lnt">160
&lt;/span>&lt;span class="lnt">161
&lt;/span>&lt;span class="lnt">162
&lt;/span>&lt;span class="lnt">163
&lt;/span>&lt;span class="lnt">164
&lt;/span>&lt;span class="lnt">165
&lt;/span>&lt;span class="lnt">166
&lt;/span>&lt;span class="lnt">167
&lt;/span>&lt;span class="lnt">168
&lt;/span>&lt;span class="lnt">169
&lt;/span>&lt;span class="lnt">170
&lt;/span>&lt;span class="lnt">171
&lt;/span>&lt;span class="lnt">172
&lt;/span>&lt;span class="lnt">173
&lt;/span>&lt;span class="lnt">174
&lt;/span>&lt;span class="lnt">175
&lt;/span>&lt;span class="lnt">176
&lt;/span>&lt;span class="lnt">177
&lt;/span>&lt;span class="lnt">178
&lt;/span>&lt;span class="lnt">179
&lt;/span>&lt;span class="lnt">180
&lt;/span>&lt;span class="lnt">181
&lt;/span>&lt;span class="lnt">182
&lt;/span>&lt;span class="lnt">183
&lt;/span>&lt;span class="lnt">184
&lt;/span>&lt;span class="lnt">185
&lt;/span>&lt;span class="lnt">186
&lt;/span>&lt;span class="lnt">187
&lt;/span>&lt;span class="lnt">188
&lt;/span>&lt;span class="lnt">189
&lt;/span>&lt;span class="lnt">190
&lt;/span>&lt;span class="lnt">191
&lt;/span>&lt;span class="lnt">192
&lt;/span>&lt;span class="lnt">193
&lt;/span>&lt;span class="lnt">194
&lt;/span>&lt;span class="lnt">195
&lt;/span>&lt;span class="lnt">196
&lt;/span>&lt;span class="lnt">197
&lt;/span>&lt;span class="lnt">198
&lt;/span>&lt;span class="lnt">199
&lt;/span>&lt;span class="lnt">200
&lt;/span>&lt;span class="lnt">201
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-sql" data-lang="sql">&lt;span class="k">CREATE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">OR&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">ALTER&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">Procedure&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">dbo&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">spBackupRestoreTesting&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">TimeLimit&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">int&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">43200&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">-- the amount of time we want to limit the restores - the default is 12 hours
&lt;/span>&lt;span class="c1">&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="k">exec&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">BIT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">AS&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">BEGIN&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">DECLARE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">fileSearch&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">nvarchar&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">500&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">backupFile&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">nvarchar&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">500&lt;/span>&lt;span class="p">),&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">data_dir&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">nvarchar&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">100&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;F:\DBRestore\&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">cmd&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">nvarchar&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">max&lt;/span>&lt;span class="p">),&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">dropCmd&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">nvarchar&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">500&lt;/span>&lt;span class="p">),&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">RestoredDBName&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">sysname&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">backupPath&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">NVARCHAR&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">500&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">error&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">nvarchar&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">600&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">-- Set the time we started the restore process so we know when we want to exit
&lt;/span>&lt;span class="c1">&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">-- if it hits the TimeLimit, we&amp;#39;ll stop for the day
&lt;/span>&lt;span class="c1">&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">DECLARE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">StartTime&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">datetime&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">SET&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">StartTime&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">GETDATE&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">DECLARE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">fileList&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">Table&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">rowNumber&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">int&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">identity&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">),&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">backupFile&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">nvarchar&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">255&lt;/span>&lt;span class="p">),&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">backupPath&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">nvarchar&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">500&lt;/span>&lt;span class="p">));&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">DECLARE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">FileListTable&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">Table&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">LogicalName&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">nvarchar&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">128&lt;/span>&lt;span class="p">),&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">PhysicalName&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">nvarchar&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">260&lt;/span>&lt;span class="p">),&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="k">Type&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">char&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">),&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">FileGroupName&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">nvarchar&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">128&lt;/span>&lt;span class="p">),&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="k">Size&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">numeric&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">20&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">),&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">MaxSize&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">numeric&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">20&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">),&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">FileId&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">bigint&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">CreateLSN&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">numeric&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">25&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">),&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">DropLSN&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">numeric&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">25&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">),&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">UniqueId&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">uniqueidentifier&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">ReadOnlyLSN&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">numeric&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">25&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">),&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">ReadWriteLSN&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">numeric&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">25&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">),&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">BackupSizeInBytes&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">bigint&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">SourceBlockSize&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">int&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">FileGroupId&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">int&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">LogGroupGUID&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">uniqueidentifier&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">DifferentialBaseLSN&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">numeric&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">25&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">),&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">DifferentialBaseGUID&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">uniqueidentifier&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">IsReadOnly&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">bit&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">IsPresent&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">bit&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">TDEThumbprint&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">varbinary&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">32&lt;/span>&lt;span class="p">),&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">SnapshotUrl&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">nvarchar&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">360&lt;/span>&lt;span class="p">));&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">DECLARE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">database_id&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">int&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">DECLARE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">database_name&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">sysname&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">DECLARE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">ag_name&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">nvarchar&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">100&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">DECLARE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">last_restore_date&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">date&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">-- loop through the list of databases
&lt;/span>&lt;span class="c1">&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">-- restore them, then run DBCC checks on them
&lt;/span>&lt;span class="c1">&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">WHILE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">GETDATE&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">DATEADD&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">ss&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">TimeLimit&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">StartTime&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">BEGIN&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">-- grab the oldest databases that haven&amp;#39;t been restored recently
&lt;/span>&lt;span class="c1">&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">SELECT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">TOP&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">database_id&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">d&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">Id&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">database_name&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">d&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">Name&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">ag_name&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">d&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">AvailabilityGroup&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">last_restore_date&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">drl&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">LastRestoredDate&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">FROM&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">DatabasesToRestore&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">d&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">LEFT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">JOIN&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">-- get the last date each database was restored
&lt;/span>&lt;span class="c1">&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">SELECT&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">DatabaseId&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">LastRestoredDate&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">MAX&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">RestoredDate&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">FROM&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">DatabaseRestoreLog&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">GROUP&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">BY&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">DatabaseId&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">drl&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">ON&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">d&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">Id&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">drl&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">DatabaseId&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">WHERE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">d&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">IsActive&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">AND&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">drl&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">LastRestoredDate&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">&amp;lt;&amp;gt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">CAST&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">GETDATE&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">AS&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">DATE&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">OR&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">drl&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">LastRestoredDate&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">IS&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NULL&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">ORDER&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">BY&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">drl&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">LastRestoredDate&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">IF&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@@&lt;/span>&lt;span class="n">ROWCOUNT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">BEGIN&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">BREAK&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">END&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">PRINT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">CONCAT&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;Restoring : &amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">database_name&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39; in AG &amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">ag_name&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39; last restored date: &amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">last_restore_date&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">SET&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">backupPath&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">CONCAT&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;\\fileserver\Backups\SQL\&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">ag_name&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;\&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">database_name&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;\&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">PRINT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">backupPath&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">SET&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">fileSearch&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;DIR *.bak /b /O:D &amp;#39;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">backupPath&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">INSERT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">INTO&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">fileList&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">backupFile&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">EXEC&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">master&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">sys&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">xp_cmdshell&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">fileSearch&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">UPDATE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">fileList&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">Set&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">backupPath&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">backupPath&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">SELECT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Top&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">backupFile&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">backupFile&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">backupPath&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">backupPath&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">FROM&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">fileList&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">WHERE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">backupFile&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">Like&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">database_name&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;%&amp;#39;&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">ORDER&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">BY&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">backupFile&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">DESC&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">IF&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">backupFile&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">Is&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">Not&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">Null&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">BEGIN&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">DECLARE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">fullPath&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">nvarchar&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">500&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">backupPath&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">backupFile&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">PRINT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;Backup File Found: &amp;#39;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">fullPath&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">INSERT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">INTO&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">FileListTable&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">EXEC&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;Restore FileListOnly From Disk = &amp;#39;&amp;#39;&amp;#39;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">fullPath&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;&amp;#39;&amp;#39;&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">SET&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">RestoredDBName&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">REPLACE&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">backupFile&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;.bak&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">-- insert into the log the Database we&amp;#39;re restoring
&lt;/span>&lt;span class="c1">&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">INSERT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">INTO&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">dbo&lt;/span>&lt;span class="p">.[&lt;/span>&lt;span class="n">DatabaseRestoreLog&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">DatabaseId&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">RestoredName&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">RestoredDate&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">RestoreStartTime&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">VALUES&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">database_id&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">RestoredDBName&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">GETDATE&lt;/span>&lt;span class="p">(),&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">GETDATE&lt;/span>&lt;span class="p">());&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">SET&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">cmd&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;Restore Database [&amp;#39;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">RestoredDBName&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;] &amp;#39;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">char&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">13&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39; From Disk = &amp;#39;&amp;#39;&amp;#39;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">fullPath&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;&amp;#39;&amp;#39; With File = 1, &amp;#39;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">char&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">13&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">SELECT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">cmd&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">cmd&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39; Move N&amp;#39;&amp;#39;&amp;#39;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">LogicalName&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;&amp;#39;&amp;#39; To N&amp;#39;&amp;#39;&amp;#39;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">data_dir&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">LogicalName&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;_&amp;#39;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">RestoredDBName&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;.&amp;#39;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">REVERSE&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">SUBSTRING&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">REVERSE&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">PhysicalName&lt;/span>&lt;span class="p">),&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">CHARINDEX&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;.&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">REVERSE&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">PhysicalName&lt;/span>&lt;span class="p">),&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">)))&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;&amp;#39;&amp;#39;, &amp;#39;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">char&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">13&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">FROM&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">FileListTable&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">SET&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">cmd&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">cmd&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39; NoUnload, Stats = 5;&amp;#39;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">char&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">13&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">SET&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">cmd&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">cmd&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;Alter Database [&amp;#39;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">RestoredDBName&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;] Set Recovery Simple With No_Wait;&amp;#39;&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">-- if for some reason the database with the restored name exists, drop it first
&lt;/span>&lt;span class="c1">&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">IF&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">EXISTS&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">SELECT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">FROM&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">sys&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">databases&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">WHERE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Name&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">RestoredDBName&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">BEGIN&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">PRINT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;Dropping existing [&amp;#39;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">RestoredDBName&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="s1">&amp;#39;]&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">SET&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">dropCmd&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;Alter Database [&amp;#39;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">RestoredDBName&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;] Set Single_User With Rollback Immediate; Drop Database [&amp;#39;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">RestoredDBName&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;];&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">PRINT&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">dropCmd&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">IF&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="k">exec&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">EXEC&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">dropCmd&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">END&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">-- if it doesn&amp;#39;t exist, then restore the database
&lt;/span>&lt;span class="c1">&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">PRINT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;Restoring [&amp;#39;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">database_name&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;] from &amp;#39;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">backupFile&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">PRINT&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">cmd&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">IF&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="k">exec&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">EXEC&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">cmd&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">-- make sure the DB exists before trying to run DBCC CHECKDB on it
&lt;/span>&lt;span class="c1">&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">IF&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">EXISTS&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">SELECT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">FROM&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">sys&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">databases&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">WHERE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Name&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">RestoredDBName&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">BEGIN&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">-- When the restore is complete, update the Log to reflect the end time
&lt;/span>&lt;span class="c1">&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">UPDATE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">dbo&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">DatabaseRestoreLog&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">SET&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">RestoreEndTime&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">GETDATE&lt;/span>&lt;span class="p">(),&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">DBCCStartTime&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">GETDATE&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">WHERE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">DatabaseId&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">database_id&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">AND&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">RestoredName&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">RestoredDBName&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">EXEC&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">dbo&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">spRunDBCCChecks&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">dbName&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">RestoredDBName&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="k">exec&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="k">exec&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">-- When the DBCC Check is complete, update the Log to reflect the end time
&lt;/span>&lt;span class="c1">&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">UPDATE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">dbo&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">DatabaseRestoreLog&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">SET&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">DBCCEndTime&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">GETDATE&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">WHERE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">DatabaseId&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">database_id&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">AND&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">RestoredName&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">RestoredDBName&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">END&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">ELSE&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">-- if the restore failed, then raise an error that it failed and send an emails
&lt;/span>&lt;span class="c1">&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">BEGIN&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">SET&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">error&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;Restore of database &amp;#39;&amp;#39;&amp;#39;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">RestoredDBName&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;&amp;#39;&amp;#39; failed. Check the log on the server.&amp;#39;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">-- send an email alert to that the DBCC CHECKDB failed for the restored item
&lt;/span>&lt;span class="c1">&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">EXEC&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">msdb&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">dbo&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">sp_send_dbmail&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">profile_name&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;Mail&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">recipients&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;useyourown@email.com&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">subject&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">error&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">body&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">error&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">RAISERROR&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">error&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">18&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">END&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">END&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">ELSE&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">BEGIN&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">SET&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">error&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;No Recent backup was found in &amp;#39;&amp;#39;&amp;#39;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">backupPath&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;&amp;#39;&amp;#39;&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">RAISERROR&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">error&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">18&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">END&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">-- delete the info on the one we just restored
&lt;/span>&lt;span class="c1">&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">DELETE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">FROM&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">fileList&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">DELETE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">FROM&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">FileListTable&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">END&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">-- once we&amp;#39;re done, let&amp;#39;s clear out the DBCC_History_Log table a bit, cause it grows fast
&lt;/span>&lt;span class="c1">&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">DELETE&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">FROM&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">dbo&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">DBCC_History_Log&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">WHERE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">DBCCCheckDate&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">GETDATE&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">30&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">-- send an email to sql-alerts with a count of what was done and what passed
&lt;/span>&lt;span class="c1">&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">DECLARE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">TotalDatabasesRestored&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">int&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">DECLARE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">TotalPassedCheck&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">int&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">SELECT&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">TotalDatabasesRestored&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">count&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="p">),&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">TotalPassedCheck&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">count&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">case&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">when&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">PassedDBCCChecks&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">then&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">DatabaseId&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">end&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">FROM&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">dbo&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">DatabaseRestoreLog&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">WHERE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">RestoredDate&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">CAST&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">GETDATE&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">as&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">date&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">DECLARE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">subject&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">nvarchar&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">100&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">CONCAT&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;Database restore results for &amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">cast&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">getdate&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">as&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">date&lt;/span>&lt;span class="p">))&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">DECLARE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">body&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">nvarchar&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">500&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">CONCAT&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;Total Databases Restored: &amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">TotalDatabasesRestored&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;Total Databases Passing DBCC Check:&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">TotalPassedCheck&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">EXEC&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">msdb&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">dbo&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">sp_send_dbmail&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">profile_name&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;Mail&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">recipients&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;useyourown@email.com&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">subject&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">subject&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">body&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">body&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">END&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;p>Once the database is restored we perform a &lt;code>DBCC CHECKDB&lt;/code> on it to see if there are any issues.&lt;/p>
&lt;p>Since we restore hundreds of databases daily, I set a limit on the amount of time we perform restore testing each day. We currently limit testing to 12 hours a day, but if need be, we could easily adjust this to 18 hours or some other value. I attempted to make this process flexible to account for adding or removing databases.&lt;/p>
&lt;h3 id="running-dbcc-checkdb">Running DBCC CheckDB&lt;/h3>
&lt;p>Outside of restoring our backups, the last piece is the most important&amp;hellip;running a &lt;code>CHECKDB&lt;/code> against the database since we do not do so in production.&lt;/p>
&lt;p>If you look closely at the previous stored procedure, you won&amp;rsquo;t see a direct call to &lt;code>DBCC CHECKDB&lt;/code>. That&amp;rsquo;s because I created a separate stored procedure to make that call. But before we get to that stored procedure, there is one more table that I created for this process. Unless you are actively watching the &lt;code>CHECKDB&lt;/code> run, you&amp;rsquo;re going to want to capture the results so you can see any issues that were found. In order to do this, I created one more table to capture the lengthy results:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-sql" data-lang="sql">&lt;span class="k">CREATE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">TABLE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">dbo&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">DBCC_History_Log&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">ID&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nb">int&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">IDENTITY&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NOT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NULL&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">CONSTRAINT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">PK_DBCC_History_Log&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">PRIMARY&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">KEY&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">DatabaseName&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">sysname&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NULL&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">DBCCCheckDate&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nb">date&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NOT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NULL&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">Error&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nb">int&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NULL&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="k">Level&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nb">int&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NULL&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="k">State&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nb">int&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NULL&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">MessageText&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nb">varchar&lt;/span>&lt;span class="p">](&lt;/span>&lt;span class="mi">7000&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NULL&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">RepairLevel&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">nvarchar&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">500&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NULL&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">Status&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nb">int&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NULL&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">DbId&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nb">int&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NULL&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">DbFragId&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nb">int&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NULL&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">ObjectId&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nb">int&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NULL&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">IndexId&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nb">int&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NULL&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">PartitionID&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nb">bigint&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NULL&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">AllocUnitID&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nb">bigint&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NULL&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">RidDbId&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nb">int&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NULL&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">RidPruId&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nb">int&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NULL&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">File&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nb">int&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NULL&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">Page&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nb">int&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NULL&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">Slot&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nb">int&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NULL&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">RefDbId&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nb">int&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NULL&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">RefPruId&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nb">int&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NULL&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">RefFile&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nb">int&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NULL&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">RefPage&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nb">int&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NULL&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">RefSlot&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nb">int&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NULL&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">Allocation&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nb">int&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NULL&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="k">TimeStamp&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">datetime&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NULL&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">CONSTRAINT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">DF_dbcc_history_TimeStamp&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">DEFAULT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">GETDATE&lt;/span>&lt;span class="p">())&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="p">);&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;p>You might be wondering why I&amp;rsquo;d want to save this info? Since this is a nightly process, I just want it to run in the background and only notify me if something is wrong in the &lt;code>CHECKDB&lt;/code>. I also don&amp;rsquo;t want to have to rerun &lt;code>CHECKDB&lt;/code> if there’s an issue. Essentially, I only need to review the data in the table to see the error, and investigate from there.&lt;/p>
&lt;p>This table contains the standard output from a &lt;code>CHECKDB&lt;/code>, but also includes a few additional columns including the name of the database being tested and the date that the check was performed. The table is populated via the final stored procedure in this process:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;span class="lnt">36
&lt;/span>&lt;span class="lnt">37
&lt;/span>&lt;span class="lnt">38
&lt;/span>&lt;span class="lnt">39
&lt;/span>&lt;span class="lnt">40
&lt;/span>&lt;span class="lnt">41
&lt;/span>&lt;span class="lnt">42
&lt;/span>&lt;span class="lnt">43
&lt;/span>&lt;span class="lnt">44
&lt;/span>&lt;span class="lnt">45
&lt;/span>&lt;span class="lnt">46
&lt;/span>&lt;span class="lnt">47
&lt;/span>&lt;span class="lnt">48
&lt;/span>&lt;span class="lnt">49
&lt;/span>&lt;span class="lnt">50
&lt;/span>&lt;span class="lnt">51
&lt;/span>&lt;span class="lnt">52
&lt;/span>&lt;span class="lnt">53
&lt;/span>&lt;span class="lnt">54
&lt;/span>&lt;span class="lnt">55
&lt;/span>&lt;span class="lnt">56
&lt;/span>&lt;span class="lnt">57
&lt;/span>&lt;span class="lnt">58
&lt;/span>&lt;span class="lnt">59
&lt;/span>&lt;span class="lnt">60
&lt;/span>&lt;span class="lnt">61
&lt;/span>&lt;span class="lnt">62
&lt;/span>&lt;span class="lnt">63
&lt;/span>&lt;span class="lnt">64
&lt;/span>&lt;span class="lnt">65
&lt;/span>&lt;span class="lnt">66
&lt;/span>&lt;span class="lnt">67
&lt;/span>&lt;span class="lnt">68
&lt;/span>&lt;span class="lnt">69
&lt;/span>&lt;span class="lnt">70
&lt;/span>&lt;span class="lnt">71
&lt;/span>&lt;span class="lnt">72
&lt;/span>&lt;span class="lnt">73
&lt;/span>&lt;span class="lnt">74
&lt;/span>&lt;span class="lnt">75
&lt;/span>&lt;span class="lnt">76
&lt;/span>&lt;span class="lnt">77
&lt;/span>&lt;span class="lnt">78
&lt;/span>&lt;span class="lnt">79
&lt;/span>&lt;span class="lnt">80
&lt;/span>&lt;span class="lnt">81
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-sql" data-lang="sql">&lt;span class="k">CREATE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">OR&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">ALTER&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">Procedure&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">dbo&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">spRunDBCCChecks&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">dbName&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">sysname&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="k">exec&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">BIT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">AS&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">BEGIN&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">DECLARE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">dropCmd&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">nvarchar&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">500&lt;/span>&lt;span class="p">),&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">error&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">nvarchar&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">600&lt;/span>&lt;span class="p">),&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">MailMsg&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">nvarchar&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">500&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">-- DBCC CheckDB will output results that we want to capture to verify if there are errors
&lt;/span>&lt;span class="c1">&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">IF&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">OBJECT_ID&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;tempdb..#tempdbcccheck&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">IS&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NOT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NULL&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">DROP&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">TABLE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">#&lt;/span>&lt;span class="n">tempdbcccheck&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">CREATE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">TABLE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">#&lt;/span>&lt;span class="n">tempdbcccheck&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">Error&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nb">int&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NULL&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="k">Level&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nb">int&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NULL&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="k">State&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nb">int&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NULL&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">MessageText&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nb">varchar&lt;/span>&lt;span class="p">](&lt;/span>&lt;span class="mi">7000&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NULL&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">RepairLevel&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">nvarchar&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">500&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NULL&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">Status&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nb">int&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NULL&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">DbId&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nb">int&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NULL&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">DbFragId&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nb">int&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NULL&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">ObjectId&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nb">int&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NULL&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">IndexId&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nb">int&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NULL&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">PartitionID&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nb">bigint&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NULL&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">AllocUnitID&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nb">bigint&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NULL&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">RidDbId&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nb">int&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NULL&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">RidPruId&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nb">int&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NULL&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">File&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nb">int&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NULL&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">Page&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nb">int&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NULL&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">Slot&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nb">int&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NULL&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">RefDbId&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nb">int&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NULL&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">RefPruId&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nb">int&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NULL&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">RefFile&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nb">int&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NULL&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">RefPage&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nb">int&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NULL&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">RefSlot&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nb">int&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NULL&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">Allocation&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nb">int&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NULL&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">PRINT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;Starting DBCC CHECKDB on &amp;#39;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">dbName&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">INSERT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">INTO&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">#&lt;/span>&lt;span class="n">tempdbcccheck&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">EXEC&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;DBCC CHECKDB(&amp;#39;&amp;#39;&amp;#39;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">dbName&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;&amp;#39;&amp;#39;) with tableresults&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">-- insert the results in the history log table
&lt;/span>&lt;span class="c1">&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">INSERT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">INTO&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">dbo&lt;/span>&lt;span class="p">].[&lt;/span>&lt;span class="n">DBCC_History_Log&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">DatabaseName&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">DBCCCheckDate&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Error&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="k">Level&lt;/span>&lt;span class="p">],&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="k">State&lt;/span>&lt;span class="p">],&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">MessageText&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">RepairLevel&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">Status&lt;/span>&lt;span class="p">],&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">DbId&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">DbFragId&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ObjectId&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">IndexId&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">PartitionID&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">AllocUnitID&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">RidDbId&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">RidPruId&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">File&lt;/span>&lt;span class="p">],&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Page&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Slot&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">RefDbId&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">RefPruId&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">RefFile&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">RefPage&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">RefSlot&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Allocation&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">SELECT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">dbName&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">GETDATE&lt;/span>&lt;span class="p">(),&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">t&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">FROM&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">#&lt;/span>&lt;span class="n">tempdbcccheck&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">t&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">-- if the there were no errors in the database check, then we can safely drop the DB
&lt;/span>&lt;span class="c1">&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">IF&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NOT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">EXISTS&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">SELECT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">FROM&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">dbo&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">DBCC_History_Log&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">WHERE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">DatabaseName&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">dbName&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">AND&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">DBCCCheckDate&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">cast&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">getdate&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">as&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">date&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">AND&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="k">Level&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">10&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">BEGIN&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">UPDATE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">dbo&lt;/span>&lt;span class="p">].[&lt;/span>&lt;span class="n">DatabaseRestoreLog&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">SET&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">PassedDBCCChecks&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">WHERE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">RestoredName&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">dbName&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">AND&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">RestoredDate&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">cast&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">getdate&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">as&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">date&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">PRINT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;Clean DBCCCheck - dropping database [&amp;#39;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">dbName&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="s1">&amp;#39;]&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">SET&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">dropCmd&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;Alter Database [&amp;#39;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">dbName&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;] Set Single_User With Rollback Immediate; Drop Database [&amp;#39;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">dbName&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;];&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">PRINT&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">dropCmd&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">IF&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="k">exec&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">BEGIN&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">EXEC&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">dropCmd&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">END&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">END&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">ELSE&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">BEGIN&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">SET&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">error&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;DBCC CHECKDB generated errors for database &amp;#39;&amp;#39;&amp;#39;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">dbName&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;&amp;#39;&amp;#39; not dropping for further review.&amp;#39;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">-- send an email alert to sql-alerts that the DBCC CHECKDB failed for the restored item
&lt;/span>&lt;span class="c1">&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">EXEC&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">msdb&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">dbo&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">sp_send_dbmail&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">profile_name&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;Mail&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">recipients&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;useyourown@email.com&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">subject&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">error&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">body&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">error&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">RAISERROR&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">error&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">18&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">END&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">END&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;p>In this stored procedure I do a couple of things, run &lt;code>CHECKDB&lt;/code> and then validate that the &lt;code>CHECKDB&lt;/code> was successful. If it wasn&amp;rsquo;t successful, then we don&amp;rsquo;t drop the database, and we send an email alert stating that something needs to be investigated. If everything is ok with the &lt;code>CHECKDB&lt;/code>, then we drop the database and it will either move to the next database to restore, or it will exit the job if we have hit the 12 hour time limit.&lt;/p>
&lt;p>To get this entire process done, I have two SQL Agent jobs running - one to update the list of the &lt;code>DatabasesToRestore&lt;/code> via &lt;code>spUpdateDatabasesToRestore&lt;/code> and one that executes the Backup and Restore Testing procedure (&lt;code>spBackupRestoreTesting&lt;/code>).&lt;/p>
&lt;p>I&amp;rsquo;ve added all these scripts to &lt;a href="https://github.com/tarynpratt/misc_sql_scripts/tree/master/BackupRestoreTesting">GitHub&lt;/a>, feel free to peruse them that way or tell me how terrible they are.&lt;/p>
&lt;h2 id="final-thoughts">Final Thoughts&lt;/h2>
&lt;p>Currently, we&amp;rsquo;re restoring 398 databases daily and running the &lt;code>DBCC CHECKDB&lt;/code> on each one of them. Does that seem like a lot? Probably, but this process has, in fact, caught issues with some of our databases.&lt;/p>
&lt;p>If the &lt;code>CHECKDB&lt;/code> finds an issue, it doesn&amp;rsquo;t drop the database (leaving it for investigation), it sends an email alert. This gives me ample time to figure out what has gone wrong and fix the issue on the production database.&lt;/p>
&lt;p>Will this capture issues on the production server that don&amp;rsquo;t exist in the backup? No, but it does give us confidence in our backups, and has found issues with databases that needed to be fixed - like data corruption. Ideally, you&amp;rsquo;d be able to run &lt;code>CHECKDB&lt;/code> in production, but when you can&amp;rsquo;t, you need to get creative. This process does two things - tests our backups daily to ensure they’re working, and runs the &lt;code>CHECKDB&lt;/code> to make sure there is no corruption in the backup (which would bite us if we ever needed a backup).&lt;/p>
&lt;p>How do you run &lt;code>CHECKDB&lt;/code>? Do you run it on your production servers or do you run it on a separate server?&lt;/p></description></item><item><title>Fighting with Deadlocks</title><link>https://tarynpivots.com/post/2021/fighting-with-deadlocks/</link><pubDate>Fri, 09 Apr 2021 04:00:00 +0000</pubDate><guid>https://tarynpivots.com/post/2021/fighting-with-deadlocks/</guid><description>&lt;p>If you follow me on &lt;a href="https://twitter.com/tarynpivots">Twitter&lt;/a>, then you might have noticed that I&amp;rsquo;ve been fighting a lot of fires lately. Between high CPU (several times &lt;a href="https://twitter.com/tarynpivots/status/1371869915053916167">1&lt;/a>, &lt;a href="https://twitter.com/tarynpivots/status/1372282151220117504">2&lt;/a>), &lt;a href="https://twitter.com/tarynpivots/status/1375473620034580480">blocking queries&lt;/a>, a &lt;a href="https://twitter.com/tarynpivots/status/1375620062736871428">slow failover&lt;/a>, and &lt;a href="https://twitter.com/tarynpivots/status/1373990159121752065">deadlocks&lt;/a> there have been a ton of things that needed attention. Not all of these issues are interesting to people, but one of them might be&amp;hellip;the deadlocks. I’m going to go into details about what deadlocks we hit, why we hit them, and how we resolved them.&lt;/p>
&lt;h2 id="background">Background&lt;/h2>
&lt;p>A few years ago, we launched &lt;a href="https://stackoverflow.com/teams/">Stack Overflow for Teams&lt;/a>, which allows you to have a private version of Stack Overflow to share knowledge. I haven&amp;rsquo;t written about the infrastructure of Teams, but my colleague, &lt;a href="https://twitter.com/deanward81">Dean Ward&lt;/a>, gave a &lt;a href="https://www.youtube.com/watch?v=g-hZZjN_Cbg">presentation about how Stack Overflow for Teams was built&lt;/a> - it&amp;rsquo;s good, go watch it.&lt;/p>
&lt;p>Here&amp;rsquo;s a brief overview of what we have on the SQL Server side - we have a 3 server cluster for Teams (2 in NY, 1 in CO) that are using Always On Availability Groups. We use schemas to keep teams separated from each other, and we put 10,000 schemas in each database, so we have multiple databases with 10,000 schemas in each one.&lt;/p>
&lt;h3 id="managing-schemas">Managing Schemas&lt;/h3>
&lt;p>When we started Stack Overflow for Teams, we manually provisioned each new team (yeah, I know) - this meant someone ran a script to create the login, user, schema, tables, and then granted the necessary permissions for the schema user. Even though we joked about it, manually provisioning new teams wasn’t scalable and didn’t last long, so we came up with a process to pre-provision everything needed for a new team to start.&lt;/p>
&lt;p>We never wanted to run into a race condition or incur the performance overhead with setting up a new team, so we pre-provisioned a set number of schemas that would be ready when someone signed up. The pre-provisioned teams would be already set up with&lt;/p>
&lt;ul>
&lt;li>Login&lt;/li>
&lt;li>User&lt;/li>
&lt;li>Schema&lt;/li>
&lt;li>Permissions&lt;/li>
&lt;li>Tables, Views, and any other database objects&lt;/li>
&lt;/ul>
&lt;p>Initially, we had about 100 schemas ready to go, and then had a scheduled job that would check periodically to refresh our provisioned bucket and replenish it back to full. We realized very quickly that 100 schemas wasn’t enough because we constantly hit issues with the logins not being &lt;a href="https://www.tarynpivots.com/post/2020/syncing-logins-between-availablity-group-replicas/">available on the secondary replicas&lt;/a>, so we decided to increase the provisioned pool to 500. For the last several years, a pool of 500 schemas worked great, until it didn’t.&lt;/p>
&lt;h2 id="the-launch-of-free-teams">The Launch of Free Teams&lt;/h2>
&lt;p>As expected, with the announcement that Stack Overflow for Teams is &lt;a href="https://meta.stackexchange.com/questions/362203/stack-overflow-for-teams-is-now-free-for-up-to-50-users-forever">free for up to 50 users&lt;/a>, we saw an incredible spike in sign-ups. Along with the spike in sign-ups, I started to receive a huge increase in alerts about deadlocks on the primary SQL Server for Teams. In the two weeks it took us to resolve the deadlocks, we hit at least 200 deadlocks.&lt;/p>
&lt;h4 id="what-is-a-deadlock">What Is A Deadlock?&lt;/h4>
&lt;p>A deadlock occurs when two processes want access to the same resource. The first one has a lock on a resource, and the second process wants the same access, or vice versa. Since the processes are competing for the same resource, the result is a situation where one process needs to terminate so the other can continue.&lt;/p>
&lt;h3 id="time-to-investigate">Time to Investigate&lt;/h3>
&lt;p>Most people who manage a highly transactional database are probably familiar with deadlocks, or have seen them pop up every now and then. We had them in the past when provisioning schemas, but it was long ago and we thought the issue was over.&lt;/p>
&lt;p>But, after the launch of free teams, I was receiving a deadlock email alert every few hours from &lt;a href="https://www.sentryone.com/products/sentryone-platform/sql-sentry/sql-server-performance-monitoring">SQL Sentry&lt;/a>. While these email alerts provided some details into the issue, I needed to dig in further to figure out what exactly was happening, and try to resolve it. Thankfully, I have a handful of tools at my disposal to help investigate what we were seeing.&lt;/p>
&lt;p>Between SQL Sentry and Brent Ozar’s &lt;a href="https://www.brentozar.com/archive/2017/12/introducing-sp_blitzlock-troubleshooting-sql-server-deadlocks/">sp_BlitzLock&lt;/a>, I was able to get more details on each deadlock, including what the victim was, what type of locks we were hitting, and the processes involved in each deadlock.&lt;/p>
&lt;p>The query text of the victim was pretty much &lt;a href="https://twitter.com/tarynpivots/status/1374741995051122689">same each time&lt;/a>. As you can see, it wasn&amp;rsquo;t very helpful, as the victim was always when another process was just trying to set the transaction level (or at least that’s all the text we ever captured):&lt;/p>
&lt;p>&lt;img src="https://tarynpivots.com/image/2021/deadlockvictim.png" alt="Repeated deadlock victim">&lt;/p>
&lt;p>The other query involved in the deadlock was also basically same each time:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-sql" data-lang="sql">&lt;span class="k">GRANT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">SELECT&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">INSERT&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">UPDATE&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">DELETE&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">ALTER&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">ON&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">SCHEMA&lt;/span>&lt;span class="p">::[&lt;/span>&lt;span class="n">channel29630&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">TO&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">channel29630&lt;/span>&lt;span class="p">]&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;p>And the &lt;a href="https://twitter.com/tarynpivots/status/1374714148085362690">deadlock graph&lt;/a> for each one was always the same, but it provided a critical piece of information that we needed - the lock type:&lt;/p>
&lt;p>&lt;img src="https://tarynpivots.com/image/2021/deadlockgraph.png" alt="Deadlock graph">&lt;/p>
&lt;p>The dreaded Sch-M lock. I knew about the evils of this lock from &lt;a href="https://michaeljswart.com/2013/04/the-sch-m-lock-is-evil/">Michael J Swart&lt;/a>, but we had never faced it to this level before.&lt;/p>
&lt;p>Every deadlock we hit was due to the permission &lt;code>GRANT&lt;/code> during provisioning. Even if the victim query was on a completely different schema, the Sch-M lock taken by the permission grant always won.&lt;/p>
&lt;h3 id="resolving-the-deadlocks">Resolving the Deadlocks&lt;/h3>
&lt;p>Now that we identified the cause of the deadlock, it was time to figure out how to stop them. We brainstormed several ideas on how to fix it.&lt;/p>
&lt;p>The scheduled job to replenish the pool of pre-provisioned teams runs every 5 minutes, so first we thought what if we spaced out that process? That wouldn&amp;rsquo;t work because all it would do is punt the problem to the new timeframe, which might potentially make it worse since we&amp;rsquo;d need to provision more schemas at a time. So, instead of the 5 - 15 we were provisioning every 5 minutes, we might need to create 50 or more if we waited an hour.&lt;/p>
&lt;p>We also thought about provisioning a larger number of schemas, and then replenishing the pool nightly when we have less traffic, but again realized that it was just punting the problem - not actually fixing it.&lt;/p>
&lt;p>Finally, we discussed pre-provisioning an entire database. That might work.&lt;/p>
&lt;p>As I mentioned earlier, we put 10,000 schemas in a database, so the thinking was, if we set up a new database and provisioned the whole database with what we needed to start a team, then we no longer would hit the deadlocks by granting the permissions every 5 minutes. By setting up all the permissions ahead of time, the scheduled job would still need the Sch-M lock, but it would be taking it on a database not yet in use, hopefully removing its impact and the deadlocks.&lt;/p>
&lt;p>The plan was to change the order in which we provisioned the new teams. We would pre-provision a new database with 10,000 logins, users, schemas, and their permissions, and would still keep our current bucket of 500 provisioned schemas that have the tables, views, and other database objects (the ones ready to go when a new team is created). Once we got close to using all of the schemas in a database, the pre-provision process would kick off on the next database and set up the next 10,000 schemas, removing the chances of getting deadlocks.&lt;/p>
&lt;p>Dean Ward spent several days refactoring the code for the provisioning and when we pushed it out, it was time to create &lt;a href="https://twitter.com/tarynpivots/status/1377279012049879041">all the schemas&lt;/a> and see if the deadlocks went away.&lt;/p>
&lt;p>It worked.&lt;/p>
&lt;p>We resolved the deadlock by refactoring the process so we weren&amp;rsquo;t taking the Sch-M lock for the permission grant every 5 minutes. By granting all the permissions ahead of time, we no longer have to fight with other processes and we stopped getting deadlocks.&lt;/p>
&lt;h2 id="final-thoughts">Final Thoughts&lt;/h2>
&lt;p>Deadlocks are &amp;ldquo;fun&amp;rdquo; and can be complicated. This one was interesting because I had never seen a deadlock victim on setting the transaction isolation level, but once I saw the Sch-M lock it was clear we needed to do some refactoring of our process. Sch-M locks are definitely not fun, and if you need to take that lock on a heavily used database you might need to look at refactoring or even performing the work off hours.&lt;/p>
&lt;h3 id="side-note">Side Note&lt;/h3>
&lt;p>Oh, and while we did resolve the deadlocks, we also introduced &lt;a href="https://twitter.com/tarynpivots/status/1377783511399030784">an issue&lt;/a> that none of us thought about until we started getting exceptions. The new refactored pre-provisioning process creates the login, user and schema, but when we move that schema to a provisioned state and we create the tables and database objects in the schema, we also change the password for the login. Our process to sync logins &lt;a href="https://www.tarynpivots.com/post/2020/syncing-logins-between-availablity-group-replicas/">across our replicas&lt;/a> didn&amp;rsquo;t account for those password changes which resulted in login failures, so we had to quickly resolve that issue after the change.&lt;/p>
&lt;p>And by the way, if you’re interested, go &lt;a href="https://stackoverflow.com/teams/create/free?utm_source=so-team&amp;amp;utm_medium=referral&amp;amp;utm_campaign=free&amp;amp;utm_content=codeSW0">sign up for a team&lt;/a>, it&amp;rsquo;s free and it won&amp;rsquo;t generate a deadlock.&lt;/p></description></item><item><title>Syncing Logins Between Availability Group Replicas</title><link>https://tarynpivots.com/post/2020/syncing-logins-between-availablity-group-replicas/</link><pubDate>Fri, 18 Dec 2020 04:00:00 +0000</pubDate><guid>https://tarynpivots.com/post/2020/syncing-logins-between-availablity-group-replicas/</guid><description>&lt;p>I mentioned this in a few previous posts, but for for those who may have missed it or forgotten, here’s a quick refresher - we use &lt;a href="https://docs.microsoft.com/en-us/sql/database-engine/availability-groups/windows/always-on-availability-groups-sql-server?view=sql-server-ver15">Always On Availability Groups&lt;/a> at &lt;a href="https://stackoverflow.com">Stack Overflow&lt;/a> on all of our main production servers running the network of public Q&amp;amp;A sites, Jobs, and &lt;a href="https://stackoverflow.com/teams">Stack Overflow for Teams&lt;/a>. It&amp;rsquo;s a great way to implement disaster recovery for a SQL Server environment.&lt;/p>
&lt;p>Always On Availability Groups can support up to nine availability replicas, and while we don’t use anywhere near that many replicas in each of our clusters, we do have 2 replicas per cluster (3 servers total), with the replicas being used as a &lt;a href="https://docs.microsoft.com/en-us/sql/database-engine/availability-groups/windows/active-secondaries-readable-secondary-replicas-always-on-availability-groups?view=sql-server-ver15">readable secondary&lt;/a>.&lt;/p>
&lt;p>Since we use readable secondaries in our environments, the application needs to connect to both the primary and the secondary servers with the same login. The catch is, logins don’t automatically sync across replicas. If the logins don’t sync, the application won’t connect to a secondary, which results in login failures.&lt;/p>
&lt;h2 id="copying-logins-with-a-t-sql-script">Copying Logins with a T-SQL Script&lt;/h2>
&lt;p>Before you jump the gun and start criticizing my method, know that I’m aware there are other ways to do this, including using &lt;a href="https://dbatools.io/keeping-availability-group-logins-in-sync-automatically/">dbatools&lt;/a>, but this method has been around since the early days of Stack Overflow and the Stack Exchange network (with some &lt;a href="https://www.tarynpivots.com/post/system-view-gotcha-with-sql-server-2019/">modifications&lt;/a>) and it works for our needs for now.&lt;/p>
&lt;p>We don’t create new databases very often, but on &lt;a href="https://stackoverflow.com/teams">Stack Overflow for Teams&lt;/a> we use schemas to keep customers separated from each other - i.e. every team has a login and user to allow it to query their data. While we pre-provision a set number of schemas, tables, and other database objects for teams, we want the logins to sync on a frequent basis to avoid login failures on the secondaries. We have a job in place which syncs the logins via a SQL Server Agent job every 10 minutes for Stack Overflow for Teams. (We have a similar job for the public Q&amp;amp;A sites that runs nightly.)&lt;/p>
&lt;p>The job needs to do several things:&lt;/p>
&lt;ol>
&lt;li>Get a list of the existing logins on the primary&lt;/li>
&lt;li>Create the logins on replica&lt;/li>
&lt;li>Drop any logins no longer being used i.e. teams that are deleted&lt;/li>
&lt;/ol>
&lt;h3 id="gather-existing-logins">Gather Existing Logins&lt;/h3>
&lt;p>The first thing the job does is, create a temp table called &lt;code>@logins&lt;/code> to store all the details needed from the primary. The table is pretty basic:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-sql" data-lang="sql">&lt;span class="k">DECLARE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">logins&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">TABLE&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">SID&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">varbinary&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">256&lt;/span>&lt;span class="p">),&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Login&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">sysname&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">SQL&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">varchar&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">1024&lt;/span>&lt;span class="p">),&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">DefaultDB&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">sysname&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="p">);&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;p>The table is populated by connecting to the primary server using &lt;a href="https://docs.microsoft.com/en-us/sql/t-sql/functions/openquery-transact-sql?view=sql-server-ver15">&lt;code>OPENQUERY&lt;/code>&lt;/a> (Eeek a linked server)&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-sql" data-lang="sql">&lt;span class="k">SELECT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">FROM&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">OPENQUERY&lt;/span>&lt;span class="p">([&lt;/span>&lt;span class="k">SQL&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">AG&lt;/span>&lt;span class="p">],&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;
&lt;/span>&lt;span class="s1"> SELECT p.sid sid_varbinary, p.name, p.default_database_name,
&lt;/span>&lt;span class="s1"> sl.password_hash pwd_varbinary,
&lt;/span>&lt;span class="s1"> CASE sl.is_policy_checked WHEN 1 THEN &amp;#39;&amp;#39;ON&amp;#39;&amp;#39; WHEN 0 THEN &amp;#39;&amp;#39;OFF&amp;#39;&amp;#39; ELSE NULL END is_policy_checked,
&lt;/span>&lt;span class="s1"> CASE sl.is_expiration_checked WHEN 1 THEN &amp;#39;&amp;#39;ON&amp;#39;&amp;#39; WHEN 0 THEN &amp;#39;&amp;#39;OFF&amp;#39;&amp;#39; ELSE NULL END is_expiration_checked
&lt;/span>&lt;span class="s1"> FROM sys.server_principals p
&lt;/span>&lt;span class="s1"> JOIN sys.syslogins l ON l.name = p.name
&lt;/span>&lt;span class="s1"> JOIN sys.sql_logins sl ON l.name = sl.name
&lt;/span>&lt;span class="s1"> WHERE p.type = &amp;#39;&amp;#39;S&amp;#39;&amp;#39;
&lt;/span>&lt;span class="s1"> AND p.name &amp;lt;&amp;gt; &amp;#39;&amp;#39;sa&amp;#39;&amp;#39;
&lt;/span>&lt;span class="s1"> AND l.denylogin = 0
&lt;/span>&lt;span class="s1"> AND l.hasaccess = 1
&lt;/span>&lt;span class="s1"> AND p.is_disabled = 0
&lt;/span>&lt;span class="s1"> ORDER BY p.name&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;p>and executing &lt;a href="https://docs.microsoft.com/en-us/troubleshoot/sql/security/transfer-logins-passwords-between-instances">sp_hexadecimal&lt;/a> to get the SID and password which are then used to create a dynamic SQL string which is stored in the temp table.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-sql" data-lang="sql">&lt;span class="k">EXEC&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">sp_hexadecimal&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">PWD_varbinary&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">PWD_string&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">OUT&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">EXEC&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">sp_hexadecimal&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">SID_varbinary&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">SID_string&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">OUT&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">SET&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">sqlString&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;CREATE LOGIN &amp;#39;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">QUOTENAME&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">name&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39; WITH PASSWORD = &amp;#39;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">PWD_string&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39; HASHED, SID = &amp;#39;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">SID_string&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;, DEFAULT_DATABASE = [&amp;#39;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">defaultdb&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;]&amp;#39;&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;, CHECK_POLICY = &amp;#39;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">is_policy_checked&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;, CHECK_EXPIRATION = &amp;#39;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">is_expiration_checked&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">INSERT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">INTO&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">logins&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">SID&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Login&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">SQL&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">DefaultDB&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">VALUES&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">SID_varbinary&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">name&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">sqlString&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">defaultdb&lt;/span>&lt;span class="p">);&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;h3 id="create-logins-on-replicas">Create Logins on Replicas&lt;/h3>
&lt;p>Now that the table has all the logins from the primary, it’s time to create them locally. While it’s possible that we have only one login to sync, it’s more likely we have multiple. Keeping this in mind, the script loops through the temp table and executes the dynamic SQL for each login that doesn’t already exist on the replica.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-sql" data-lang="sql">&lt;span class="k">DECLARE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="k">sql&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">varchar&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">1024&lt;/span>&lt;span class="p">),&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">db&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">sysname&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">loginname&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">sysname&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">DECLARE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">login_cursor&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">CURSOR&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">FOR&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">SELECT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">SQL&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">DefaultDB&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Login&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">FROM&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">logins&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">WHERE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">SID&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NOT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">IN&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">SELECT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">sid&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">FROM&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">sys&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">server_principals&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">AND&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">EXISTS&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">SELECT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">FROM&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">sys&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">databases&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">WHERE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">name&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">DefaultDB&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">AND&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Login&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NOT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">IN&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">SELECT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">name&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">FROM&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">sys&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">server_principals&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">WHERE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">name&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Login&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">And&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">type&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;S&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">OPEN&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">login_cursor&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="c1">-- add new logins
&lt;/span>&lt;span class="c1">&lt;/span>&lt;span class="k">FETCH&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NEXT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">FROM&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">login_cursor&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">INTO&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="k">sql&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">db&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">loginname&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="n">WHILE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@@&lt;/span>&lt;span class="n">fetch_status&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">BEGIN&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">EXEC&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="k">sql&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">FETCH&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NEXT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">FROM&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">login_cursor&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">INTO&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="k">sql&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">db&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">loginname&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">END&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">CLOSE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">login_cursor&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">DEALLOCATE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">login_cursor&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;h3 id="drop-unused-logins-on-replicas">Drop Unused Logins on Replicas&lt;/h3>
&lt;p>On the public Q&amp;amp;A sites, like Stack Overflow, we don’t need to drop logins, but after running Stack Overflow for Teams for several months, we realized very quickly that we need to purge logins no longer being used. The last step of the job goes through the existing logins on the secondaries and deletes any that don’t exist on the primary. Using the original &lt;code>@logins&lt;/code> temp table I can get a list of the logins to drop:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-sql" data-lang="sql">&lt;span class="k">SELECT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">p&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">sid&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">p&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">name&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">p&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">default_database_name&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">FROM&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">sys&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">server_principals&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">p&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">JOIN&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">sys&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">syslogins&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">l&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">ON&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">l&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">name&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">p&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">name&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">JOIN&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">sys&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">sql_logins&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">sl&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">ON&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">l&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">name&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">sl&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">name&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">WHERE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">p&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="k">type&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;S&amp;#39;&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">AND&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">p&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">name&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">&amp;lt;&amp;gt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;sa&amp;#39;&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">AND&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">l&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">denylogin&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">AND&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">l&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">hasaccess&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">AND&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">p&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">is_disabled&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">AND&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NOT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">EXISTS&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">SELECT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">FROM&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">logins&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">cl&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">WHERE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">p&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">sid&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">cl&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">sid&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">AND&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">p&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">name&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">cl&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">Login&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">AND&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">p&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">default_database_name&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">cl&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">DefaultDB&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">ORDER&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">BY&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">p&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">name&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;p>then it creates a dynamic SQL string which gets executed cleaning up the unused logins.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-sql" data-lang="sql">&lt;span class="k">SET&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">drop_sqlString&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;DROP LOGIN &amp;#39;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">QUOTENAME&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">name&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;; &amp;#39;&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="c1">--print @drop_sqlString
&lt;/span>&lt;span class="c1">&lt;/span>&lt;span class="k">EXEC&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">drop_sqlString&lt;/span>&lt;span class="p">);&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;h3 id="putting-the-pieces-together">Putting the Pieces Together&lt;/h3>
&lt;p>Both the create and delete of the logins are done in various loops, in other words cursors. The full script is below:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt"> 10
&lt;/span>&lt;span class="lnt"> 11
&lt;/span>&lt;span class="lnt"> 12
&lt;/span>&lt;span class="lnt"> 13
&lt;/span>&lt;span class="lnt"> 14
&lt;/span>&lt;span class="lnt"> 15
&lt;/span>&lt;span class="lnt"> 16
&lt;/span>&lt;span class="lnt"> 17
&lt;/span>&lt;span class="lnt"> 18
&lt;/span>&lt;span class="lnt"> 19
&lt;/span>&lt;span class="lnt"> 20
&lt;/span>&lt;span class="lnt"> 21
&lt;/span>&lt;span class="lnt"> 22
&lt;/span>&lt;span class="lnt"> 23
&lt;/span>&lt;span class="lnt"> 24
&lt;/span>&lt;span class="lnt"> 25
&lt;/span>&lt;span class="lnt"> 26
&lt;/span>&lt;span class="lnt"> 27
&lt;/span>&lt;span class="lnt"> 28
&lt;/span>&lt;span class="lnt"> 29
&lt;/span>&lt;span class="lnt"> 30
&lt;/span>&lt;span class="lnt"> 31
&lt;/span>&lt;span class="lnt"> 32
&lt;/span>&lt;span class="lnt"> 33
&lt;/span>&lt;span class="lnt"> 34
&lt;/span>&lt;span class="lnt"> 35
&lt;/span>&lt;span class="lnt"> 36
&lt;/span>&lt;span class="lnt"> 37
&lt;/span>&lt;span class="lnt"> 38
&lt;/span>&lt;span class="lnt"> 39
&lt;/span>&lt;span class="lnt"> 40
&lt;/span>&lt;span class="lnt"> 41
&lt;/span>&lt;span class="lnt"> 42
&lt;/span>&lt;span class="lnt"> 43
&lt;/span>&lt;span class="lnt"> 44
&lt;/span>&lt;span class="lnt"> 45
&lt;/span>&lt;span class="lnt"> 46
&lt;/span>&lt;span class="lnt"> 47
&lt;/span>&lt;span class="lnt"> 48
&lt;/span>&lt;span class="lnt"> 49
&lt;/span>&lt;span class="lnt"> 50
&lt;/span>&lt;span class="lnt"> 51
&lt;/span>&lt;span class="lnt"> 52
&lt;/span>&lt;span class="lnt"> 53
&lt;/span>&lt;span class="lnt"> 54
&lt;/span>&lt;span class="lnt"> 55
&lt;/span>&lt;span class="lnt"> 56
&lt;/span>&lt;span class="lnt"> 57
&lt;/span>&lt;span class="lnt"> 58
&lt;/span>&lt;span class="lnt"> 59
&lt;/span>&lt;span class="lnt"> 60
&lt;/span>&lt;span class="lnt"> 61
&lt;/span>&lt;span class="lnt"> 62
&lt;/span>&lt;span class="lnt"> 63
&lt;/span>&lt;span class="lnt"> 64
&lt;/span>&lt;span class="lnt"> 65
&lt;/span>&lt;span class="lnt"> 66
&lt;/span>&lt;span class="lnt"> 67
&lt;/span>&lt;span class="lnt"> 68
&lt;/span>&lt;span class="lnt"> 69
&lt;/span>&lt;span class="lnt"> 70
&lt;/span>&lt;span class="lnt"> 71
&lt;/span>&lt;span class="lnt"> 72
&lt;/span>&lt;span class="lnt"> 73
&lt;/span>&lt;span class="lnt"> 74
&lt;/span>&lt;span class="lnt"> 75
&lt;/span>&lt;span class="lnt"> 76
&lt;/span>&lt;span class="lnt"> 77
&lt;/span>&lt;span class="lnt"> 78
&lt;/span>&lt;span class="lnt"> 79
&lt;/span>&lt;span class="lnt"> 80
&lt;/span>&lt;span class="lnt"> 81
&lt;/span>&lt;span class="lnt"> 82
&lt;/span>&lt;span class="lnt"> 83
&lt;/span>&lt;span class="lnt"> 84
&lt;/span>&lt;span class="lnt"> 85
&lt;/span>&lt;span class="lnt"> 86
&lt;/span>&lt;span class="lnt"> 87
&lt;/span>&lt;span class="lnt"> 88
&lt;/span>&lt;span class="lnt"> 89
&lt;/span>&lt;span class="lnt"> 90
&lt;/span>&lt;span class="lnt"> 91
&lt;/span>&lt;span class="lnt"> 92
&lt;/span>&lt;span class="lnt"> 93
&lt;/span>&lt;span class="lnt"> 94
&lt;/span>&lt;span class="lnt"> 95
&lt;/span>&lt;span class="lnt"> 96
&lt;/span>&lt;span class="lnt"> 97
&lt;/span>&lt;span class="lnt"> 98
&lt;/span>&lt;span class="lnt"> 99
&lt;/span>&lt;span class="lnt">100
&lt;/span>&lt;span class="lnt">101
&lt;/span>&lt;span class="lnt">102
&lt;/span>&lt;span class="lnt">103
&lt;/span>&lt;span class="lnt">104
&lt;/span>&lt;span class="lnt">105
&lt;/span>&lt;span class="lnt">106
&lt;/span>&lt;span class="lnt">107
&lt;/span>&lt;span class="lnt">108
&lt;/span>&lt;span class="lnt">109
&lt;/span>&lt;span class="lnt">110
&lt;/span>&lt;span class="lnt">111
&lt;/span>&lt;span class="lnt">112
&lt;/span>&lt;span class="lnt">113
&lt;/span>&lt;span class="lnt">114
&lt;/span>&lt;span class="lnt">115
&lt;/span>&lt;span class="lnt">116
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-sql" data-lang="sql">&lt;span class="k">DECLARE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">name&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">sysname&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">PWD_varbinary&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">varbinary&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">256&lt;/span>&lt;span class="p">),&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">PWD_string&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">varchar&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">514&lt;/span>&lt;span class="p">),&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">SID_varbinary&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">varbinary&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">85&lt;/span>&lt;span class="p">),&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">SID_string&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">varchar&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">514&lt;/span>&lt;span class="p">),&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">sqlString&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">varchar&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">1024&lt;/span>&lt;span class="p">),&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">is_policy_checked&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">varchar&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">3&lt;/span>&lt;span class="p">),&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">is_expiration_checked&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">varchar&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">3&lt;/span>&lt;span class="p">),&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">defaultdb&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">sysname&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">DECLARE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">logins&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">TABLE&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">SID&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">varbinary&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">256&lt;/span>&lt;span class="p">),&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Login&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">sysname&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">SQL&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">varchar&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">1024&lt;/span>&lt;span class="p">),&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">DefaultDB&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">sysname&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">DECLARE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">login_cursor&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">CURSOR&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">FOR&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">SELECT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">FROM&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">OPENQUERY&lt;/span>&lt;span class="p">([&lt;/span>&lt;span class="k">SQL&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">AG&lt;/span>&lt;span class="p">],&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;
&lt;/span>&lt;span class="s1"> SELECT p.sid, p.name, p.default_database_name,
&lt;/span>&lt;span class="s1"> sl.password_hash pwd_varbinary,
&lt;/span>&lt;span class="s1"> CASE sl.is_policy_checked WHEN 1 THEN &amp;#39;&amp;#39;ON&amp;#39;&amp;#39; WHEN 0 THEN &amp;#39;&amp;#39;OFF&amp;#39;&amp;#39; ELSE NULL END is_policy_checked,
&lt;/span>&lt;span class="s1"> CASE sl.is_expiration_checked WHEN 1 THEN &amp;#39;&amp;#39;ON&amp;#39;&amp;#39; WHEN 0 THEN &amp;#39;&amp;#39;OFF&amp;#39;&amp;#39; ELSE NULL END is_expiration_checked
&lt;/span>&lt;span class="s1"> FROM sys.server_principals p
&lt;/span>&lt;span class="s1"> JOIN sys.syslogins l ON l.name = p.name
&lt;/span>&lt;span class="s1"> JOIN sys.sql_logins sl ON l.name = sl.name
&lt;/span>&lt;span class="s1"> WHERE p.type = &amp;#39;&amp;#39;S&amp;#39;&amp;#39;
&lt;/span>&lt;span class="s1"> AND p.name &amp;lt;&amp;gt; &amp;#39;&amp;#39;sa&amp;#39;&amp;#39;
&lt;/span>&lt;span class="s1"> AND l.denylogin = 0
&lt;/span>&lt;span class="s1"> AND l.hasaccess = 1
&lt;/span>&lt;span class="s1"> AND p.is_disabled = 0
&lt;/span>&lt;span class="s1"> ORDER BY p.name&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">OPEN&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">login_cursor&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">FETCH&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NEXT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">FROM&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">login_cursor&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">INTO&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">SID_varbinary&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">name&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">defaultdb&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">PWD_varbinary&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">is_policy_checked&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">is_expiration_checked&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="n">WHILE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@@&lt;/span>&lt;span class="n">fetch_status&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">BEGIN&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">EXEC&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">sp_hexadecimal&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">PWD_varbinary&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">PWD_string&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">OUT&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">EXEC&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">sp_hexadecimal&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">SID_varbinary&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">SID_string&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">OUT&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">SET&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">sqlString&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;CREATE LOGIN &amp;#39;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">QUOTENAME&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">name&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39; WITH PASSWORD = &amp;#39;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">PWD_string&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39; HASHED, SID = &amp;#39;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">SID_string&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;, DEFAULT_DATABASE = [&amp;#39;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">defaultdb&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;]&amp;#39;&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;, CHECK_POLICY = &amp;#39;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">is_policy_checked&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;, CHECK_EXPIRATION = &amp;#39;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">is_expiration_checked&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">INSERT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">INTO&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">logins&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">SID&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Login&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">SQL&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">DefaultDB&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">VALUES&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">SID_varbinary&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">name&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">sqlString&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">defaultdb&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">FETCH&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NEXT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">FROM&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">login_cursor&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">INTO&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">SID_varbinary&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">name&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">defaultdb&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">PWD_varbinary&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">is_policy_checked&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">is_expiration_checked&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">END&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">CLOSE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">login_cursor&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">DEALLOCATE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">login_cursor&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">DECLARE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="k">sql&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">varchar&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">1024&lt;/span>&lt;span class="p">),&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">db&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">sysname&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">loginname&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">sysname&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">DECLARE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">login_cursor&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">CURSOR&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">FOR&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">SELECT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">SQL&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">DefaultDB&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Login&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">FROM&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">logins&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">WHERE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">SID&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NOT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">IN&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">SELECT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">sid&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">FROM&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">sys&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">server_principals&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">AND&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">EXISTS&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">SELECT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">FROM&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">sys&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">databases&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">WHERE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">name&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">DefaultDB&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">AND&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Login&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NOT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">IN&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">SELECT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">name&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">FROM&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">sys&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">server_principals&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">WHERE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">name&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Login&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">And&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">type&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;S&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">OPEN&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">login_cursor&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="c1">-- add new logins
&lt;/span>&lt;span class="c1">&lt;/span>&lt;span class="k">FETCH&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NEXT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">FROM&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">login_cursor&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">INTO&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="k">sql&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">db&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">loginname&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="n">WHILE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@@&lt;/span>&lt;span class="n">fetch_status&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">BEGIN&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">EXEC&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="k">sql&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">--print @sql
&lt;/span>&lt;span class="c1">&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">FETCH&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NEXT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">FROM&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">login_cursor&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">INTO&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="k">sql&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">db&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">loginname&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">END&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">CLOSE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">login_cursor&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">DEALLOCATE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">login_cursor&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="c1">-- drop logins that are no longer being used
&lt;/span>&lt;span class="c1">&lt;/span>&lt;span class="k">DECLARE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">drop_sqlString&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">nvarchar&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">max&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">DECLARE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">drop_login_cursor&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">CURSOR&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">FOR&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">-- logins on current server that don&amp;#39;t exist on the primary
&lt;/span>&lt;span class="c1">&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">SELECT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">p&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">sid&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">p&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">name&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">p&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">default_database_name&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">FROM&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">sys&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">server_principals&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">p&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">JOIN&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">sys&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">syslogins&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">l&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">ON&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">l&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">name&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">p&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">name&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">JOIN&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">sys&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">sql_logins&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">sl&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">ON&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">l&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">name&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">sl&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">name&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">WHERE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">p&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="k">type&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;S&amp;#39;&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">AND&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">p&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">name&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">&amp;lt;&amp;gt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;sa&amp;#39;&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">AND&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">l&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">denylogin&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">AND&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">l&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">hasaccess&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">AND&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">p&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">is_disabled&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">AND&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NOT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">EXISTS&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">SELECT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">FROM&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">logins&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">cl&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">WHERE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">p&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">sid&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">cl&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">sid&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">AND&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">p&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">name&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">cl&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">Login&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">AND&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">p&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">default_database_name&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">cl&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">DefaultDB&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">ORDER&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">BY&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">p&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">name&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">OPEN&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">drop_login_cursor&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">FETCH&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NEXT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">FROM&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">drop_login_cursor&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">INTO&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">SID_varbinary&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">name&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">defaultdb&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="n">WHILE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@@&lt;/span>&lt;span class="n">fetch_status&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">BEGIN&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">SET&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">drop_sqlString&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;DROP LOGIN &amp;#39;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">QUOTENAME&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">name&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;; &amp;#39;&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">--print @drop_sqlString
&lt;/span>&lt;span class="c1">&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">EXEC&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">drop_sqlString&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">FETCH&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NEXT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">FROM&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">drop_login_cursor&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">INTO&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">SID_varbinary&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">name&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">defaultdb&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">END&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">CLOSE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">drop_login_cursor&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">DEALLOCATE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">drop_login_cursor&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;p>As I said, this might not be the way you would do this, but this works for us and keeps the logins synced across all of our replicas which minimizes login failures. I’ve uploaded the &lt;a href="https://github.com/tarynpratt/misc_sql_scripts/tree/master/SyncLogins">full script&lt;/a> to GitHub, if you&amp;rsquo;re interested in it.&lt;/p></description></item><item><title>Deploying My SQL Server Maintenance Scripts</title><link>https://tarynpivots.com/post/2020/deploying-my-sql-server-maintenance-scripts/</link><pubDate>Fri, 16 Oct 2020 04:00:00 +0000</pubDate><guid>https://tarynpivots.com/post/2020/deploying-my-sql-server-maintenance-scripts/</guid><description>&lt;p>In my &lt;a href="https://www.tarynpivots.com/post/2020/my-sql-server-toolbox/">previous post&lt;/a>, I listed out the tools I use with SQL Server. Some of the tools are SQL scripts that need to be deployed to each server. If you have 1-2 SQL Servers, manually deploying scripts might not be bad, but ideally you don&amp;rsquo;t want to manually deploy anything, so I wrote a little script that allow me to install the SQL scripts in my toolbox to any environment.&lt;/p>
&lt;h2 id="background">Background&lt;/h2>
&lt;p>When I started as the DBA at Stack Overflow, our SQL Servers were kind of a mess (sorry to those who maintained them before me, but they were). Many of the servers hadn&amp;rsquo;t been patched in a long time, and we had different versions of various SQL scripts like, &lt;a href="http://whoisactive.com/">sp_whoisactive&lt;/a> and Brent Ozar&amp;rsquo;s &lt;a href="https://www.brentozar.com/responder/">First Reponder Kit&lt;/a>, everywhere. Since the SQL Servers were now my responsibility I wanted everything in order. I decided that one of my first tasks would be to create a simple process to deploy the same scripts everwhere.&lt;/p>
&lt;h2 id="my-little-deploying-scripts-project">My Little Deploying Scripts Project&lt;/h2>
&lt;p>When I started the project, I had already decided what maintenance tools I wanted to install across our environments - these are most of the SQL scripts in my toolbox - I just needed to figure out how I was going to get them on our 30 servers at the same time.&lt;/p>
&lt;p>After several years away from SQL Server and PowerShell, I needed to get my feet wet again, so I decided to write a very basic process that would do the following:&lt;/p>
&lt;ol>
&lt;li>Put the .sql files somewhere accessible&lt;/li>
&lt;li>Get the list of servers to install on&lt;/li>
&lt;li>Loop through each server and deploy all the .sql files&lt;/li>
&lt;/ol>
&lt;h3 id="storing-the-sql-scripts">Storing the SQL Scripts&lt;/h3>
&lt;p>I decided to put the .sql files in a centralized location that all of the servers would be able to access. This was easy enough because we already had infrastructure in place that could be used for this purpose. All I needed to do was pick a spot and throw all the files in a directory.&lt;/p>
&lt;p>Once the files were in the location, I would just loop through them and execute each one. Step 1 done.&lt;/p>
&lt;h3 id="gathering-the-list-of-sql-servers">Gathering the List of SQL Servers&lt;/h3>
&lt;p>Step 2 in the project was to get a list of all the SQL Servers I wanted the tools on. Easy enough, right? But then I needed a way to parse that list in PowerShell. I wasn&amp;rsquo;t exact sure how I wanted to do this.&lt;/p>
&lt;p>I looked at some of the other tools being used at Stack Overflow for ideas. The SRE team uses &lt;a href="https://puppet.com/">puppet&lt;/a> to deploy to our infrastructure, and puppet uses JSON files to store key-value pairs. I wondered if I could do the same for this tiny little project.&lt;/p>
&lt;p>After several failed attempts at getting the JSON right, I was able to use the following format to store the info about the SQL Servers I wanted to deploy to:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="p">{&lt;/span>
&lt;span class="nt">&amp;#34;ScriptDirectory&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="s2">&amp;#34;\\\\put\\your\\directory\\name\\here\\&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;span class="nt">&amp;#34;servers&amp;#34;&lt;/span>&lt;span class="p">:[&lt;/span>
&lt;span class="p">{&lt;/span>
&lt;span class="nt">&amp;#34;name&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="s2">&amp;#34;sqlserver01&amp;#34;&lt;/span>
&lt;span class="p">,&lt;/span>&lt;span class="nt">&amp;#34;type&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="s2">&amp;#34;zone1_servers&amp;#34;&lt;/span>
&lt;span class="p">},&lt;/span>
&lt;span class="p">{&lt;/span>
&lt;span class="nt">&amp;#34;name&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="s2">&amp;#34;sqlserver02&amp;#34;&lt;/span>
&lt;span class="p">,&lt;/span>&lt;span class="nt">&amp;#34;type&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="s2">&amp;#34;zone3_servers&amp;#34;&lt;/span>
&lt;span class="p">},&lt;/span>
&lt;span class="p">{&lt;/span>
&lt;span class="nt">&amp;#34;name&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="s2">&amp;#34;sqlserver03&amp;#34;&lt;/span>
&lt;span class="p">,&lt;/span>&lt;span class="nt">&amp;#34;type&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="s2">&amp;#34;zone4_servers&amp;#34;&lt;/span>
&lt;span class="p">},&lt;/span>
&lt;span class="p">{&lt;/span>
&lt;span class="nt">&amp;#34;name&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="s2">&amp;#34;protectedsqlserver01&amp;#34;&lt;/span>
&lt;span class="p">,&lt;/span>&lt;span class="nt">&amp;#34;type&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="s2">&amp;#34;zone2_servers&amp;#34;&lt;/span>
&lt;span class="p">}&lt;/span>
&lt;span class="p">]&lt;/span>
&lt;span class="p">}&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;p>In the config.json file, I&amp;rsquo;m storing the &lt;code>ScriptDirectory&lt;/code> which is the path I put all of the .sql files to install. Next, it contains an array of the list of servers. Within the array, I&amp;rsquo;m storing the &lt;code>name&lt;/code> of the server and then the &lt;code>type&lt;/code> of the server. The &lt;code>name&lt;/code> is pretty self-explanatory, just include each server name and you&amp;rsquo;re done.&lt;/p>
&lt;p>The &lt;code>type&lt;/code> might seem slightly odd since these are all SQL Servers, but there was a reason I needed this. We have some servers protected by different firewalls, for example the servers that hold the Stack Overflow and the Stack Exchange network of databases are in a totally different zone, than the servers for &lt;a href="https://stackoverflow.com/teams">Stack Overflow for Teams&lt;/a>. My PowerShell script would need to be able to distinguish between the multiple firewalls, so I used this as the identifier.&lt;/p>
&lt;p>Now that I had a way to get all the servers to push to Step 2 was done.&lt;/p>
&lt;h3 id="my-little-powershell-script">My Little PowerShell Script&lt;/h3>
&lt;p>The last part of the project was to put all the pieces together and write the PowerShell script to deploy everything automatically. I wrote the script to accept a couple of parameters:&lt;/p>
&lt;ul>
&lt;li>&lt;code>$DeploySecure&lt;/code> - this parameter is required and is used to direct the deployment to the specific firewalled areas I mentioned above&lt;/li>
&lt;li>&lt;code>$ServerGroup&lt;/code> - this is an optional parameter and will let you deploy to specific sets of servers based on their &lt;code>type&lt;/code> in the config&lt;/li>
&lt;/ul>
&lt;p>Keep in mind this script is about 3 years old, and I know it could be written much better than it is.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;span class="lnt">36
&lt;/span>&lt;span class="lnt">37
&lt;/span>&lt;span class="lnt">38
&lt;/span>&lt;span class="lnt">39
&lt;/span>&lt;span class="lnt">40
&lt;/span>&lt;span class="lnt">41
&lt;/span>&lt;span class="lnt">42
&lt;/span>&lt;span class="lnt">43
&lt;/span>&lt;span class="lnt">44
&lt;/span>&lt;span class="lnt">45
&lt;/span>&lt;span class="lnt">46
&lt;/span>&lt;span class="lnt">47
&lt;/span>&lt;span class="lnt">48
&lt;/span>&lt;span class="lnt">49
&lt;/span>&lt;span class="lnt">50
&lt;/span>&lt;span class="lnt">51
&lt;/span>&lt;span class="lnt">52
&lt;/span>&lt;span class="lnt">53
&lt;/span>&lt;span class="lnt">54
&lt;/span>&lt;span class="lnt">55
&lt;/span>&lt;span class="lnt">56
&lt;/span>&lt;span class="lnt">57
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-powershell" data-lang="powershell">&lt;span class="cm">&amp;lt;#
&lt;/span>&lt;span class="cm">&lt;/span>&lt;span class="sd">.SYNOPSIS&lt;/span>&lt;span class="cm">
&lt;/span>&lt;span class="cm"> Script for deploying SQL Server maintenance scripts across environments
&lt;/span>&lt;span class="cm">
&lt;/span>&lt;span class="cm">&lt;/span>&lt;span class="sd">.EXAMPLE&lt;/span>&lt;span class="cm">
&lt;/span>&lt;span class="cm"> # for zone 1 environments
&lt;/span>&lt;span class="cm"> .\Deploy-MaintenanceScripts.ps1 -DeploySecure $false
&lt;/span>&lt;span class="cm">
&lt;/span>&lt;span class="cm"> # for zone 2 environments
&lt;/span>&lt;span class="cm"> .\Deploy-MaintenanceScripts.ps1 -DeploySecure $true
&lt;/span>&lt;span class="cm">#&amp;gt;&lt;/span>
&lt;span class="p">[&lt;/span>&lt;span class="k">CmdletBinding&lt;/span>&lt;span class="p">()]&lt;/span> &lt;span class="c">#See http://technet.microsoft.com/en-us/library/hh847884(v=wps.620).aspx for CmdletBinding common parameters&lt;/span>
&lt;span class="k">param&lt;/span>&lt;span class="p">(&lt;/span>
&lt;span class="p">[&lt;/span>&lt;span class="k">parameter&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">Mandatory&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="nv">$true&lt;/span>&lt;span class="p">)]&lt;/span>
&lt;span class="no">[bool]&lt;/span>&lt;span class="nv">$DeploySecure&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="nv">$false&lt;/span>&lt;span class="p">,&lt;/span>
&lt;span class="p">[&lt;/span>&lt;span class="k">parameter&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">Mandatory&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="nv">$false&lt;/span>&lt;span class="p">)]&lt;/span>
&lt;span class="no">[string]&lt;/span>&lt;span class="nv">$ServerGroup&lt;/span>
&lt;span class="p">)&lt;/span>
&lt;span class="nv">$Config&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="nb">Get-Content&lt;/span> &lt;span class="p">.\&lt;/span>&lt;span class="n">Config&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">json&lt;/span> &lt;span class="p">|&lt;/span> &lt;span class="nb">ConvertFrom-Json&lt;/span>
&lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nv">$DeploySecure&lt;/span> &lt;span class="o">-eq&lt;/span> &lt;span class="nv">$false&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;span class="nv">$Servers&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="nv">$Config&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">servers&lt;/span> &lt;span class="p">|&lt;/span> &lt;span class="nb">Where-Object&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="nv">$_&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nb">type &lt;/span>&lt;span class="o">-ne&lt;/span> &lt;span class="s2">&amp;#34;zone2_servers&amp;#34;&lt;/span>&lt;span class="p">}&lt;/span>
&lt;span class="nb">Write-Host&lt;/span> &lt;span class="s2">&amp;#34;Deploying to non-zone 2 environments.&amp;#34;&lt;/span>
&lt;span class="p">}&lt;/span>
&lt;span class="k">else&lt;/span> &lt;span class="p">{&lt;/span>
&lt;span class="nv">$Servers&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="nv">$Config&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">servers&lt;/span> &lt;span class="p">|&lt;/span> &lt;span class="nb">Where-Object&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="nv">$_&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nb">type &lt;/span>&lt;span class="o">-eq&lt;/span> &lt;span class="s2">&amp;#34;zone2_servers&amp;#34;&lt;/span>&lt;span class="p">}&lt;/span>
&lt;span class="nb">Write-Host&lt;/span> &lt;span class="s2">&amp;#34;Deploying to zone 2 environments.&amp;#34;&lt;/span>
&lt;span class="p">}&lt;/span>
&lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nv">$ServerGroup&lt;/span>&lt;span class="p">){&lt;/span>
&lt;span class="nv">$Servers&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="nv">$Config&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">servers&lt;/span> &lt;span class="p">|&lt;/span> &lt;span class="nb">Where-Object&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="nv">$_&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nb">type &lt;/span>&lt;span class="o">-eq&lt;/span> &lt;span class="nv">$ServerGroup&lt;/span>&lt;span class="p">}&lt;/span>
&lt;span class="nb">Write-Host&lt;/span> &lt;span class="s2">&amp;#34;Deploying servers to $ServerGroup&amp;#34;&lt;/span>
&lt;span class="p">}&lt;/span>
&lt;span class="nv">$ScriptDirectory&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="nv">$Config&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">ScriptDirectory&lt;/span>
&lt;span class="k">If&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">-not&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nb">Test-Path&lt;/span> &lt;span class="nv">$ScriptDirectory&lt;/span>&lt;span class="p">)){&lt;/span>
&lt;span class="nb">Write-Error&lt;/span> &lt;span class="s2">&amp;#34;$ScriptDirectory cannot be accessed!&amp;#34;&lt;/span> &lt;span class="n">-ErrorAction&lt;/span> &lt;span class="n">Stop&lt;/span>
&lt;span class="p">}&lt;/span>
&lt;span class="nv">$scripts&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="nb">Get-ChildItem&lt;/span> &lt;span class="nv">$ScriptDirectory&lt;/span> &lt;span class="p">|&lt;/span> &lt;span class="nb">Where-Object&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="nv">$_&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">Extension&lt;/span> &lt;span class="o">-eq&lt;/span> &lt;span class="s2">&amp;#34;.sql&amp;#34;&lt;/span>&lt;span class="p">}&lt;/span>
&lt;span class="nv">$script_count&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nb">Get-ChildItem&lt;/span> &lt;span class="nv">$ScriptDirectory&lt;/span> &lt;span class="p">|&lt;/span> &lt;span class="nb">Measure-Object&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="n">Count&lt;/span>
&lt;span class="nv">$str_script&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="k">if&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nv">$script_count&lt;/span> &lt;span class="o">-eq&lt;/span> &lt;span class="n">1&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="s1">&amp;#39;script&amp;#39;&lt;/span>&lt;span class="p">}&lt;/span> &lt;span class="k">else&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="s1">&amp;#39;scripts&amp;#39;&lt;/span>&lt;span class="p">}&lt;/span>
&lt;span class="nb">Write-Host&lt;/span> &lt;span class="s2">&amp;#34;Attempting to deploy $script_count $str_script to each server.&amp;#34;&lt;/span>
&lt;span class="k">foreach&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nv">$server&lt;/span> &lt;span class="k">in&lt;/span> &lt;span class="nv">$Servers&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;span class="nb">Write-Host&lt;/span> &lt;span class="s2">&amp;#34;Deploying to&amp;#34;&lt;/span> &lt;span class="nv">$server&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">name&lt;/span>
&lt;span class="k">foreach&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nv">$script&lt;/span> &lt;span class="k">in&lt;/span> &lt;span class="nv">$scripts&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;span class="nb">Write-Host&lt;/span> &lt;span class="s2">&amp;#34;Installing&amp;#34;&lt;/span> &lt;span class="nv">$script&lt;/span>
&lt;span class="nb">Invoke-Sqlcmd&lt;/span> &lt;span class="n">-ServerInstance&lt;/span> &lt;span class="nv">$server&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">Name&lt;/span> &lt;span class="n">-InputFile&lt;/span> &lt;span class="nv">$script&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">FullName&lt;/span> &lt;span class="n">-DisableVariables&lt;/span>
&lt;span class="p">}&lt;/span>
&lt;span class="p">}&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;p>Step 3 done.&lt;/p>
&lt;p>As you can see, it&amp;rsquo;s a pretty basic script. It grabs the list of servers from the config.json file, goes to the &lt;code>ScriptDirectory&lt;/code> to loop through each file, and executes it on each SQL Server. I&amp;rsquo;m sure there are plenty of other ways to do this, but it works and it only needs to be run when there are changes to the maintenance scripts I want to deploy which isn&amp;rsquo;t often.&lt;/p>
&lt;p>The script is flexible for my needs. If I want to execute it for my restricted firewalled area, I just need to run:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-powershell" data-lang="powershell">&lt;span class="p">.\&lt;/span>&lt;span class="nb">Deploy-MaintenanceScripts&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">ps1&lt;/span> &lt;span class="n">-DeploySecure&lt;/span> &lt;span class="nv">$true&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;p>And it will install the scripts on every server in &lt;code>zone2_servers&lt;/code>.&lt;/p>
&lt;p>If I want to deploy to a specific set of servers, I can run:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-powershell" data-lang="powershell">&lt;span class="p">.\&lt;/span>&lt;span class="nb">Deploy-MaintenanceScripts&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">ps1&lt;/span> &lt;span class="n">-DeploySecure&lt;/span> &lt;span class="nv">$false&lt;/span> &lt;span class="p">-&lt;/span>&lt;span class="nv">$ServerGroup&lt;/span> &lt;span class="n">zone1_servers&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;p>And it will deploy to all servers with &lt;code>zone1_servers&lt;/code> as their type.&lt;/p>
&lt;p>I&amp;rsquo;ve uploaded the code to &lt;a href="https://github.com/tarynpratt/misc_sql_scripts/tree/master/DeployMaintenanceScripts">a repo in GitHub&lt;/a> if anyone would find it useful.&lt;/p>
&lt;h3 id="next-steps">Next Steps&lt;/h3>
&lt;p>Since I rarely need to make changes to the scripts, I don&amp;rsquo;t have this currently setup in our CI/CD to deploy automatically when I push changes to GitHub. I could, and might at some point, then I wouldn&amp;rsquo;t have to manually deploy via an automated script.&lt;/p>
&lt;p>While this works for me, what do you use to deploy maintenance scripts to all your SQL Servers? Do you have a homegrown process or a 3rd party tool?&lt;/p></description></item><item><title>My SQL Server Toolbox</title><link>https://tarynpivots.com/post/2020/my-sql-server-toolbox/</link><pubDate>Thu, 15 Oct 2020 04:00:00 +0000</pubDate><guid>https://tarynpivots.com/post/2020/my-sql-server-toolbox/</guid><description>&lt;p>I get asked a lot about the tools I use at Stack Overflow to monitor and work with our SQL Servers. I figured it might be helpful to others to make the list public. I&amp;rsquo;ll also do my best to keep it updated as things change.&lt;/p>
&lt;p>The current list of both free and 3rd party paid tools is below:&lt;/p>
&lt;h2 id="free-tools">Free Tools&lt;/h2>
&lt;ul>
&lt;li>
&lt;p>&lt;a href="https://github.com/opserver/Opserver">Opserver&lt;/a> - Stack Exchange&amp;rsquo;s Monitoring System - I pretty much live in our instances of Opserver because it gives me a one-stop shop to see the health of all of our SQL Servers&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Brent Ozar&amp;rsquo;s &lt;a href="https://www.brentozar.com/responder/">First Reponder Kit&lt;/a>. This is filled with lots of goodies to help you understand what&amp;rsquo;s going on with your server. Currently we install these scripts from the kit:&lt;/p>
&lt;ul>
&lt;li>sp_Blitz.sql&lt;/li>
&lt;li>sp_BlitzCache.sql&lt;/li>
&lt;li>sp_BlitzFirst.sql&lt;/li>
&lt;li>sp_BlitzIndex.sql&lt;/li>
&lt;li>sp_BlitzLock.sql&lt;/li>
&lt;li>sp_BlitzQueryStore.sql&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>Adam Machanic&amp;rsquo;s &lt;a href="http://whoisactive.com/">sp_whoisactive&lt;/a> - this is similar to executing &lt;code>sp_who2&lt;/code> but provides a more detailed look at what is happening on your server&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;a href="https://support.microsoft.com/en-us/help/918992/how-to-transfer-logins-and-passwords-between-instances-of-sql-server">sp_hexadecimal and sp_help_revlogin&lt;/a> to transfer logins between instances of SQL Server&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Ola Hallengren&amp;rsquo;s &lt;a href="https://ola.hallengren.com/">SQL Server Maintenance Solution&lt;/a> for Backups, Integrity Checks, and Index and Statistics Maintenance. We don&amp;rsquo;t use the entire package of scripts, but we currently use:&lt;/p>
&lt;ul>
&lt;li>DatabaseIntegrityCheck.sql&lt;/li>
&lt;li>IndexOptimize.sql&lt;/li>
&lt;li>CommandExecute.sql&lt;/li>
&lt;li>CommandLog.sql&lt;/li>
&lt;li>DatabaseBackup.sql - we use a modified version of this script everywhere&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;a href="https://dbatools.io/">dbatools&lt;/a> PowerShell modules&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Erik Darling&amp;rsquo;s &lt;a href="https://github.com/erikdarlingdata/DarlingData">SQL Server Troubleshooting Scripts&lt;/a> - additional tools to help you identify and troubleshoot problems with your SQL Servers&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Columnstore Maintenance Scripts from a few places:&lt;/p>
&lt;ul>
&lt;li>Joe Obbish&amp;rsquo;s &lt;a href="https://github.com/jobbish-sql/SQL-Server-Multi-Thread">cci_maintenance&lt;/a>&lt;/li>
&lt;li>Nike Neugebauer&amp;rsquo;s &lt;a href="https://github.com/NikoNeugebauer/CISL">Columnstore Indexes Scripts Library&lt;/a>&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;h2 id="3rd-party-tools">3rd Party Tools&lt;/h2>
&lt;ul>
&lt;li>
&lt;p>SentryOne &lt;a href="https://www.sentryone.com/plan-explorer">Plan Explorer&lt;/a> - this is a great tool to review execution plans. I find it far easier to review execution plans in Plan Explorer than in SQL Server Management Studio&lt;/p>
&lt;/li>
&lt;li>
&lt;p>SentryOne &lt;a href="https://www.sentryone.com/products/sentryone-platform/sql-sentry/sql-server-performance-monitoring">SQL Sentry&lt;/a> monitors all of our SQL Servers and provides excellent details on performance and insight into specific queries that might be running during various CPU spikes or other periods of concern&lt;/p>
&lt;/li>
&lt;li>
&lt;p>ApexSQL &lt;a href="https://www.apexsql.com/sql-tools-script.aspx">SQL script generation&lt;/a> - there are times we need to rebase our SQL migrations and this tool is a great way to script out all objects and data&lt;/p>
&lt;/li>
&lt;/ul>
&lt;p>All the SQL scripts get deployed via process that I wrote. I&amp;rsquo;ll share how I do that in my next post.&lt;/p></description></item><item><title>Migrating a 40TB SQL Server Database</title><link>https://tarynpivots.com/post/migrating-40tb-sql-server-database/</link><pubDate>Tue, 28 Jul 2020 06:00:38 +0000</pubDate><guid>https://tarynpivots.com/post/migrating-40tb-sql-server-database/</guid><description>&lt;p>Initially, I wasn’t sure whether to write about this migration project, but when I randomly asked &lt;a href="https://twitter.com/tarynpivots/status/1234966141526671360">if people would be interested&lt;/a>, the response was overwhelming. This was a long, kind of boring, very repetitive, and at times incredibly frustrating project, but I learned a lot, and maybe someone else will learn from this too. There may be far better ways to move this amount of data. In the path I went down, there was a huge amount of juggling that had to take place (I’ll explain that later). Thankfully, I’m a decent juggler.&lt;/p>
&lt;p>While most people are probably interested in what we do with our primary SQL Servers (the servers that run Stack Overflow and the Stack Exchange network of sites), this post isn’t about those servers. This post is about what I called a miscellaneous server in a &lt;a href="https://www.tarynpivots.com/post/how-we-upgraded-stackoverflow-to-sql-server-2017/">previous post&lt;/a> - more specifically, it’s about the migration of our traffic log data.&lt;/p>
&lt;h2 id="background">Background&lt;/h2>
&lt;p>Our HAProxy&lt;!-- raw HTML omitted -->1&lt;!-- raw HTML omitted --> logs aka traffic logs, are currently stored on two SQL Servers, one in New York, and one in Colorado. While we store a minimal summary of the traffic data, we have a lot of it. At the beginning of 2019, we had about 4.5 years of data, totaling about 38TB. The database was initially designed to have a single table for each day. This meant that at the start of 2019 we had approximately 1600 tables in a single database with multiple database files (due to the &lt;a href="https://docs.microsoft.com/en-us/sql/sql-server/maximum-capacity-specifications-for-sql-server?view=sql-server-ver15">16TB size limit of data files&lt;/a>). Each table had a &lt;a href="https://docs.microsoft.com/en-us/sql/relational-databases/indexes/columnstore-indexes-overview?view=sql-server-ver15">clustered columnstore index&lt;/a>, which held anywhere from 100-400 million rows in it.&lt;/p>
&lt;p>I had to move the data from their existing daily tables into a new structure — one table for each month. This needed to be completed on both the NY and CO servers, and the data was stored on spinny drives (Eeek). No matter what, it was going to be painful and slow.&lt;/p>
&lt;p>Oh, and to make things even more complicated, this had to be done with minimal free disk space on the existing servers. The servers had a 44TB spinny drive partition, and we were using 36-38TB of it, depending on the server. This meant I’d be following these steps throughout the process:&lt;/p>
&lt;ul>
&lt;li>migrate data to the new format&lt;/li>
&lt;li>delete old data&lt;/li>
&lt;li>shrink old database files&amp;hellip;yeah, I know, but I had no choice&lt;/li>
&lt;li>repeat over, and over, and over again&lt;/li>
&lt;/ul>
&lt;p>I fully expected the process to take months, and it did — after hitting delays in getting new servers the entire project took about 11 months.&lt;/p>
&lt;!-- raw HTML omitted -->
&lt;p>&lt;!-- raw HTML omitted -->1 &lt;!-- raw HTML omitted -->Note: If you&amp;rsquo;re interested, my colleague, Nick Craver (&lt;a href="https://nickcraver.com/">b&lt;/a> | &lt;a href="https://twitter.com/Nick_Craver/">t&lt;/a>), wrote extensively about our monitoring on his blog and gave an &lt;a href="https://nickcraver.com/blog/2018/11/29/stack-overflow-how-we-do-monitoring/#logs-haproxy">overview of our HAProxy usage&lt;/a>.&lt;/p>
&lt;h3 id="things-to-remember">Things to Remember&lt;/h3>
&lt;p>Before I get started there are a few things I need to mention which impacted the project:&lt;/p>
&lt;ul>
&lt;li>I work remote 100% of the time, so I would not be able to run any of the processes from my local computer. Everything needed to be executed on a machine that was constantly connected to the network, so there wouldn’t be any VPN failures.&lt;/li>
&lt;li>I needed to run this on two separate machines. I was doing this on SQL servers in two different datacenters, I needed a machine in the same location, so I wouldn’t have to deal with slowness over the network.&lt;/li>
&lt;li>We have jump boxes that we use for various things at Stack Overflow, and we have one in NY and in CO, which were perfect to run the migration&lt;/li>
&lt;li>The old database was still being used in a live production environment, meaning that as I was moving data, we were constantly adding a new table daily. In other words, I had a moving target&lt;/li>
&lt;li>The databases are in &lt;code>simple recovery&lt;/code> so we don&amp;rsquo;t have to deal with transaction logs. Our backup is the original source log files and the pair of SQL Servers - NY and CO - are copies of each other.&lt;/li>
&lt;/ul>
&lt;p>The bullets above were pain points throughout the project. Being disconnected from the VPN meant I’d have to reconnect to monitor the migration progress. Getting booted from a jumpbox, meant that whatever process was running would need to be kicked off again, after some clean-up. Since we were still inserting new data I was chasing the target, the longer it took, the more data I&amp;rsquo;d be moving in the end.&lt;/p>
&lt;h3 id="why-oh-why-would-you-do-this">Why, oh why would you do this?&lt;/h3>
&lt;p>I like to be miserable. Kidding.&lt;/p>
&lt;p>There were lots of reasons this needed to be done, one being tech debt. We realized that the original daily table structure wasn&amp;rsquo;t ideal. If we needed to query something over several days or months, it was terrible — lots of &lt;code>UNION ALL&lt;/code>s or loops to crawl through days or even months at a time was slow.&lt;/p>
&lt;h3 id="why-not-delete-or-purge-some-of-the-data">Why not delete or purge some of the data?&lt;/h3>
&lt;p>As I mentioned, we were dealing with very little free disk space on both servers. This data is primarily used by the developers for investigating issues and by the data team for analysis. We already had purged some data, but for the data team - more data is better.&lt;/p>
&lt;p>Instead of purging, the plan was to eventually get new hardware to replace them, but we weren’t exactly sure when that would happen. The goal was to migrate the data to the new format, then when we got new servers it would be as simple as moving the drives to get the new database in place (or so we thought).&lt;/p>
&lt;h2 id="so-it-begins">So It Begins&lt;/h2>
&lt;p>Each server had the following drives and approximate free space:&lt;/p>
&lt;ul>
&lt;li>a 230GB C: drive for Windows - obviously we wouldn&amp;rsquo;t put data files here&lt;/li>
&lt;li>a 3.64TB NVMe D: drive - containing tempdb, one data file, and the log file for the existing HAProxyLogs database - it was approximately 85% full&lt;/li>
&lt;li>a 44TB E: drive filled with spinny disks - with the remaining 3 data files for the HAProxyLogs database - this was anywhere from 85-90% full&lt;/li>
&lt;/ul>
&lt;p>There was not a lot of room to move terabytes of data to a new database on the same server.&lt;/p>
&lt;p>Since new servers were on down the road, I asked about the possibility of getting more disk space. Having more space would give me some breathing room to not be so crunched during the process. After some research, it was decided that we could fit a couple of additional NVMe SSDs into both servers. We only had PCIe slots available in the server, so we ended up using U.2 NVMe drives on PCIe adapters to get us some space. The SSDs gave us a brand spanking new F: drive that was &lt;strong>14TB&lt;/strong> of free space. While I couldn’t move everything to the new drive, it gave me plenty of space to work with, at least, for a little bit.&lt;/p>
&lt;p>Now that we had free space to use, it was time to set up the new database and begin the migration. I wrote the script to create the new database:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-sql" data-lang="sql">&lt;span class="k">CREATE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">DATABASE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">TrafficLogs&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">CONTAINMENT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NONE&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">ON&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">PRIMARY&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">NAME&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">N&lt;/span>&lt;span class="s1">&amp;#39;TrafficLogs_Current&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">FILENAME&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">N&lt;/span>&lt;span class="s1">&amp;#39;F:\Data\TrafficLogs_Current.mdf&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">SIZE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">102400000&lt;/span>&lt;span class="n">KB&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">FILEGROWTH&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">5120000&lt;/span>&lt;span class="n">KB&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="p">),&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="n">FILEGROUP&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">TrafficLogs_Archive&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">NAME&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">N&lt;/span>&lt;span class="s1">&amp;#39;TrafficLogs_Archive1&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">FILENAME&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">N&lt;/span>&lt;span class="s1">&amp;#39;E:\Data\TrafficLogs_Archive1.ndf&amp;#39;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">SIZE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">102400000&lt;/span>&lt;span class="n">KB&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">FILEGROWTH&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">5120000&lt;/span>&lt;span class="n">KB&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="p">),&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">NAME&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">N&lt;/span>&lt;span class="s1">&amp;#39;TrafficLogs_Archive2&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">FILENAME&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">N&lt;/span>&lt;span class="s1">&amp;#39;E:\Data\TrafficLogs_Archive2.ndf&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">SIZE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">102400000&lt;/span>&lt;span class="n">KB&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">FILEGROWTH&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">5120000&lt;/span>&lt;span class="n">KB&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="p">),&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">NAME&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">N&lt;/span>&lt;span class="s1">&amp;#39;TrafficLogs_Archive3&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">FILENAME&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">N&lt;/span>&lt;span class="s1">&amp;#39;E:\Data\TrafficLogs_Archive3.ndf&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">SIZE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">102400000&lt;/span>&lt;span class="n">KB&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">FILEGROWTH&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">5120000&lt;/span>&lt;span class="n">KB&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="n">LOG&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">ON&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">NAME&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">N&lt;/span>&lt;span class="s1">&amp;#39;TrafficLogs_log&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">FILENAME&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">N&lt;/span>&lt;span class="s1">&amp;#39;F:\Data\TrafficLogs_log.ldf&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">SIZE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">5120000&lt;/span>&lt;span class="n">KB&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">MAXSIZE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">2048&lt;/span>&lt;span class="n">GB&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">FILEGROWTH&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">102400000&lt;/span>&lt;span class="n">KB&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="p">);&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;p>The database was going to have all of the old historical tables (i.e. the 40TBs) stored on the &lt;code>TrafficLogs_Archive&lt;/code> filegroup, and we&amp;rsquo;d use the &lt;code>PRIMARY&lt;/code> and &lt;code>TrafficLogs_Current&lt;/code> for newer data being added.&lt;/p>
&lt;p>You&amp;rsquo;ll notice that the &lt;code>TrafficLogs_Archive&lt;/code> filegroup went on our already very full E: drive, instead of the new F: drive - more on that mistake later.&lt;/p>
&lt;h2 id="moving-all-the-data">Moving All The Data&lt;/h2>
&lt;p>We had a database, so it was time to start the migration. To be clear, I was actually taking over a process that was started, stopped, and then punted from several years earlier. The project was on the backlog of things to be done long before I became the DBA at Stack Overflow. Back then, everyone realized what a time consuming project this would be, and since we didn’t have the resources to devote to it, the can continuously got kicked down the road. As a result, more and more tables were added making the overall task a lot larger.&lt;/p>
&lt;p>Since this had been a previously abandoned project, there were already some scripts written, which meant I wasn’t totally starting from scratch. I was handed a couple of scripts to:&lt;/p>
&lt;ol>
&lt;li>
&lt;p>Create all of the new monthly tables&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;span class="lnt">36
&lt;/span>&lt;span class="lnt">37
&lt;/span>&lt;span class="lnt">38
&lt;/span>&lt;span class="lnt">39
&lt;/span>&lt;span class="lnt">40
&lt;/span>&lt;span class="lnt">41
&lt;/span>&lt;span class="lnt">42
&lt;/span>&lt;span class="lnt">43
&lt;/span>&lt;span class="lnt">44
&lt;/span>&lt;span class="lnt">45
&lt;/span>&lt;span class="lnt">46
&lt;/span>&lt;span class="lnt">47
&lt;/span>&lt;span class="lnt">48
&lt;/span>&lt;span class="lnt">49
&lt;/span>&lt;span class="lnt">50
&lt;/span>&lt;span class="lnt">51
&lt;/span>&lt;span class="lnt">52
&lt;/span>&lt;span class="lnt">53
&lt;/span>&lt;span class="lnt">54
&lt;/span>&lt;span class="lnt">55
&lt;/span>&lt;span class="lnt">56
&lt;/span>&lt;span class="lnt">57
&lt;/span>&lt;span class="lnt">58
&lt;/span>&lt;span class="lnt">59
&lt;/span>&lt;span class="lnt">60
&lt;/span>&lt;span class="lnt">61
&lt;/span>&lt;span class="lnt">62
&lt;/span>&lt;span class="lnt">63
&lt;/span>&lt;span class="lnt">64
&lt;/span>&lt;span class="lnt">65
&lt;/span>&lt;span class="lnt">66
&lt;/span>&lt;span class="lnt">67
&lt;/span>&lt;span class="lnt">68
&lt;/span>&lt;span class="lnt">69
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-sql" data-lang="sql">&lt;span class="c1">-- written by Nick Craver
&lt;/span>&lt;span class="c1">&lt;/span>&lt;span class="k">Declare&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="k">month&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">datetime&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;2015-08-01&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">Declare&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">endmonth&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">datetime&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;2021-01-01&amp;#39;&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="n">WHILE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="k">month&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">endmonth&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">BEGIN&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">Set&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">NoCount&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">On&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">Declare&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">prevMonth&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">datetime&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">DateAdd&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">Month&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="k">month&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">Declare&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">nextMonth&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">datetime&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">DateAdd&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">Month&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="k">month&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">Declare&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">monthTable&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">sysname&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;Logs_&amp;#39;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">Cast&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">DatePart&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">Year&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="k">month&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">as&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">varchar&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;_&amp;#39;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">Right&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;0&amp;#39;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">Cast&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">DatePart&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">Month&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="k">month&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">as&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">varchar&lt;/span>&lt;span class="p">),&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">Begin&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Try&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">If&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Object_Id&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">monthTable&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;U&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">Is&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">Not&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">Null&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">Begin&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">Declare&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">error&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">nvarchar&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">400&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;Month &amp;#39;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">Convert&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nb">varchar&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">10&lt;/span>&lt;span class="p">),&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="k">month&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">120&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39; has already been moved to &amp;#39;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">monthTable&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;, aborting.&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Throw&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">501337&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">error&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">Return&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">End&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">-- Table Creation
&lt;/span>&lt;span class="c1">&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">Declare&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">tableTemplate&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">nvarchar&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">4000&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;
&lt;/span>&lt;span class="s1"> Create Table {Name} (
&lt;/span>&lt;span class="s1"> [CreationDate] datetime Not Null,
&lt;/span>&lt;span class="s1"> &amp;lt;insert all the columns&amp;gt;,
&lt;/span>&lt;span class="s1"> Constraint CK_{Name}_Low Check (CreationDate &amp;gt;= &amp;#39;&amp;#39;{LowerDate}&amp;#39;&amp;#39;),
&lt;/span>&lt;span class="s1"> Constraint CK_{Name}_High Check (CreationDate &amp;lt; &amp;#39;&amp;#39;{UpperDate}&amp;#39;&amp;#39;)
&lt;/span>&lt;span class="s1"> ) On {Filegroup};
&lt;/span>&lt;span class="s1">
&lt;/span>&lt;span class="s1"> Create Clustered Columnstore Index CCI_{Name}
&lt;/span>&lt;span class="s1"> On {Name} With (Data_Compression = {Compression}) On {Filegroup};&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">-- Constraints exist for metadata swap
&lt;/span>&lt;span class="c1">&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">Declare&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="k">table&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">nvarchar&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">4000&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">tableTemplate&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">Set&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="k">table&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">Replace&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="k">table&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;{Name}&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">monthTable&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">Set&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="k">table&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">Replace&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="k">table&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;{Filegroup}&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;Logs_Archive&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">Set&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="k">table&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">Replace&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="k">table&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;{LowerDate}&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">Convert&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nb">varchar&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">20&lt;/span>&lt;span class="p">),&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="k">month&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">120&lt;/span>&lt;span class="p">));&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">Set&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="k">table&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">Replace&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="k">table&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;{UpperDate}&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">Convert&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nb">varchar&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">20&lt;/span>&lt;span class="p">),&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">nextMonth&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">120&lt;/span>&lt;span class="p">));&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">Set&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="k">table&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">Replace&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="k">table&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;{Compression}&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;ColumnStore_Archive&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Print&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="k">table&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">Exec&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">sp_executesql&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="k">table&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">Declare&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">moveSql&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">nvarchar&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">4000&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;Create Clustered Columnstore Index CCI_{Name}
&lt;/span>&lt;span class="s1"> On {Name} With (Drop_Existing = On, Data_Compression = Columnstore_Archive) On Logs_Archive;&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">Set&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">moveSql&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">Replace&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">moveSql&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;{Name}&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">monthTable&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Print&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">moveSql&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">Exec&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">sp_executesql&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">moveSql&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">End&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Try&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">Begin&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Catch&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">Select&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Error_Number&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ErrorNumber&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Error_Severity&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ErrorSeverity&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Error_State&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ErrorState&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Error_Procedure&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ErrorProcedure&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Error_Line&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ErrorLine&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Error_Message&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ErrorMessage&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Throw&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">End&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Catch&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">set&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="k">month&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">dateadd&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">month&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="k">month&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">END&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">GO&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;/li>
&lt;li>
&lt;p>A LINQPad script to loop through each day, starting with the earliest, and insert the data into the new table&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt"> 10
&lt;/span>&lt;span class="lnt"> 11
&lt;/span>&lt;span class="lnt"> 12
&lt;/span>&lt;span class="lnt"> 13
&lt;/span>&lt;span class="lnt"> 14
&lt;/span>&lt;span class="lnt"> 15
&lt;/span>&lt;span class="lnt"> 16
&lt;/span>&lt;span class="lnt"> 17
&lt;/span>&lt;span class="lnt"> 18
&lt;/span>&lt;span class="lnt"> 19
&lt;/span>&lt;span class="lnt"> 20
&lt;/span>&lt;span class="lnt"> 21
&lt;/span>&lt;span class="lnt"> 22
&lt;/span>&lt;span class="lnt"> 23
&lt;/span>&lt;span class="lnt"> 24
&lt;/span>&lt;span class="lnt"> 25
&lt;/span>&lt;span class="lnt"> 26
&lt;/span>&lt;span class="lnt"> 27
&lt;/span>&lt;span class="lnt"> 28
&lt;/span>&lt;span class="lnt"> 29
&lt;/span>&lt;span class="lnt"> 30
&lt;/span>&lt;span class="lnt"> 31
&lt;/span>&lt;span class="lnt"> 32
&lt;/span>&lt;span class="lnt"> 33
&lt;/span>&lt;span class="lnt"> 34
&lt;/span>&lt;span class="lnt"> 35
&lt;/span>&lt;span class="lnt"> 36
&lt;/span>&lt;span class="lnt"> 37
&lt;/span>&lt;span class="lnt"> 38
&lt;/span>&lt;span class="lnt"> 39
&lt;/span>&lt;span class="lnt"> 40
&lt;/span>&lt;span class="lnt"> 41
&lt;/span>&lt;span class="lnt"> 42
&lt;/span>&lt;span class="lnt"> 43
&lt;/span>&lt;span class="lnt"> 44
&lt;/span>&lt;span class="lnt"> 45
&lt;/span>&lt;span class="lnt"> 46
&lt;/span>&lt;span class="lnt"> 47
&lt;/span>&lt;span class="lnt"> 48
&lt;/span>&lt;span class="lnt"> 49
&lt;/span>&lt;span class="lnt"> 50
&lt;/span>&lt;span class="lnt"> 51
&lt;/span>&lt;span class="lnt"> 52
&lt;/span>&lt;span class="lnt"> 53
&lt;/span>&lt;span class="lnt"> 54
&lt;/span>&lt;span class="lnt"> 55
&lt;/span>&lt;span class="lnt"> 56
&lt;/span>&lt;span class="lnt"> 57
&lt;/span>&lt;span class="lnt"> 58
&lt;/span>&lt;span class="lnt"> 59
&lt;/span>&lt;span class="lnt"> 60
&lt;/span>&lt;span class="lnt"> 61
&lt;/span>&lt;span class="lnt"> 62
&lt;/span>&lt;span class="lnt"> 63
&lt;/span>&lt;span class="lnt"> 64
&lt;/span>&lt;span class="lnt"> 65
&lt;/span>&lt;span class="lnt"> 66
&lt;/span>&lt;span class="lnt"> 67
&lt;/span>&lt;span class="lnt"> 68
&lt;/span>&lt;span class="lnt"> 69
&lt;/span>&lt;span class="lnt"> 70
&lt;/span>&lt;span class="lnt"> 71
&lt;/span>&lt;span class="lnt"> 72
&lt;/span>&lt;span class="lnt"> 73
&lt;/span>&lt;span class="lnt"> 74
&lt;/span>&lt;span class="lnt"> 75
&lt;/span>&lt;span class="lnt"> 76
&lt;/span>&lt;span class="lnt"> 77
&lt;/span>&lt;span class="lnt"> 78
&lt;/span>&lt;span class="lnt"> 79
&lt;/span>&lt;span class="lnt"> 80
&lt;/span>&lt;span class="lnt"> 81
&lt;/span>&lt;span class="lnt"> 82
&lt;/span>&lt;span class="lnt"> 83
&lt;/span>&lt;span class="lnt"> 84
&lt;/span>&lt;span class="lnt"> 85
&lt;/span>&lt;span class="lnt"> 86
&lt;/span>&lt;span class="lnt"> 87
&lt;/span>&lt;span class="lnt"> 88
&lt;/span>&lt;span class="lnt"> 89
&lt;/span>&lt;span class="lnt"> 90
&lt;/span>&lt;span class="lnt"> 91
&lt;/span>&lt;span class="lnt"> 92
&lt;/span>&lt;span class="lnt"> 93
&lt;/span>&lt;span class="lnt"> 94
&lt;/span>&lt;span class="lnt"> 95
&lt;/span>&lt;span class="lnt"> 96
&lt;/span>&lt;span class="lnt"> 97
&lt;/span>&lt;span class="lnt"> 98
&lt;/span>&lt;span class="lnt"> 99
&lt;/span>&lt;span class="lnt">100
&lt;/span>&lt;span class="lnt">101
&lt;/span>&lt;span class="lnt">102
&lt;/span>&lt;span class="lnt">103
&lt;/span>&lt;span class="lnt">104
&lt;/span>&lt;span class="lnt">105
&lt;/span>&lt;span class="lnt">106
&lt;/span>&lt;span class="lnt">107
&lt;/span>&lt;span class="lnt">108
&lt;/span>&lt;span class="lnt">109
&lt;/span>&lt;span class="lnt">110
&lt;/span>&lt;span class="lnt">111
&lt;/span>&lt;span class="lnt">112
&lt;/span>&lt;span class="lnt">113
&lt;/span>&lt;span class="lnt">114
&lt;/span>&lt;span class="lnt">115
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cs" data-lang="cs">&lt;span class="p">--&lt;/span> &lt;span class="n">written&lt;/span> &lt;span class="k">by&lt;/span> &lt;span class="n">Nick&lt;/span> &lt;span class="n">Craver&lt;/span>
&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="n">Query&lt;/span> &lt;span class="n">Kind&lt;/span>&lt;span class="p">=&lt;/span>&lt;span class="s">&amp;#34;Program&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="n">NuGetReference&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>&lt;span class="n">Dapper&lt;/span>&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="n">NuGetReference&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="n">Namespace&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>&lt;span class="n">Dapper&lt;/span>&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="n">Namespace&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="n">Query&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;span class="k">void&lt;/span> &lt;span class="n">Main&lt;/span>&lt;span class="p">()&lt;/span>
&lt;span class="p">{&lt;/span>
&lt;span class="n">MoveDate&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">new&lt;/span> &lt;span class="n">DateTime&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="m">2015&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="m">08&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="m">1&lt;/span>&lt;span class="p">));&lt;/span>
&lt;span class="n">DateTime&lt;/span> &lt;span class="n">date&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="n">DateTime&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="m">2015&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="m">08&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="m">1&lt;/span>&lt;span class="p">);&lt;/span>
&lt;span class="k">while&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">date&lt;/span> &lt;span class="p">&amp;lt;&lt;/span> &lt;span class="n">DateTime&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">UtcNow&lt;/span>&lt;span class="p">)&lt;/span>
&lt;span class="p">{&lt;/span>
&lt;span class="n">MoveDate&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">date&lt;/span>&lt;span class="p">);&lt;/span>
&lt;span class="n">date&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">date&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">AddDays&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="m">1&lt;/span>&lt;span class="p">);&lt;/span>
&lt;span class="p">}&lt;/span>
&lt;span class="p">}&lt;/span>
&lt;span class="k">static&lt;/span> &lt;span class="k">readonly&lt;/span> &lt;span class="n">List&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="kt">string&lt;/span>&lt;span class="p">&amp;gt;&lt;/span> &lt;span class="n">cols&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="n">List&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="kt">string&lt;/span>&lt;span class="p">&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="s">&amp;#34;&amp;lt;col list&amp;gt;&amp;#34;&lt;/span> &lt;span class="p">};&lt;/span>
&lt;span class="k">public&lt;/span> &lt;span class="k">void&lt;/span> &lt;span class="n">MoveDate&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">DateTime&lt;/span> &lt;span class="n">date&lt;/span>&lt;span class="p">)&lt;/span>
&lt;span class="p">{&lt;/span>
&lt;span class="kt">var&lt;/span> &lt;span class="n">tableName&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">GetTableName&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">date&lt;/span>&lt;span class="p">);&lt;/span>
&lt;span class="kt">var&lt;/span> &lt;span class="n">destTable&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">GetDestTableName&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">date&lt;/span>&lt;span class="p">);&lt;/span>
&lt;span class="s">$&amp;#34;Attempting to migrate {date:yyyy-MM-dd} f
&lt;/span>&lt;span class="s"> from {tableName} to {destTable}&amp;#34;&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">Dump&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">$&amp;#34;{date:yyyy-MM-dd}&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;span class="k">using&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="kt">var&lt;/span> &lt;span class="n">conn&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">GetConn&lt;/span>&lt;span class="p">())&lt;/span>
&lt;span class="p">{&lt;/span>
&lt;span class="kt">int&lt;/span> &lt;span class="n">rowCount&lt;/span>&lt;span class="p">;&lt;/span>
&lt;span class="k">try&lt;/span>
&lt;span class="p">{&lt;/span>
&lt;span class="n">rowCount&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">conn&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">QuerySingle&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="kt">int&lt;/span>&lt;span class="p">&amp;gt;(&lt;/span>&lt;span class="s">$&amp;#34;Select Count(*)
&lt;/span>&lt;span class="s"> From HAProxyLogs.dbo.{tableName};&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;span class="p">}&lt;/span>
&lt;span class="k">catch&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">SqlException&lt;/span> &lt;span class="n">e&lt;/span>&lt;span class="p">)&lt;/span>
&lt;span class="p">{&lt;/span>
&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34; Error migrating: &amp;#34;&lt;/span> &lt;span class="p">+&lt;/span> &lt;span class="n">e&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">Message&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="n">Dump&lt;/span>&lt;span class="p">();&lt;/span>
&lt;span class="k">return&lt;/span>&lt;span class="p">;&lt;/span>
&lt;span class="p">}&lt;/span>
&lt;span class="s">$&amp;#34; Summary for {date:yyyy-MM-dd}&amp;#34;&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">Dump&lt;/span>&lt;span class="p">();&lt;/span>
&lt;span class="s">$&amp;#34; {rowCount:n0} row(s) in {tableName}&amp;#34;&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">Dump&lt;/span>&lt;span class="p">();&lt;/span>
&lt;span class="kt">var&lt;/span> &lt;span class="n">pb&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="n">Util&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">ProgressBar&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">$&amp;#34;{tableName} (0/{rowCount})&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;span class="n">pb&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">Dump&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">tableName&lt;/span> &lt;span class="p">+&lt;/span> &lt;span class="s">&amp;#34; copy&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;span class="n">Func&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="kt">int&lt;/span>&lt;span class="p">&amp;gt;&lt;/span> &lt;span class="n">GetDestRowCount&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">()&lt;/span>
&lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="n">conn&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">QuerySingle&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="kt">int&lt;/span>&lt;span class="p">&amp;gt;(&lt;/span>&lt;span class="s">$&amp;#34;Select Count(*)
&lt;/span>&lt;span class="s"> From {destTable}
&lt;/span>&lt;span class="s"> Where CreationDate &amp;gt;= @date
&lt;/span>&lt;span class="s"> And CreationDate &amp;lt; @date + 1;&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="n">date&lt;/span> &lt;span class="p">});&lt;/span>
&lt;span class="n">Action&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="kt">long&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kt">int&lt;/span>&lt;span class="p">&amp;gt;&lt;/span> &lt;span class="n">UpdatePB&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">copied&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">total&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span>
&lt;span class="p">{&lt;/span>
&lt;span class="n">pb&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">Fraction&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="kt">double&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="n">copied&lt;/span> &lt;span class="p">/&lt;/span> &lt;span class="n">total&lt;/span>&lt;span class="p">;&lt;/span>
&lt;span class="n">pb&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">Caption&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s">$&amp;#34;{tableName} ({copied}/{total})&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;span class="p">};&lt;/span>
&lt;span class="kt">var&lt;/span> &lt;span class="n">destRowCount&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">GetDestRowCount&lt;/span>&lt;span class="p">();&lt;/span>
&lt;span class="s">$&amp;#34; {destRowCount:n0} row(s) in {destTable}&amp;#34;&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">Dump&lt;/span>&lt;span class="p">();&lt;/span>
&lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">destRowCount&lt;/span> &lt;span class="p">&amp;gt;&lt;/span> &lt;span class="m">0&lt;/span>&lt;span class="p">)&lt;/span>
&lt;span class="p">{&lt;/span>
&lt;span class="s">$&amp;#34;Rows found in destiation table - aborting!&amp;#34;&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">Dump&lt;/span>&lt;span class="p">();&lt;/span>
&lt;span class="k">return&lt;/span>&lt;span class="p">;&lt;/span>
&lt;span class="p">}&lt;/span>
&lt;span class="kt">var&lt;/span> &lt;span class="n">reader&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">conn&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">ExecuteReader&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">$&amp;#34;Select {string.Join(&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="s">&amp;#34;, cols)}
&lt;/span>&lt;span class="s">&lt;/span> &lt;span class="n">From&lt;/span> &lt;span class="n">HAProxyLogs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">dbo&lt;/span>&lt;span class="p">.{&lt;/span>&lt;span class="n">tableName&lt;/span>&lt;span class="p">};&lt;/span>&lt;span class="s">&amp;#34;);
&lt;/span>&lt;span class="s">&lt;/span>
&lt;span class="k">using&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">SqlConnection&lt;/span> &lt;span class="n">dest&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">GetConn&lt;/span>&lt;span class="p">())&lt;/span>
&lt;span class="p">{&lt;/span>
&lt;span class="n">dest&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">Open&lt;/span>&lt;span class="p">();&lt;/span>
&lt;span class="k">using&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">SqlBulkCopy&lt;/span> &lt;span class="n">bc&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="n">SqlBulkCopy&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">dest&lt;/span>&lt;span class="p">))&lt;/span>
&lt;span class="p">{&lt;/span>
&lt;span class="n">bc&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">BulkCopyTimeout&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="m">5&lt;/span>&lt;span class="p">*&lt;/span>&lt;span class="m">60&lt;/span>&lt;span class="p">;&lt;/span>
&lt;span class="n">bc&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">BatchSize&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="m">1048576&lt;/span>&lt;span class="p">;&lt;/span>
&lt;span class="n">bc&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">NotifyAfter&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="m">100000&lt;/span>&lt;span class="p">;&lt;/span>
&lt;span class="n">bc&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">DestinationTableName&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">GetDestTableName&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">date&lt;/span>&lt;span class="p">);&lt;/span>
&lt;span class="n">bc&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">SqlRowsCopied&lt;/span> &lt;span class="p">+=&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">e&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">s&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="n">UpdatePB&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">s&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">RowsCopied&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">rowCount&lt;/span>&lt;span class="p">);&lt;/span>
&lt;span class="k">foreach&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="kt">var&lt;/span> &lt;span class="n">c&lt;/span> &lt;span class="k">in&lt;/span> &lt;span class="n">cols&lt;/span>&lt;span class="p">)&lt;/span>
&lt;span class="p">{&lt;/span>
&lt;span class="n">bc&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">ColumnMappings&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">Add&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">c&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">c&lt;/span>&lt;span class="p">);&lt;/span>
&lt;span class="p">}&lt;/span>
&lt;span class="k">try&lt;/span>
&lt;span class="p">{&lt;/span>
&lt;span class="n">bc&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">WriteToServer&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">reader&lt;/span>&lt;span class="p">);&lt;/span>
&lt;span class="p">}&lt;/span>
&lt;span class="k">catch&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">Exception&lt;/span> &lt;span class="n">ex&lt;/span>&lt;span class="p">)&lt;/span>
&lt;span class="p">{&lt;/span>
&lt;span class="n">Console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">WriteLine&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">ex&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">Message&lt;/span>&lt;span class="p">);&lt;/span>
&lt;span class="p">}&lt;/span>
&lt;span class="k">finally&lt;/span>
&lt;span class="p">{&lt;/span>
&lt;span class="n">reader&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">Close&lt;/span>&lt;span class="p">();&lt;/span>
&lt;span class="p">}&lt;/span>
&lt;span class="p">}&lt;/span>
&lt;span class="kt">var&lt;/span> &lt;span class="n">destFinalCount&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">GetDestRowCount&lt;/span>&lt;span class="p">();&lt;/span>
&lt;span class="p">}&lt;/span>
&lt;span class="p">}&lt;/span>
&lt;span class="p">}&lt;/span>
&lt;span class="k">public&lt;/span> &lt;span class="n">SqlConnection&lt;/span> &lt;span class="n">GetConn&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span>
&lt;span class="k">new&lt;/span> &lt;span class="n">SqlConnection&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">new&lt;/span> &lt;span class="n">SqlConnectionStringBuilder&lt;/span>
&lt;span class="p">{&lt;/span>
&lt;span class="n">DataSource&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s">&amp;#34;servername&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;span class="n">InitialCatalog&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s">&amp;#34;TrafficLogs&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;span class="n">IntegratedSecurity&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="k">true&lt;/span>
&lt;span class="p">}.&lt;/span>&lt;span class="n">ToString&lt;/span>&lt;span class="p">());&lt;/span>
&lt;span class="k">public&lt;/span> &lt;span class="kt">string&lt;/span> &lt;span class="n">GetTableName&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">DateTime&lt;/span> &lt;span class="n">dt&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="s">$&amp;#34;Log_{dt:yyyy_MM_dd}&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;span class="k">public&lt;/span> &lt;span class="kt">string&lt;/span> &lt;span class="n">GetDestTableName&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">DateTime&lt;/span> &lt;span class="n">dt&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="s">$&amp;#34;HAProxyLogs_{dt:yyyy_MM}&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;/li>
&lt;/ol>
&lt;p>Ideally, this was going to be as simple as kicking off the LINQPad script, and letting it run in the background looping through all the tables. I thought I wouldn’t have to worry about it, but things are never really that easy.&lt;/p>
&lt;p>I started the migration in Colorado, on January 14, 2019 (yes, I know the exact date). Pulling millions of rows of data from spinny drives, and then inserting it back into new tables on those same spinny drives was slow. By day 2, &lt;a href="https://twitter.com/tarynpivots/status/1085225095168065536">knew this was going to be painful&lt;/a>. My original plan was to run the migration in Colorado first, then in New York. However, once I saw how slow each table was taking, I decided to run them simultaneously. About a week later, I kicked off the New York data migration.&lt;/p>
&lt;h2 id="data-migration-issues">Data Migration Issues&lt;/h2>
&lt;p>Early on, I hit various issues that I needed to work through. Some of these were easy fixes, others not so much.&lt;/p>
&lt;h3 id="script-timeouts">Script Timeouts&lt;/h3>
&lt;p>The first issue was with the C# script. The script periodically timed-out when querying the database. After adjusting the settings, I crossed my fingers and hoped it would continue to work, which it did, for the most part.&lt;/p>
&lt;h3 id="random-script-errors">Random Script Errors&lt;/h3>
&lt;p>Next, I ran into an issue, where the script would continue to the next table, aka day, if there was an error. That was terrible because we wanted the data to be &lt;a href="https://docs.microsoft.com/en-us/sql/relational-databases/indexes/columnstore-indexes-query-performance?view=sql-server-ver15">inserted in date order&lt;/a>, for the clustered columnstore index. If the script errored and moved on to the next day, cleanup would need to be done for the day that didn’t finish. At that point, I’d have to start the process over for that particular day.&lt;/p>
&lt;p>You might be wondering what I mean by &amp;ldquo;cleanup&amp;rdquo;.&lt;/p>
&lt;p>Let me take a quick step back and explain the old process. The original table structure aka the daily tables were rowstore with a generic identity (ID) column to make each row unique. After 14 days, we would drop the primary key from the table, and add a clustered columnstore index. Even though the conversion to a clustered columnstore occurred, the ID column would remain.&lt;/p>
&lt;p>In the new table, we dropped the ID column. Dropping this column meant, trying to figure out the uniqueness of the rows inserted before a failure would be incredibly difficult. It was far easier, in the event of a failure, to just delete everything from the table for that day, and everything after that day (if it moved to another table), and restart the process.&lt;/p>
&lt;h3 id="data-validation">Data Validation&lt;/h3>
&lt;p>I realized I needed to validate that the total number of rows inserted into the new table matched the total number of rows from the old daily table. Sounds easy, right? Well, not necessarily.&lt;/p>
&lt;p>Sure, the original tables were per day, but there were cases where instead of containing data from &lt;code>date 00:00-23:59&lt;/code> it would potentially include some rows from &lt;code>date+1&lt;/code>. This was a known issue (due to latency of logs arriving from HAProxy), and I was told the totals didn’t have to match perfectly - if we lost some rows, it was fine. The thing is, when you’re responsible for moving data from one system to another, you really want things to match&amp;hellip;perfectly.&lt;/p>
&lt;p>Because of the stragglers from a different day, I couldn’t just query the total rows from the old table and verify that the new table contained the same count. I had to come up with a way to query the count by day. I did this by adding a column to the new table called &lt;code>OriginalLogTable&lt;/code> and then populating it with the name of the old table, for example &lt;code>Log_2019_07_01&lt;/code>. Having this column allowed me to compare the count of rows in the old table, with the count in the new table by grouping by the &lt;code>OriginalLogTable&lt;/code>. This solved my problem because when I finished a month, I could easily verify that each day matched between the old and new tables, using the script below:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt"> 10
&lt;/span>&lt;span class="lnt"> 11
&lt;/span>&lt;span class="lnt"> 12
&lt;/span>&lt;span class="lnt"> 13
&lt;/span>&lt;span class="lnt"> 14
&lt;/span>&lt;span class="lnt"> 15
&lt;/span>&lt;span class="lnt"> 16
&lt;/span>&lt;span class="lnt"> 17
&lt;/span>&lt;span class="lnt"> 18
&lt;/span>&lt;span class="lnt"> 19
&lt;/span>&lt;span class="lnt"> 20
&lt;/span>&lt;span class="lnt"> 21
&lt;/span>&lt;span class="lnt"> 22
&lt;/span>&lt;span class="lnt"> 23
&lt;/span>&lt;span class="lnt"> 24
&lt;/span>&lt;span class="lnt"> 25
&lt;/span>&lt;span class="lnt"> 26
&lt;/span>&lt;span class="lnt"> 27
&lt;/span>&lt;span class="lnt"> 28
&lt;/span>&lt;span class="lnt"> 29
&lt;/span>&lt;span class="lnt"> 30
&lt;/span>&lt;span class="lnt"> 31
&lt;/span>&lt;span class="lnt"> 32
&lt;/span>&lt;span class="lnt"> 33
&lt;/span>&lt;span class="lnt"> 34
&lt;/span>&lt;span class="lnt"> 35
&lt;/span>&lt;span class="lnt"> 36
&lt;/span>&lt;span class="lnt"> 37
&lt;/span>&lt;span class="lnt"> 38
&lt;/span>&lt;span class="lnt"> 39
&lt;/span>&lt;span class="lnt"> 40
&lt;/span>&lt;span class="lnt"> 41
&lt;/span>&lt;span class="lnt"> 42
&lt;/span>&lt;span class="lnt"> 43
&lt;/span>&lt;span class="lnt"> 44
&lt;/span>&lt;span class="lnt"> 45
&lt;/span>&lt;span class="lnt"> 46
&lt;/span>&lt;span class="lnt"> 47
&lt;/span>&lt;span class="lnt"> 48
&lt;/span>&lt;span class="lnt"> 49
&lt;/span>&lt;span class="lnt"> 50
&lt;/span>&lt;span class="lnt"> 51
&lt;/span>&lt;span class="lnt"> 52
&lt;/span>&lt;span class="lnt"> 53
&lt;/span>&lt;span class="lnt"> 54
&lt;/span>&lt;span class="lnt"> 55
&lt;/span>&lt;span class="lnt"> 56
&lt;/span>&lt;span class="lnt"> 57
&lt;/span>&lt;span class="lnt"> 58
&lt;/span>&lt;span class="lnt"> 59
&lt;/span>&lt;span class="lnt"> 60
&lt;/span>&lt;span class="lnt"> 61
&lt;/span>&lt;span class="lnt"> 62
&lt;/span>&lt;span class="lnt"> 63
&lt;/span>&lt;span class="lnt"> 64
&lt;/span>&lt;span class="lnt"> 65
&lt;/span>&lt;span class="lnt"> 66
&lt;/span>&lt;span class="lnt"> 67
&lt;/span>&lt;span class="lnt"> 68
&lt;/span>&lt;span class="lnt"> 69
&lt;/span>&lt;span class="lnt"> 70
&lt;/span>&lt;span class="lnt"> 71
&lt;/span>&lt;span class="lnt"> 72
&lt;/span>&lt;span class="lnt"> 73
&lt;/span>&lt;span class="lnt"> 74
&lt;/span>&lt;span class="lnt"> 75
&lt;/span>&lt;span class="lnt"> 76
&lt;/span>&lt;span class="lnt"> 77
&lt;/span>&lt;span class="lnt"> 78
&lt;/span>&lt;span class="lnt"> 79
&lt;/span>&lt;span class="lnt"> 80
&lt;/span>&lt;span class="lnt"> 81
&lt;/span>&lt;span class="lnt"> 82
&lt;/span>&lt;span class="lnt"> 83
&lt;/span>&lt;span class="lnt"> 84
&lt;/span>&lt;span class="lnt"> 85
&lt;/span>&lt;span class="lnt"> 86
&lt;/span>&lt;span class="lnt"> 87
&lt;/span>&lt;span class="lnt"> 88
&lt;/span>&lt;span class="lnt"> 89
&lt;/span>&lt;span class="lnt"> 90
&lt;/span>&lt;span class="lnt"> 91
&lt;/span>&lt;span class="lnt"> 92
&lt;/span>&lt;span class="lnt"> 93
&lt;/span>&lt;span class="lnt"> 94
&lt;/span>&lt;span class="lnt"> 95
&lt;/span>&lt;span class="lnt"> 96
&lt;/span>&lt;span class="lnt"> 97
&lt;/span>&lt;span class="lnt"> 98
&lt;/span>&lt;span class="lnt"> 99
&lt;/span>&lt;span class="lnt">100
&lt;/span>&lt;span class="lnt">101
&lt;/span>&lt;span class="lnt">102
&lt;/span>&lt;span class="lnt">103
&lt;/span>&lt;span class="lnt">104
&lt;/span>&lt;span class="lnt">105
&lt;/span>&lt;span class="lnt">106
&lt;/span>&lt;span class="lnt">107
&lt;/span>&lt;span class="lnt">108
&lt;/span>&lt;span class="lnt">109
&lt;/span>&lt;span class="lnt">110
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-sql" data-lang="sql">&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">object_id&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;tempdb..#NewTableDetails&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">is&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">not&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">null&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">drop&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">table&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">#&lt;/span>&lt;span class="n">NewTableDetails&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">create&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">table&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">#&lt;/span>&lt;span class="n">NewTableDetails&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">TrafficLogOrigTable&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">varchar&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">50&lt;/span>&lt;span class="p">),&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">TrafficLogTotalRows&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">bigint&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">OriginalLogDate&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">date&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">object_id&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;tempdb..#LogDetails&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">is&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">not&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">null&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">drop&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">table&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">#&lt;/span>&lt;span class="n">LogDetails&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">create&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">table&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">#&lt;/span>&lt;span class="n">LogDetails&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">LogDate&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">date&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">OriginalTotalRows&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">bigint&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">declare&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="k">sql&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">nvarchar&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">max&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;&amp;#39;&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">declare&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">startdate&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">datetime&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;2019-10-01&amp;#39;&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">declare&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">enddate&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">datetime&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;2019-11-01&amp;#39;&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">object_id&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;tempdb..#dates&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">is&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">not&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">null&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">drop&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">table&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">#&lt;/span>&lt;span class="n">dates&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">CREATE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">TABLE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">#&lt;/span>&lt;span class="n">dates&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nb">Date&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">DATE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">PRIMARY&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">KEY&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">FirstOfMonth&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">AS&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">CONVERT&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nb">DATE&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">DATEADD&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">MONTH&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">DATEDIFF&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">MONTH&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nb">Date&lt;/span>&lt;span class="p">]),&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">)),&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">OldTableName&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">as&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">concat&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;Log_&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">year&lt;/span>&lt;span class="p">([&lt;/span>&lt;span class="nb">date&lt;/span>&lt;span class="p">]),&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;_&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">right&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;0&amp;#39;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">rtrim&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">month&lt;/span>&lt;span class="p">([&lt;/span>&lt;span class="nb">date&lt;/span>&lt;span class="p">])),&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">),&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;_&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">right&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;0&amp;#39;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">rtrim&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">day&lt;/span>&lt;span class="p">([&lt;/span>&lt;span class="nb">date&lt;/span>&lt;span class="p">])),&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">)),&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">NewTableName&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">as&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">concat&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;HAProxyLogs_&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">year&lt;/span>&lt;span class="p">([&lt;/span>&lt;span class="nb">date&lt;/span>&lt;span class="p">]),&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;_&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">right&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;0&amp;#39;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">rtrim&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">month&lt;/span>&lt;span class="p">([&lt;/span>&lt;span class="nb">date&lt;/span>&lt;span class="p">])),&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">))&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">INSERT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">#&lt;/span>&lt;span class="n">dates&lt;/span>&lt;span class="p">([&lt;/span>&lt;span class="nb">Date&lt;/span>&lt;span class="p">])&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">SELECT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">d&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">FROM&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">SELECT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">d&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">DATEADD&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">DAY&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">rn&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">StartDate&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">FROM&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">SELECT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">TOP&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">DATEDIFF&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">DAY&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">StartDate&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">enddate&lt;/span>&lt;span class="p">))&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">rn&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ROW_NUMBER&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">OVER&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">ORDER&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">BY&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">s1&lt;/span>&lt;span class="p">.[&lt;/span>&lt;span class="n">object_id&lt;/span>&lt;span class="p">])&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">FROM&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">sys&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">all_objects&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">AS&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">s1&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">CROSS&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">JOIN&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">sys&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">all_objects&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">AS&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">s2&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">ORDER&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">BY&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">s1&lt;/span>&lt;span class="p">.[&lt;/span>&lt;span class="n">object_id&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">AS&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">x&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">AS&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">y&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="c1">-- for each new table name get the count of rows, get list of OldTables
&lt;/span>&lt;span class="c1">-- Imported and then get the counts for each one
&lt;/span>&lt;span class="c1">&lt;/span>&lt;span class="k">declare&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">newtablename&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">nvarchar&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">100&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">declare&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="nb">date&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">datetime&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">declare&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">newtable_cursor&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">cursor&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">for&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">select&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">distinct&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">NewTableName&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">from&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">#&lt;/span>&lt;span class="n">dates&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">where&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nb">date&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">enddate&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">open&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">newtable_cursor&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">fetch&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">next&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">from&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">newtable_cursor&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">into&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">newtablename&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="n">while&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@@&lt;/span>&lt;span class="n">FETCH_STATUS&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">begin&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">set&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="k">sql&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;insert into #NewTableDetails (TrafficLogOrigTable, TrafficLogTotalRows)
&lt;/span>&lt;span class="s1"> select
&lt;/span>&lt;span class="s1"> TrafficLogOrigTable = OriginalLogTable,
&lt;/span>&lt;span class="s1"> TrafficLogTotalRows = count(*)
&lt;/span>&lt;span class="s1"> from TrafficLogs.dbo.[&amp;#39;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">newtablename&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="s1">&amp;#39;]
&lt;/span>&lt;span class="s1"> group by OriginalLogTable &amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">exec&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">sp_executesql&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="k">sql&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">FETCH&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NEXT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">FROM&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">newtable_cursor&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">INTO&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">newtablename&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">end&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">CLOSE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">newtable_cursor&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">DEALLOCATE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">newtable_cursor&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">update&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">#&lt;/span>&lt;span class="n">NewTableDetails&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">set&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">OriginalLogDate&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">Cast&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">Replace&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">Replace&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">TrafficLogOrigTable&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;Log_&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;&amp;#39;&lt;/span>&lt;span class="p">),&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;_&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;-&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">as&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">date&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">declare&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">oldTableName&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">nvarchar&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">100&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">declare&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">oldtabledate&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">date&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">declare&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">table_cursor&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">cursor&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">for&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">select&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">OriginalLogDate&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">TrafficLogOrigTable&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">from&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">#&lt;/span>&lt;span class="n">NewTableDetails&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">open&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">table_cursor&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">fetch&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">next&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">from&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">table_cursor&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">into&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">oldtabledate&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">oldTableName&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="n">while&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@@&lt;/span>&lt;span class="n">FETCH_STATUS&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">begin&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">set&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="k">sql&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;insert into #LogDetails (LogDate, OriginalTotalRows)
&lt;/span>&lt;span class="s1"> select
&lt;/span>&lt;span class="s1"> LogDate = &amp;#39;&amp;#39;&amp;#39;&lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">convert&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nb">varchar&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">10&lt;/span>&lt;span class="p">),&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">oldtabledate&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">23&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="s1">&amp;#39;&amp;#39;&amp;#39;,
&lt;/span>&lt;span class="s1"> OriginalTotalRows = count(*)
&lt;/span>&lt;span class="s1"> from HAProxyLogs.dbo.[&amp;#39;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">oldTableName&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="s1">&amp;#39;]&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">exec&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">sp_executesql&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="k">sql&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">FETCH&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NEXT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">FROM&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">table_cursor&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">INTO&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">oldtabledate&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">oldTableName&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">end&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">CLOSE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">table_cursor&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">DEALLOCATE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">table_cursor&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">select&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">nt&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">OriginalLogDate&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">nt&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">TrafficLogOrigTable&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">nt&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">TrafficLogTotalRows&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ot&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">OriginalTotalRows&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ot&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">LogDate&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">IsMatch&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">case&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">when&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">nt&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">TrafficLogTotalRows&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ot&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">OriginalTotalRows&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">then&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;Y&amp;#39;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">else&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;N&amp;#39;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">end&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">from&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">#&lt;/span>&lt;span class="n">NewTableDetails&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">nt&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">full&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">join&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">#&lt;/span>&lt;span class="n">LogDetails&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ot&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">on&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">nt&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">OriginalLogDate&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ot&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">LogDate&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">order&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">by&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">nt&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">OriginalLogDate&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;h3 id="column-changes">Column Changes&lt;/h3>
&lt;p>I also discovered that we added new columns after the original daily tables were designed. This meant that over the course of the data migration the table structure would change. I adjusted the LinqPad script so it would add the new columns when needed based on date, this would make sure it continued to work and we wouldn’t skip new columns.&lt;/p>
&lt;p>There were many more issues which involved debugging the script, but eventually I had a C# script that fixed all of the above problems. The new script looked a little like this:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt"> 10
&lt;/span>&lt;span class="lnt"> 11
&lt;/span>&lt;span class="lnt"> 12
&lt;/span>&lt;span class="lnt"> 13
&lt;/span>&lt;span class="lnt"> 14
&lt;/span>&lt;span class="lnt"> 15
&lt;/span>&lt;span class="lnt"> 16
&lt;/span>&lt;span class="lnt"> 17
&lt;/span>&lt;span class="lnt"> 18
&lt;/span>&lt;span class="lnt"> 19
&lt;/span>&lt;span class="lnt"> 20
&lt;/span>&lt;span class="lnt"> 21
&lt;/span>&lt;span class="lnt"> 22
&lt;/span>&lt;span class="lnt"> 23
&lt;/span>&lt;span class="lnt"> 24
&lt;/span>&lt;span class="lnt"> 25
&lt;/span>&lt;span class="lnt"> 26
&lt;/span>&lt;span class="lnt"> 27
&lt;/span>&lt;span class="lnt"> 28
&lt;/span>&lt;span class="lnt"> 29
&lt;/span>&lt;span class="lnt"> 30
&lt;/span>&lt;span class="lnt"> 31
&lt;/span>&lt;span class="lnt"> 32
&lt;/span>&lt;span class="lnt"> 33
&lt;/span>&lt;span class="lnt"> 34
&lt;/span>&lt;span class="lnt"> 35
&lt;/span>&lt;span class="lnt"> 36
&lt;/span>&lt;span class="lnt"> 37
&lt;/span>&lt;span class="lnt"> 38
&lt;/span>&lt;span class="lnt"> 39
&lt;/span>&lt;span class="lnt"> 40
&lt;/span>&lt;span class="lnt"> 41
&lt;/span>&lt;span class="lnt"> 42
&lt;/span>&lt;span class="lnt"> 43
&lt;/span>&lt;span class="lnt"> 44
&lt;/span>&lt;span class="lnt"> 45
&lt;/span>&lt;span class="lnt"> 46
&lt;/span>&lt;span class="lnt"> 47
&lt;/span>&lt;span class="lnt"> 48
&lt;/span>&lt;span class="lnt"> 49
&lt;/span>&lt;span class="lnt"> 50
&lt;/span>&lt;span class="lnt"> 51
&lt;/span>&lt;span class="lnt"> 52
&lt;/span>&lt;span class="lnt"> 53
&lt;/span>&lt;span class="lnt"> 54
&lt;/span>&lt;span class="lnt"> 55
&lt;/span>&lt;span class="lnt"> 56
&lt;/span>&lt;span class="lnt"> 57
&lt;/span>&lt;span class="lnt"> 58
&lt;/span>&lt;span class="lnt"> 59
&lt;/span>&lt;span class="lnt"> 60
&lt;/span>&lt;span class="lnt"> 61
&lt;/span>&lt;span class="lnt"> 62
&lt;/span>&lt;span class="lnt"> 63
&lt;/span>&lt;span class="lnt"> 64
&lt;/span>&lt;span class="lnt"> 65
&lt;/span>&lt;span class="lnt"> 66
&lt;/span>&lt;span class="lnt"> 67
&lt;/span>&lt;span class="lnt"> 68
&lt;/span>&lt;span class="lnt"> 69
&lt;/span>&lt;span class="lnt"> 70
&lt;/span>&lt;span class="lnt"> 71
&lt;/span>&lt;span class="lnt"> 72
&lt;/span>&lt;span class="lnt"> 73
&lt;/span>&lt;span class="lnt"> 74
&lt;/span>&lt;span class="lnt"> 75
&lt;/span>&lt;span class="lnt"> 76
&lt;/span>&lt;span class="lnt"> 77
&lt;/span>&lt;span class="lnt"> 78
&lt;/span>&lt;span class="lnt"> 79
&lt;/span>&lt;span class="lnt"> 80
&lt;/span>&lt;span class="lnt"> 81
&lt;/span>&lt;span class="lnt"> 82
&lt;/span>&lt;span class="lnt"> 83
&lt;/span>&lt;span class="lnt"> 84
&lt;/span>&lt;span class="lnt"> 85
&lt;/span>&lt;span class="lnt"> 86
&lt;/span>&lt;span class="lnt"> 87
&lt;/span>&lt;span class="lnt"> 88
&lt;/span>&lt;span class="lnt"> 89
&lt;/span>&lt;span class="lnt"> 90
&lt;/span>&lt;span class="lnt"> 91
&lt;/span>&lt;span class="lnt"> 92
&lt;/span>&lt;span class="lnt"> 93
&lt;/span>&lt;span class="lnt"> 94
&lt;/span>&lt;span class="lnt"> 95
&lt;/span>&lt;span class="lnt"> 96
&lt;/span>&lt;span class="lnt"> 97
&lt;/span>&lt;span class="lnt"> 98
&lt;/span>&lt;span class="lnt"> 99
&lt;/span>&lt;span class="lnt">100
&lt;/span>&lt;span class="lnt">101
&lt;/span>&lt;span class="lnt">102
&lt;/span>&lt;span class="lnt">103
&lt;/span>&lt;span class="lnt">104
&lt;/span>&lt;span class="lnt">105
&lt;/span>&lt;span class="lnt">106
&lt;/span>&lt;span class="lnt">107
&lt;/span>&lt;span class="lnt">108
&lt;/span>&lt;span class="lnt">109
&lt;/span>&lt;span class="lnt">110
&lt;/span>&lt;span class="lnt">111
&lt;/span>&lt;span class="lnt">112
&lt;/span>&lt;span class="lnt">113
&lt;/span>&lt;span class="lnt">114
&lt;/span>&lt;span class="lnt">115
&lt;/span>&lt;span class="lnt">116
&lt;/span>&lt;span class="lnt">117
&lt;/span>&lt;span class="lnt">118
&lt;/span>&lt;span class="lnt">119
&lt;/span>&lt;span class="lnt">120
&lt;/span>&lt;span class="lnt">121
&lt;/span>&lt;span class="lnt">122
&lt;/span>&lt;span class="lnt">123
&lt;/span>&lt;span class="lnt">124
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cs" data-lang="cs">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="n">Query&lt;/span> &lt;span class="n">Kind&lt;/span>&lt;span class="p">=&lt;/span>&lt;span class="s">&amp;#34;Program&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="n">NuGetReference&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>&lt;span class="n">Dapper&lt;/span>&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="n">NuGetReference&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="n">Namespace&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>&lt;span class="n">Dapper&lt;/span>&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="n">Namespace&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="n">Query&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;span class="k">void&lt;/span> &lt;span class="n">Main&lt;/span>&lt;span class="p">()&lt;/span>
&lt;span class="p">{&lt;/span>
&lt;span class="n">DateTime&lt;/span> &lt;span class="n">date&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="n">DateTime&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="m">2016&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="m">07&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="m">26&lt;/span>&lt;span class="p">);&lt;/span>
&lt;span class="k">while&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">date&lt;/span> &lt;span class="p">&amp;lt;&lt;/span> &lt;span class="n">DateTime&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">UtcNow&lt;/span>&lt;span class="p">)&lt;/span>
&lt;span class="p">{&lt;/span>
&lt;span class="n">MoveDate&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">date&lt;/span>&lt;span class="p">);&lt;/span>
&lt;span class="n">date&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">date&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">AddDays&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="m">1&lt;/span>&lt;span class="p">);&lt;/span>
&lt;span class="p">}&lt;/span>
&lt;span class="p">}&lt;/span>
&lt;span class="k">static&lt;/span> &lt;span class="k">readonly&lt;/span> &lt;span class="n">List&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="kt">string&lt;/span>&lt;span class="p">&amp;gt;&lt;/span> &lt;span class="n">cols&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="n">List&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="kt">string&lt;/span>&lt;span class="p">&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="s">&amp;#34;&amp;lt;col list&amp;gt;&amp;#34;&lt;/span> &lt;span class="p">};&lt;/span>
&lt;span class="k">public&lt;/span> &lt;span class="k">void&lt;/span> &lt;span class="n">MoveDate&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">DateTime&lt;/span> &lt;span class="n">date&lt;/span>&lt;span class="p">)&lt;/span>
&lt;span class="p">{&lt;/span>
&lt;span class="kt">var&lt;/span> &lt;span class="n">tableName&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">GetTableName&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">date&lt;/span>&lt;span class="p">);&lt;/span>
&lt;span class="kt">var&lt;/span> &lt;span class="n">destTable&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">GetDestTableName&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">date&lt;/span>&lt;span class="p">);&lt;/span>
&lt;span class="s">$&amp;#34;Attempting to migrate {date:yyyy-MM-dd} from {tableName}
&lt;/span>&lt;span class="s"> to {destTable}&amp;#34;&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">Dump&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">$&amp;#34;{date:yyyy-MM-dd}&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">date&lt;/span> &lt;span class="p">&amp;gt;=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="n">DateTime&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="m">2017&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="m">01&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="m">12&lt;/span>&lt;span class="p">))&lt;/span>
&lt;span class="p">{&lt;/span>
&lt;span class="n">cols&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">Add&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;new col1&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;span class="n">cols&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">Add&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;new col2&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;span class="p">}&lt;/span>
&lt;span class="k">using&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="kt">var&lt;/span> &lt;span class="n">conn&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">GetConn&lt;/span>&lt;span class="p">())&lt;/span>
&lt;span class="p">{&lt;/span>
&lt;span class="kt">int&lt;/span> &lt;span class="n">rowCount&lt;/span>&lt;span class="p">;&lt;/span>
&lt;span class="k">try&lt;/span>
&lt;span class="p">{&lt;/span>
&lt;span class="n">rowCount&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">conn&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">QuerySingle&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="kt">int&lt;/span>&lt;span class="p">&amp;gt;(&lt;/span>&lt;span class="s">$&amp;#34;Select Count(*)
&lt;/span>&lt;span class="s"> From HAProxyLogs.dbo.{tableName};&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;span class="p">}&lt;/span>
&lt;span class="k">catch&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">SqlException&lt;/span> &lt;span class="n">e&lt;/span>&lt;span class="p">)&lt;/span>
&lt;span class="p">{&lt;/span>
&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34; Error migrating: &amp;#34;&lt;/span> &lt;span class="p">+&lt;/span> &lt;span class="n">e&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">Message&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="n">Dump&lt;/span>&lt;span class="p">();&lt;/span>
&lt;span class="k">return&lt;/span>&lt;span class="p">;&lt;/span>
&lt;span class="p">}&lt;/span>
&lt;span class="s">$&amp;#34; Summary for {date:yyyy-MM-dd}&amp;#34;&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">Dump&lt;/span>&lt;span class="p">();&lt;/span>
&lt;span class="s">$&amp;#34; {rowCount:n0} row(s) in {tableName}&amp;#34;&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">Dump&lt;/span>&lt;span class="p">();&lt;/span>
&lt;span class="kt">var&lt;/span> &lt;span class="n">pb&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="n">Util&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">ProgressBar&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">$&amp;#34;{tableName} (0/{rowCount})&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;span class="n">pb&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">Dump&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">tableName&lt;/span> &lt;span class="p">+&lt;/span> &lt;span class="s">&amp;#34; copy&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;span class="n">Func&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="kt">int&lt;/span>&lt;span class="p">&amp;gt;&lt;/span> &lt;span class="n">GetDestRowCount&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">()&lt;/span>
&lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="n">conn&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">QuerySingle&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="kt">int&lt;/span>&lt;span class="p">&amp;gt;(&lt;/span>&lt;span class="s">$&amp;#34;Select Count(*)
&lt;/span>&lt;span class="s"> From {destTable}
&lt;/span>&lt;span class="s"> Where CreationDate &amp;gt;= @date
&lt;/span>&lt;span class="s"> And OriginalLogTable = &amp;#39;{tableName}&amp;#39;;&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="n">date&lt;/span> &lt;span class="p">});&lt;/span>
&lt;span class="n">Action&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="kt">long&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kt">int&lt;/span>&lt;span class="p">&amp;gt;&lt;/span> &lt;span class="n">UpdatePB&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">copied&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">total&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span>
&lt;span class="p">{&lt;/span>
&lt;span class="n">pb&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">Fraction&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="kt">double&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="n">copied&lt;/span> &lt;span class="p">/&lt;/span> &lt;span class="n">total&lt;/span>&lt;span class="p">;&lt;/span>
&lt;span class="n">pb&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">Caption&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s">$&amp;#34;{tableName} ({copied}/{total})&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;span class="p">};&lt;/span>
&lt;span class="kt">var&lt;/span> &lt;span class="n">destRowCount&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">GetDestRowCount&lt;/span>&lt;span class="p">();&lt;/span>
&lt;span class="s">$&amp;#34;There are {destRowCount:n0} row(s) for table: {destTable}&amp;#34;&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">Dump&lt;/span>&lt;span class="p">();&lt;/span>
&lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">destRowCount&lt;/span> &lt;span class="p">&amp;gt;&lt;/span> &lt;span class="m">0&lt;/span>&lt;span class="p">)&lt;/span>
&lt;span class="p">{&lt;/span>
&lt;span class="s">$&amp;#34;Rows found in destination table - aborting!&amp;#34;&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">Dump&lt;/span>&lt;span class="p">();&lt;/span>
&lt;span class="k">return&lt;/span>&lt;span class="p">;&lt;/span>
&lt;span class="p">}&lt;/span>
&lt;span class="kt">var&lt;/span> &lt;span class="n">sqlString&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s">$&amp;#34;Select {string.Join(&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="s">&amp;#34;, cols)}, OriginalLogTable = &amp;#39;{tableName}&amp;#39;
&lt;/span>&lt;span class="s">&lt;/span> &lt;span class="n">From&lt;/span> &lt;span class="n">HAProxyLogs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">dbo&lt;/span>&lt;span class="p">.{&lt;/span>&lt;span class="n">tableName&lt;/span>&lt;span class="p">};&lt;/span>&lt;span class="s">&amp;#34;;
&lt;/span>&lt;span class="s">&lt;/span> &lt;span class="kt">var&lt;/span> &lt;span class="n">reader&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">conn&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">ExecuteReader&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">sqlString&lt;/span>&lt;span class="p">);&lt;/span>
&lt;span class="k">using&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">SqlConnection&lt;/span> &lt;span class="n">dest&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">GetConn&lt;/span>&lt;span class="p">())&lt;/span>
&lt;span class="p">{&lt;/span>
&lt;span class="n">dest&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">Open&lt;/span>&lt;span class="p">();&lt;/span>
&lt;span class="k">using&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">SqlBulkCopy&lt;/span> &lt;span class="n">bc&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="n">SqlBulkCopy&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">dest&lt;/span>&lt;span class="p">))&lt;/span>
&lt;span class="p">{&lt;/span>
&lt;span class="n">bc&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">BulkCopyTimeout&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="m">15&lt;/span>&lt;span class="p">*&lt;/span>&lt;span class="m">60&lt;/span>&lt;span class="p">;&lt;/span>
&lt;span class="n">bc&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">BatchSize&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="m">1048576&lt;/span>&lt;span class="p">;&lt;/span>
&lt;span class="n">bc&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">NotifyAfter&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="m">100000&lt;/span>&lt;span class="p">;&lt;/span>
&lt;span class="n">bc&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">DestinationTableName&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">GetDestTableName&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">date&lt;/span>&lt;span class="p">);&lt;/span>
&lt;span class="n">bc&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">SqlRowsCopied&lt;/span> &lt;span class="p">+=&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">e&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">s&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="n">UpdatePB&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">s&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">RowsCopied&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">rowCount&lt;/span>&lt;span class="p">);&lt;/span>
&lt;span class="k">foreach&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="kt">var&lt;/span> &lt;span class="n">c&lt;/span> &lt;span class="k">in&lt;/span> &lt;span class="n">cols&lt;/span>&lt;span class="p">)&lt;/span>
&lt;span class="p">{&lt;/span>
&lt;span class="n">bc&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">ColumnMappings&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">Add&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">c&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">c&lt;/span>&lt;span class="p">);&lt;/span>
&lt;span class="p">}&lt;/span>
&lt;span class="k">try&lt;/span>
&lt;span class="p">{&lt;/span>
&lt;span class="n">bc&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">ColumnMappings&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">Add&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;OriginalLogTable&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s">&amp;#34;OriginalLogTable&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;span class="n">bc&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">WriteToServer&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">reader&lt;/span>&lt;span class="p">);&lt;/span>
&lt;span class="p">}&lt;/span>
&lt;span class="k">catch&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">Exception&lt;/span> &lt;span class="n">ex&lt;/span>&lt;span class="p">)&lt;/span>
&lt;span class="p">{&lt;/span>
&lt;span class="n">Console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">WriteLine&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">ex&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">Message&lt;/span>&lt;span class="p">);&lt;/span>
&lt;span class="p">}&lt;/span>
&lt;span class="k">finally&lt;/span>
&lt;span class="p">{&lt;/span>
&lt;span class="n">reader&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">Close&lt;/span>&lt;span class="p">();&lt;/span>
&lt;span class="p">}&lt;/span>
&lt;span class="p">}&lt;/span>
&lt;span class="c1">// try updating stats after full dump to see if that stops time-outs
&lt;/span>&lt;span class="c1">&lt;/span> &lt;span class="kt">var&lt;/span> &lt;span class="n">statsString&lt;/span>
&lt;span class="p">=&lt;/span> &lt;span class="s">$&amp;#34;waitfor delay &amp;#39;00:00:05&amp;#39;;
&lt;/span>&lt;span class="s"> update statistics dbo.[{destTable}] [{destTable}_OriginalLogTable];&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;span class="n">conn&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">Query&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">statsString&lt;/span>&lt;span class="p">);&lt;/span>
&lt;span class="kt">var&lt;/span> &lt;span class="n">destFinalCount&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">GetDestRowCount&lt;/span>&lt;span class="p">();&lt;/span>
&lt;span class="s">$&amp;#34;After migration there are {destFinalCount:n0} row(s) in {destTable}&amp;#34;&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">Dump&lt;/span>&lt;span class="p">();&lt;/span>
&lt;span class="p">}&lt;/span>
&lt;span class="p">}&lt;/span>
&lt;span class="p">}&lt;/span>
&lt;span class="k">public&lt;/span> &lt;span class="n">SqlConnection&lt;/span> &lt;span class="n">GetConn&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span>
&lt;span class="k">new&lt;/span> &lt;span class="n">SqlConnection&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;Data Source=servername;Initial Catalog=TrafficLogs;Trusted_Connection=True;Connection Timeout = 1500;&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;span class="k">public&lt;/span> &lt;span class="kt">string&lt;/span> &lt;span class="n">GetTableName&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">DateTime&lt;/span> &lt;span class="n">dt&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="s">$&amp;#34;Log_{dt:yyyy_MM_dd}&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;span class="k">public&lt;/span> &lt;span class="kt">string&lt;/span> &lt;span class="n">GetDestTableName&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">DateTime&lt;/span> &lt;span class="n">dt&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="s">$&amp;#34;HAProxyLogs_{dt:yyyy_MM}&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;h2 id="its-so-slow">It&amp;rsquo;s So Slow&lt;/h2>
&lt;p>We all knew this was going to be a slow process, but I don&amp;rsquo;t think anyone realized how slow. I did everything I could think of to try and speed up the script.&lt;/p>
&lt;p>It took about 2-2.5 hours to insert each old table into the new table. By the middle of February, I had:&lt;/p>
&lt;ul>
&lt;li>1332 tables left to migrate in New York&lt;/li>
&lt;li>and 782 tables left in Colorado&lt;/li>
&lt;/ul>
&lt;p>At the rate it was going, I knew &lt;a href="https://twitter.com/tarynpivots/status/1098695568140906496">it was going to take months to complete&lt;/a>.&lt;/p>
&lt;p>I went back to the drawing board to figure out some way to move the data faster. Obviously, I wanted to follow the same steps - insert old data, verify the row count matches, move to the next day, repeat - I used the C# script as my model - I mean it worked, it was just a little slow. I tried a bunch of different things, none were blazingly fast:&lt;/p>
&lt;ul>
&lt;li>an SSIS package - sorry SSIS fans, this was even slower and I won&amp;rsquo;t embarrass myself by sharing it&lt;/li>
&lt;li>a SQL script using dynamic SQL - this was also pretty slow&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt"> 10
&lt;/span>&lt;span class="lnt"> 11
&lt;/span>&lt;span class="lnt"> 12
&lt;/span>&lt;span class="lnt"> 13
&lt;/span>&lt;span class="lnt"> 14
&lt;/span>&lt;span class="lnt"> 15
&lt;/span>&lt;span class="lnt"> 16
&lt;/span>&lt;span class="lnt"> 17
&lt;/span>&lt;span class="lnt"> 18
&lt;/span>&lt;span class="lnt"> 19
&lt;/span>&lt;span class="lnt"> 20
&lt;/span>&lt;span class="lnt"> 21
&lt;/span>&lt;span class="lnt"> 22
&lt;/span>&lt;span class="lnt"> 23
&lt;/span>&lt;span class="lnt"> 24
&lt;/span>&lt;span class="lnt"> 25
&lt;/span>&lt;span class="lnt"> 26
&lt;/span>&lt;span class="lnt"> 27
&lt;/span>&lt;span class="lnt"> 28
&lt;/span>&lt;span class="lnt"> 29
&lt;/span>&lt;span class="lnt"> 30
&lt;/span>&lt;span class="lnt"> 31
&lt;/span>&lt;span class="lnt"> 32
&lt;/span>&lt;span class="lnt"> 33
&lt;/span>&lt;span class="lnt"> 34
&lt;/span>&lt;span class="lnt"> 35
&lt;/span>&lt;span class="lnt"> 36
&lt;/span>&lt;span class="lnt"> 37
&lt;/span>&lt;span class="lnt"> 38
&lt;/span>&lt;span class="lnt"> 39
&lt;/span>&lt;span class="lnt"> 40
&lt;/span>&lt;span class="lnt"> 41
&lt;/span>&lt;span class="lnt"> 42
&lt;/span>&lt;span class="lnt"> 43
&lt;/span>&lt;span class="lnt"> 44
&lt;/span>&lt;span class="lnt"> 45
&lt;/span>&lt;span class="lnt"> 46
&lt;/span>&lt;span class="lnt"> 47
&lt;/span>&lt;span class="lnt"> 48
&lt;/span>&lt;span class="lnt"> 49
&lt;/span>&lt;span class="lnt"> 50
&lt;/span>&lt;span class="lnt"> 51
&lt;/span>&lt;span class="lnt"> 52
&lt;/span>&lt;span class="lnt"> 53
&lt;/span>&lt;span class="lnt"> 54
&lt;/span>&lt;span class="lnt"> 55
&lt;/span>&lt;span class="lnt"> 56
&lt;/span>&lt;span class="lnt"> 57
&lt;/span>&lt;span class="lnt"> 58
&lt;/span>&lt;span class="lnt"> 59
&lt;/span>&lt;span class="lnt"> 60
&lt;/span>&lt;span class="lnt"> 61
&lt;/span>&lt;span class="lnt"> 62
&lt;/span>&lt;span class="lnt"> 63
&lt;/span>&lt;span class="lnt"> 64
&lt;/span>&lt;span class="lnt"> 65
&lt;/span>&lt;span class="lnt"> 66
&lt;/span>&lt;span class="lnt"> 67
&lt;/span>&lt;span class="lnt"> 68
&lt;/span>&lt;span class="lnt"> 69
&lt;/span>&lt;span class="lnt"> 70
&lt;/span>&lt;span class="lnt"> 71
&lt;/span>&lt;span class="lnt"> 72
&lt;/span>&lt;span class="lnt"> 73
&lt;/span>&lt;span class="lnt"> 74
&lt;/span>&lt;span class="lnt"> 75
&lt;/span>&lt;span class="lnt"> 76
&lt;/span>&lt;span class="lnt"> 77
&lt;/span>&lt;span class="lnt"> 78
&lt;/span>&lt;span class="lnt"> 79
&lt;/span>&lt;span class="lnt"> 80
&lt;/span>&lt;span class="lnt"> 81
&lt;/span>&lt;span class="lnt"> 82
&lt;/span>&lt;span class="lnt"> 83
&lt;/span>&lt;span class="lnt"> 84
&lt;/span>&lt;span class="lnt"> 85
&lt;/span>&lt;span class="lnt"> 86
&lt;/span>&lt;span class="lnt"> 87
&lt;/span>&lt;span class="lnt"> 88
&lt;/span>&lt;span class="lnt"> 89
&lt;/span>&lt;span class="lnt"> 90
&lt;/span>&lt;span class="lnt"> 91
&lt;/span>&lt;span class="lnt"> 92
&lt;/span>&lt;span class="lnt"> 93
&lt;/span>&lt;span class="lnt"> 94
&lt;/span>&lt;span class="lnt"> 95
&lt;/span>&lt;span class="lnt"> 96
&lt;/span>&lt;span class="lnt"> 97
&lt;/span>&lt;span class="lnt"> 98
&lt;/span>&lt;span class="lnt"> 99
&lt;/span>&lt;span class="lnt">100
&lt;/span>&lt;span class="lnt">101
&lt;/span>&lt;span class="lnt">102
&lt;/span>&lt;span class="lnt">103
&lt;/span>&lt;span class="lnt">104
&lt;/span>&lt;span class="lnt">105
&lt;/span>&lt;span class="lnt">106
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-sql" data-lang="sql">&lt;span class="k">declare&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">startDate&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">datetime&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;2015-11-01&amp;#39;&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">declare&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">endDate&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">datetime&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;2015-12-01&amp;#39;&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">declare&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">OldTableName&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">varchar&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">100&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">declare&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">NewTableName&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">varchar&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">100&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">declare&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">cols&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">varchar&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">max&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;comma, separated, col, list&amp;#39;&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">declare&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">originalrowcount&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">int&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">declare&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">newrowcount&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">int&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">declare&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">finalrowcount&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">int&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">declare&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">rowcountsql&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">nvarchar&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">max&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">declare&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">insertsql&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">nvarchar&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">max&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">declare&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">statssql&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">nvarchar&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">max&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="n">while&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">startDate&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">endDate&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">begin&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">set&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">OldTableName&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">concat&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;Log_&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">year&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">startDate&lt;/span>&lt;span class="p">),&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;_&amp;#39;&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">right&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;0&amp;#39;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">rtrim&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">month&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">startDate&lt;/span>&lt;span class="p">)),&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">),&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;_&amp;#39;&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">right&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;0&amp;#39;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">rtrim&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">day&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">startDate&lt;/span>&lt;span class="p">)),&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">))&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">set&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">NewTableName&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">concat&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;HAProxyLogs_&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">year&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">startDate&lt;/span>&lt;span class="p">),&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;_&amp;#39;&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">right&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;0&amp;#39;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">rtrim&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">month&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">startDate&lt;/span>&lt;span class="p">)),&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">))&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="n">print&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">concat&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;Attempting to migrate &amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">convert&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nb">varchar&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">10&lt;/span>&lt;span class="p">),&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">startDate&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">120&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39; from &amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">OldTableName&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39; to &amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">NewTableName&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="n">print&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39; &amp;#39;&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">startDate&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">&amp;gt;=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;2017-01-12&amp;#39;&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">begin&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">charindex&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;new col1&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">cols&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">begin&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">set&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">cols&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">concat&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">cols&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;, new col1&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">end&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">charindex&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;new col2&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">cols&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">begin&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">set&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">cols&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">concat&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">cols&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;, new col2&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">end&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">end&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">set&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">rowcountsql&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">concat&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;select @cnt = count(*) from HAProxyLogs.dbo.[&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">OldTableName&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;]&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">exec&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">sp_executesql&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">rowcountsql&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">N&lt;/span>&lt;span class="s1">&amp;#39;@cnt int output&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">cnt&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">originalrowcount&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">output&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="cm">/*
&lt;/span>&lt;span class="cm">print concat(&amp;#39;Summary for &amp;#39;, convert(varchar(10), @startDate, 120)
&lt;/span>&lt;span class="cm"> , &amp;#39; &amp;#39;, @originalrowcount, &amp;#39; rows in &amp;#39;, @OldTableName);
&lt;/span>&lt;span class="cm">print &amp;#39; &amp;#39;
&lt;/span>&lt;span class="cm">print concat(&amp;#39;Starting copy of &amp;#39;, @OldTableName);
&lt;/span>&lt;span class="cm">print &amp;#39; &amp;#39;
&lt;/span>&lt;span class="cm">*/&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="c1">-- check if the new table has any rows in it
&lt;/span>&lt;span class="c1">&lt;/span>&lt;span class="k">set&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">rowcountsql&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">concat&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;select @cnt = count(*) from &amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">NewTableName&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39; where CreationDate &amp;gt;= &amp;#39;&amp;#39;&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">convert&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nb">varchar&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">10&lt;/span>&lt;span class="p">),&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">startDate&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">120&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;&amp;#39;&amp;#39; and OriginalLogTable = &amp;#39;&amp;#39;&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">OldTableName&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;&amp;#39;&amp;#39;;&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="c1">--print @rowcountsql
&lt;/span>&lt;span class="c1">&lt;/span>&lt;span class="k">exec&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">sp_executesql&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">rowcountsql&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">N&lt;/span>&lt;span class="s1">&amp;#39;@cnt int output&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">cnt&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">newrowcount&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">output&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="n">print&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">concat&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;There are &amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">newrowcount&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39; row(s) for table: &amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">NewTableName&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="n">print&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39; &amp;#39;&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">If&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">newrowcount&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">begin&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">print&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;Rows found in the destination table - aborting&amp;#39;&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">print&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39; &amp;#39;&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">break&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">end&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">set&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">insertsql&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">concat&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;insert into TrafficLogs.dbo.&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">NewTableName&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;(&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">cols&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;, OriginalLogTable)
&lt;/span>&lt;span class="s1"> select &amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">cols&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;, OriginalLogTable = &amp;#39;&amp;#39;&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">OldTableName&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;&amp;#39;&amp;#39;
&lt;/span>&lt;span class="s1"> from HAProxyLogs.dbo.&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">OldTableName&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="n">print&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">insertsql&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="n">print&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39; &amp;#39;&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">exec&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">sp_executesql&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">insertsql&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">set&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">statssql&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">concat&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;update statistics TrafficLogs.dbo.&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">NewTableName&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39; &amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">NewTableName&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;_OriginalLogTable&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="n">print&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">statssql&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="n">print&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39; &amp;#39;&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">exec&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">sp_executesql&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">statssql&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">set&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">rowcountsql&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">concat&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;select @cnt = count(*) from &amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">NewTableName&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39; where CreationDate &amp;gt;= &amp;#39;&amp;#39;&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">convert&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nb">varchar&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">10&lt;/span>&lt;span class="p">),&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">startDate&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">120&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;&amp;#39;&amp;#39; and OriginalLogTable = &amp;#39;&amp;#39;&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">OldTableName&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;&amp;#39;&amp;#39;;&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">exec&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">sp_executesql&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">rowcountsql&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">N&lt;/span>&lt;span class="s1">&amp;#39;@cnt int output&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">cnt&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">finalrowcount&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">output&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="n">print&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">concat&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;After migration there are &amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">finalrowcount&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39; row(s) in the &amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">NewTableName&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">If&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">originalrowcount&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">&amp;lt;&amp;gt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">finalrowcount&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">begin&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">print&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;final row does not match original stopping operation&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">print&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39; &amp;#39;&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">break&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">end&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">set&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">startDate&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">dateadd&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">day&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">startDate&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">END&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;ul>
&lt;li>a PowerShell version - slow but seemed to be more stable than the original C# script&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt"> 10
&lt;/span>&lt;span class="lnt"> 11
&lt;/span>&lt;span class="lnt"> 12
&lt;/span>&lt;span class="lnt"> 13
&lt;/span>&lt;span class="lnt"> 14
&lt;/span>&lt;span class="lnt"> 15
&lt;/span>&lt;span class="lnt"> 16
&lt;/span>&lt;span class="lnt"> 17
&lt;/span>&lt;span class="lnt"> 18
&lt;/span>&lt;span class="lnt"> 19
&lt;/span>&lt;span class="lnt"> 20
&lt;/span>&lt;span class="lnt"> 21
&lt;/span>&lt;span class="lnt"> 22
&lt;/span>&lt;span class="lnt"> 23
&lt;/span>&lt;span class="lnt"> 24
&lt;/span>&lt;span class="lnt"> 25
&lt;/span>&lt;span class="lnt"> 26
&lt;/span>&lt;span class="lnt"> 27
&lt;/span>&lt;span class="lnt"> 28
&lt;/span>&lt;span class="lnt"> 29
&lt;/span>&lt;span class="lnt"> 30
&lt;/span>&lt;span class="lnt"> 31
&lt;/span>&lt;span class="lnt"> 32
&lt;/span>&lt;span class="lnt"> 33
&lt;/span>&lt;span class="lnt"> 34
&lt;/span>&lt;span class="lnt"> 35
&lt;/span>&lt;span class="lnt"> 36
&lt;/span>&lt;span class="lnt"> 37
&lt;/span>&lt;span class="lnt"> 38
&lt;/span>&lt;span class="lnt"> 39
&lt;/span>&lt;span class="lnt"> 40
&lt;/span>&lt;span class="lnt"> 41
&lt;/span>&lt;span class="lnt"> 42
&lt;/span>&lt;span class="lnt"> 43
&lt;/span>&lt;span class="lnt"> 44
&lt;/span>&lt;span class="lnt"> 45
&lt;/span>&lt;span class="lnt"> 46
&lt;/span>&lt;span class="lnt"> 47
&lt;/span>&lt;span class="lnt"> 48
&lt;/span>&lt;span class="lnt"> 49
&lt;/span>&lt;span class="lnt"> 50
&lt;/span>&lt;span class="lnt"> 51
&lt;/span>&lt;span class="lnt"> 52
&lt;/span>&lt;span class="lnt"> 53
&lt;/span>&lt;span class="lnt"> 54
&lt;/span>&lt;span class="lnt"> 55
&lt;/span>&lt;span class="lnt"> 56
&lt;/span>&lt;span class="lnt"> 57
&lt;/span>&lt;span class="lnt"> 58
&lt;/span>&lt;span class="lnt"> 59
&lt;/span>&lt;span class="lnt"> 60
&lt;/span>&lt;span class="lnt"> 61
&lt;/span>&lt;span class="lnt"> 62
&lt;/span>&lt;span class="lnt"> 63
&lt;/span>&lt;span class="lnt"> 64
&lt;/span>&lt;span class="lnt"> 65
&lt;/span>&lt;span class="lnt"> 66
&lt;/span>&lt;span class="lnt"> 67
&lt;/span>&lt;span class="lnt"> 68
&lt;/span>&lt;span class="lnt"> 69
&lt;/span>&lt;span class="lnt"> 70
&lt;/span>&lt;span class="lnt"> 71
&lt;/span>&lt;span class="lnt"> 72
&lt;/span>&lt;span class="lnt"> 73
&lt;/span>&lt;span class="lnt"> 74
&lt;/span>&lt;span class="lnt"> 75
&lt;/span>&lt;span class="lnt"> 76
&lt;/span>&lt;span class="lnt"> 77
&lt;/span>&lt;span class="lnt"> 78
&lt;/span>&lt;span class="lnt"> 79
&lt;/span>&lt;span class="lnt"> 80
&lt;/span>&lt;span class="lnt"> 81
&lt;/span>&lt;span class="lnt"> 82
&lt;/span>&lt;span class="lnt"> 83
&lt;/span>&lt;span class="lnt"> 84
&lt;/span>&lt;span class="lnt"> 85
&lt;/span>&lt;span class="lnt"> 86
&lt;/span>&lt;span class="lnt"> 87
&lt;/span>&lt;span class="lnt"> 88
&lt;/span>&lt;span class="lnt"> 89
&lt;/span>&lt;span class="lnt"> 90
&lt;/span>&lt;span class="lnt"> 91
&lt;/span>&lt;span class="lnt"> 92
&lt;/span>&lt;span class="lnt"> 93
&lt;/span>&lt;span class="lnt"> 94
&lt;/span>&lt;span class="lnt"> 95
&lt;/span>&lt;span class="lnt"> 96
&lt;/span>&lt;span class="lnt"> 97
&lt;/span>&lt;span class="lnt"> 98
&lt;/span>&lt;span class="lnt"> 99
&lt;/span>&lt;span class="lnt">100
&lt;/span>&lt;span class="lnt">101
&lt;/span>&lt;span class="lnt">102
&lt;/span>&lt;span class="lnt">103
&lt;/span>&lt;span class="lnt">104
&lt;/span>&lt;span class="lnt">105
&lt;/span>&lt;span class="lnt">106
&lt;/span>&lt;span class="lnt">107
&lt;/span>&lt;span class="lnt">108
&lt;/span>&lt;span class="lnt">109
&lt;/span>&lt;span class="lnt">110
&lt;/span>&lt;span class="lnt">111
&lt;/span>&lt;span class="lnt">112
&lt;/span>&lt;span class="lnt">113
&lt;/span>&lt;span class="lnt">114
&lt;/span>&lt;span class="lnt">115
&lt;/span>&lt;span class="lnt">116
&lt;/span>&lt;span class="lnt">117
&lt;/span>&lt;span class="lnt">118
&lt;/span>&lt;span class="lnt">119
&lt;/span>&lt;span class="lnt">120
&lt;/span>&lt;span class="lnt">121
&lt;/span>&lt;span class="lnt">122
&lt;/span>&lt;span class="lnt">123
&lt;/span>&lt;span class="lnt">124
&lt;/span>&lt;span class="lnt">125
&lt;/span>&lt;span class="lnt">126
&lt;/span>&lt;span class="lnt">127
&lt;/span>&lt;span class="lnt">128
&lt;/span>&lt;span class="lnt">129
&lt;/span>&lt;span class="lnt">130
&lt;/span>&lt;span class="lnt">131
&lt;/span>&lt;span class="lnt">132
&lt;/span>&lt;span class="lnt">133
&lt;/span>&lt;span class="lnt">134
&lt;/span>&lt;span class="lnt">135
&lt;/span>&lt;span class="lnt">136
&lt;/span>&lt;span class="lnt">137
&lt;/span>&lt;span class="lnt">138
&lt;/span>&lt;span class="lnt">139
&lt;/span>&lt;span class="lnt">140
&lt;/span>&lt;span class="lnt">141
&lt;/span>&lt;span class="lnt">142
&lt;/span>&lt;span class="lnt">143
&lt;/span>&lt;span class="lnt">144
&lt;/span>&lt;span class="lnt">145
&lt;/span>&lt;span class="lnt">146
&lt;/span>&lt;span class="lnt">147
&lt;/span>&lt;span class="lnt">148
&lt;/span>&lt;span class="lnt">149
&lt;/span>&lt;span class="lnt">150
&lt;/span>&lt;span class="lnt">151
&lt;/span>&lt;span class="lnt">152
&lt;/span>&lt;span class="lnt">153
&lt;/span>&lt;span class="lnt">154
&lt;/span>&lt;span class="lnt">155
&lt;/span>&lt;span class="lnt">156
&lt;/span>&lt;span class="lnt">157
&lt;/span>&lt;span class="lnt">158
&lt;/span>&lt;span class="lnt">159
&lt;/span>&lt;span class="lnt">160
&lt;/span>&lt;span class="lnt">161
&lt;/span>&lt;span class="lnt">162
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-powershell" data-lang="powershell">&lt;span class="cm">&amp;lt;#
&lt;/span>&lt;span class="cm"> &lt;/span>&lt;span class="sd">.SYNOPSIS&lt;/span>&lt;span class="cm">
&lt;/span>&lt;span class="cm"> Traffic Log Data Migration
&lt;/span>&lt;span class="cm">
&lt;/span>&lt;span class="cm"> &lt;/span>&lt;span class="sd">.EXAMPLE&lt;/span>&lt;span class="cm">
&lt;/span>&lt;span class="cm"> .\DataMigration.ps1 -ServerName name -StartDate 2019-01-01 -EndDate 2019-02-01
&lt;/span>&lt;span class="cm">#&amp;gt;&lt;/span>
&lt;span class="p">[&lt;/span>&lt;span class="k">CmdletBinding&lt;/span>&lt;span class="p">()]&lt;/span> &lt;span class="c">#See http://technet.microsoft.com/en-us/library/hh847884(v=wps.620).aspx for CmdletBinding common parameters&lt;/span>
&lt;span class="k">param&lt;/span>&lt;span class="p">(&lt;/span>
&lt;span class="p">[&lt;/span>&lt;span class="k">parameter&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">Mandatory&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="nv">$true&lt;/span>&lt;span class="p">)]&lt;/span>
&lt;span class="no">[string]&lt;/span>&lt;span class="nv">$ServerName&lt;/span>&lt;span class="p">,&lt;/span>
&lt;span class="p">[&lt;/span>&lt;span class="k">parameter&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">Mandatory&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="nv">$true&lt;/span>&lt;span class="p">)]&lt;/span>
&lt;span class="no">[DateTime]&lt;/span>&lt;span class="nv">$StartDate&lt;/span>&lt;span class="p">,&lt;/span>
&lt;span class="p">[&lt;/span>&lt;span class="k">parameter&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">Mandatory&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="nv">$true&lt;/span>&lt;span class="p">)]&lt;/span>
&lt;span class="no">[DateTime]&lt;/span>&lt;span class="nv">$EndDate&lt;/span>
&lt;span class="p">)&lt;/span>
&lt;span class="c"># taken from https://blog.netnerds.net/2015/05/getting-total-number-of-rows-copied-in-sqlbulkcopy-using-powershell/&lt;/span>
&lt;span class="nv">$source&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s1">&amp;#39;namespace System.Data.SqlClient
&lt;/span>&lt;span class="s1">{
&lt;/span>&lt;span class="s1"> using Reflection;
&lt;/span>&lt;span class="s1"> public static class SqlBulkCopyExtension
&lt;/span>&lt;span class="s1"> {
&lt;/span>&lt;span class="s1"> const String _rowsCopiedFieldName = &amp;#34;_rowsCopied&amp;#34;;
&lt;/span>&lt;span class="s1"> static FieldInfo _rowsCopiedField = null;
&lt;/span>&lt;span class="s1"> public static int RowsCopiedCount(this SqlBulkCopy bulkCopy)
&lt;/span>&lt;span class="s1"> {
&lt;/span>&lt;span class="s1"> if (_rowsCopiedField == null) _rowsCopiedField
&lt;/span>&lt;span class="s1"> = typeof(SqlBulkCopy).GetField(_rowsCopiedFieldName, BindingFlags.NonPublic
&lt;/span>&lt;span class="s1"> | BindingFlags.GetField | BindingFlags.Instance);
&lt;/span>&lt;span class="s1"> return (int)_rowsCopiedField.GetValue(bulkCopy);
&lt;/span>&lt;span class="s1"> }
&lt;/span>&lt;span class="s1"> }
&lt;/span>&lt;span class="s1">}
&lt;/span>&lt;span class="s1">&amp;#39;&lt;/span>
&lt;span class="nb">Add-Type&lt;/span> &lt;span class="n">-ReferencedAssemblies&lt;/span> &lt;span class="s1">&amp;#39;System.Data.dll&amp;#39;&lt;/span> &lt;span class="n">-TypeDefinition&lt;/span> &lt;span class="nv">$source&lt;/span>
&lt;span class="nv">$null&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="no">[Reflection.Assembly]&lt;/span>&lt;span class="p">::&lt;/span>&lt;span class="n">LoadWithPartialName&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;System.Data&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;span class="k">Function&lt;/span> &lt;span class="n">ConnectionString&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="no">[string]&lt;/span> &lt;span class="nv">$ServerName&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="no">[string]&lt;/span> &lt;span class="nv">$DbName&lt;/span>&lt;span class="p">)&lt;/span>
&lt;span class="p">{&lt;/span>
&lt;span class="s2">&amp;#34;Data Source=$ServerName;Initial Catalog=TrafficLogs;Trusted_Connection=True;Connection Timeout = 2000;&amp;#34;&lt;/span>
&lt;span class="p">}&lt;/span>
&lt;span class="k">Function&lt;/span> &lt;span class="n">GetOriginalTableRowCount&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nv">$sqlConnection&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nv">$table&lt;/span>&lt;span class="p">){&lt;/span>
&lt;span class="nv">$sqlConnection&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">open&lt;/span>&lt;span class="p">();&lt;/span>
&lt;span class="nv">$OriginalRowCountCmd&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;select OriginalCount = count(*) from &amp;#34;&lt;/span> &lt;span class="p">+&lt;/span> &lt;span class="nv">$table&lt;/span>&lt;span class="p">;&lt;/span>
&lt;span class="nv">$SqlCmd&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="nb">New-Object&lt;/span> &lt;span class="n">System&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">Data&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">SqlClient&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">SqlCommand&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nv">$OriginalRowCountCmd&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nv">$sqlConnection&lt;/span>&lt;span class="p">)&lt;/span>
&lt;span class="nv">$SqlCmd&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">CommandTimeout&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;span class="nv">$row_count&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="no">[Int64]&lt;/span> &lt;span class="nv">$SqlCmd&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">ExecuteScalar&lt;/span>&lt;span class="p">()&lt;/span>
&lt;span class="nv">$sqlConnection&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">Close&lt;/span>&lt;span class="p">();&lt;/span>
&lt;span class="k">return&lt;/span> &lt;span class="nv">$row_count&lt;/span>
&lt;span class="p">}&lt;/span>
&lt;span class="k">Function&lt;/span> &lt;span class="n">GetNewTableRowCount&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nv">$sqlConnection&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nv">$table&lt;/span>&lt;span class="p">){&lt;/span>
&lt;span class="nv">$sqlConnection&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">open&lt;/span>&lt;span class="p">();&lt;/span>
&lt;span class="nv">$RowCountCmd&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;select NewCount = count(*) from &amp;#34;&lt;/span> &lt;span class="p">+&lt;/span> &lt;span class="nv">$table&lt;/span>
&lt;span class="p">+&lt;/span> &lt;span class="s2">&amp;#34; where CreationDate &amp;gt;= &amp;#39;&amp;#34;&lt;/span>&lt;span class="p">+(&lt;/span>&lt;span class="nv">$i&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">ToString&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;yyyy-MM-dd&amp;#34;&lt;/span>&lt;span class="p">))&lt;/span>
&lt;span class="p">+&lt;/span>&lt;span class="s2">&amp;#34;&amp;#39; and OriginalLogTable = &amp;#39;Log_&amp;#34;&lt;/span>&lt;span class="p">+(&lt;/span>&lt;span class="nv">$i&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">ToString&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;yyyy_MM_dd&amp;#34;&lt;/span>&lt;span class="p">))+&lt;/span>&lt;span class="s2">&amp;#34;&amp;#39;&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;span class="nv">$SqlCmd&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="nb">New-Object&lt;/span> &lt;span class="n">System&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">Data&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">SqlClient&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">SqlCommand&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nv">$RowCountCmd&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nv">$sqlConnection&lt;/span>&lt;span class="p">)&lt;/span>
&lt;span class="nv">$SqlCmd&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">CommandTimeout&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;span class="nv">$row_count&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="no">[Int64]&lt;/span> &lt;span class="nv">$SqlCmd&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">ExecuteScalar&lt;/span>&lt;span class="p">()&lt;/span>
&lt;span class="nv">$sqlConnection&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">Close&lt;/span>&lt;span class="p">();&lt;/span>
&lt;span class="k">return&lt;/span> &lt;span class="nv">$row_count&lt;/span>
&lt;span class="p">}&lt;/span>
&lt;span class="nv">$cols&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;col1&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;col2&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;col3&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;...&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;span class="k">for&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nv">$i&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="nv">$StartDate&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="nv">$i&lt;/span> &lt;span class="o">-lt&lt;/span> &lt;span class="nv">$EndDate&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="nv">$i&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="nv">$i&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">AddDays&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">1&lt;/span>&lt;span class="p">))&lt;/span>
&lt;span class="p">{&lt;/span>
&lt;span class="nv">$MigrationStart&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="nb">Get-Date&lt;/span>
&lt;span class="nb">Write-Host&lt;/span> &lt;span class="s2">&amp;#34;Migration started at: $MigrationStart&amp;#34;&lt;/span>
&lt;span class="nv">$OldTableName&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;HAProxyLogs.dbo.Log_&amp;#34;&lt;/span>&lt;span class="p">+(&lt;/span>&lt;span class="nv">$i&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">ToString&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;yyyy_MM_dd&amp;#34;&lt;/span>&lt;span class="p">))&lt;/span>
&lt;span class="nv">$NewTableName&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;TrafficLogs.dbo.HAProxyLogs_&amp;#34;&lt;/span>&lt;span class="p">+(&lt;/span>&lt;span class="nv">$i&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">ToString&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;yyyy_MM&amp;#34;&lt;/span>&lt;span class="p">))&lt;/span>
&lt;span class="nb">Write-Host&lt;/span> &lt;span class="s2">&amp;#34;Attempting to migrate &amp;#34;&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nv">$i&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">ToString&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;yyyy_MM_dd&amp;#34;&lt;/span>&lt;span class="p">))&lt;/span>&lt;span class="s2">&amp;#34; from &amp;#34;&lt;/span> &lt;span class="nv">$OldTableName&lt;/span> &lt;span class="s2">&amp;#34; to &amp;#34;&lt;/span> &lt;span class="nv">$NewTableName&lt;/span>&lt;span class="p">;&lt;/span>
&lt;span class="c"># build the connection string and open it&lt;/span>
&lt;span class="nv">$SrcConnStr&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">ConnectionString&lt;/span> &lt;span class="nv">$ServerName&lt;/span>
&lt;span class="nv">$SrcConn&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="nb">New-Object&lt;/span> &lt;span class="n">System&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">Data&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">SqlClient&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">SQLConnection&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nv">$SrcConnStr&lt;/span>&lt;span class="p">)&lt;/span>
&lt;span class="c"># whats the row count on the original table in HAProxyLogs&lt;/span>
&lt;span class="nv">$originalRowCount&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">GetOriginalTableRowCount&lt;/span> &lt;span class="nv">$SrcConn&lt;/span> &lt;span class="nv">$OldTableName&lt;/span>
&lt;span class="nb">Write-Host&lt;/span> &lt;span class="s2">&amp;#34;Summary for &amp;#34;&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nv">$i&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">ToString&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;yyyy-MM-dd&amp;#34;&lt;/span>&lt;span class="p">))&lt;/span>&lt;span class="s2">&amp;#34;:
&lt;/span>&lt;span class="s2"> There are &amp;#34;&lt;/span>&lt;span class="nv">$originalRowCount&lt;/span>&lt;span class="s2">&amp;#34; rows in table &amp;#34;&lt;/span>&lt;span class="nv">$OldTableName&lt;/span>
&lt;span class="c"># make sure there is no data in the table currently for the day&lt;/span>
&lt;span class="nv">$newRowCount&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">GetNewTableRowCount&lt;/span> &lt;span class="nv">$SrcConn&lt;/span> &lt;span class="nv">$NewTableName&lt;/span>
&lt;span class="k">if&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nv">$newRowCount&lt;/span> &lt;span class="o">-gt&lt;/span> &lt;span class="n">0&lt;/span>&lt;span class="p">){&lt;/span>
&lt;span class="nb">Write-Host&lt;/span> &lt;span class="s2">&amp;#34;Rows found in the destination table - aborting migration&amp;#34;&lt;/span>
&lt;span class="k">break&lt;/span>&lt;span class="p">;&lt;/span>
&lt;span class="p">}&lt;/span>
&lt;span class="k">else&lt;/span> &lt;span class="p">{&lt;/span>
&lt;span class="nb">Write-Host&lt;/span> &lt;span class="s2">&amp;#34;No data for the date in the destination table - continuing migration&amp;#34;&lt;/span>
&lt;span class="p">}&lt;/span>
&lt;span class="c"># select the data we need from the original table&lt;/span>
&lt;span class="nv">$selectCmdText&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;select &amp;#34;&lt;/span>&lt;span class="p">+&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nv">$cols&lt;/span> &lt;span class="n">-join&lt;/span> &lt;span class="s1">&amp;#39;, &amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;span class="p">+&lt;/span>&lt;span class="s2">&amp;#34;, OriginalLogTable = &amp;#39;Log_&amp;#34;&lt;/span>&lt;span class="p">+(&lt;/span>&lt;span class="nv">$i&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">ToString&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;yyyy_MM_dd&amp;#34;&lt;/span>&lt;span class="p">))&lt;/span>
&lt;span class="p">+&lt;/span>&lt;span class="s2">&amp;#34;&amp;#39; from &amp;#34;&lt;/span>&lt;span class="p">+&lt;/span>&lt;span class="nv">$OldTableName&lt;/span>&lt;span class="p">;&lt;/span>
&lt;span class="nv">$SqlCommand&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="nb">New-Object&lt;/span> &lt;span class="n">system&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">Data&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">SqlClient&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">SqlCommand&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nv">$selectCmdText&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nv">$SrcConn&lt;/span>&lt;span class="p">)&lt;/span>
&lt;span class="nv">$SqlCommand&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">CommandTimeout&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;span class="nv">$SrcConn&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">Open&lt;/span>&lt;span class="p">()&lt;/span>
&lt;span class="no">[System.Data.SqlClient.SqlDataReader]&lt;/span> &lt;span class="nv">$SqlReader&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="nv">$SqlCommand&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">ExecuteReader&lt;/span>&lt;span class="p">()&lt;/span>
&lt;span class="nv">$destConnection&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">ConnectionString&lt;/span> &lt;span class="nv">$ServerName&lt;/span>
&lt;span class="nv">$bulkCopy&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="nb">New-Object&lt;/span> &lt;span class="n">Data&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">SqlClient&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">SqlBulkCopy&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nv">$destConnection&lt;/span>&lt;span class="p">)&lt;/span>
&lt;span class="nv">$bulkCopy&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">BatchSize&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">1048576&lt;/span>
&lt;span class="nv">$bulkCopy&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">BulkCopyTimeout&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">0&lt;/span> &lt;span class="c"># 20*60&lt;/span>
&lt;span class="nv">$bulkCopy&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">DestinationTableName&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="nv">$NewTableName&lt;/span>
&lt;span class="nv">$bulkCopy&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">NotifyAfter&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">500000&lt;/span>
&lt;span class="c"># taken from https://blog.netnerds.net/2015/05/getting-total-number-of-rows-copied-in-sqlbulkcopy-using-powershell/&lt;/span>
&lt;span class="nv">$bulkCopy&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">Add_SqlRowscopied&lt;/span>&lt;span class="p">(&lt;/span>
&lt;span class="p">{&lt;/span>&lt;span class="nb">Write-Host&lt;/span> &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">$(&lt;/span>&lt;span class="nv">$args&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">1&lt;/span>&lt;span class="p">].&lt;/span>&lt;span class="n">RowsCopied&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="s2">/$originalRowCount rows copied
&lt;/span>&lt;span class="s2"> from $OldTableName to $NewTableName&amp;#34;&lt;/span> &lt;span class="p">})&lt;/span>
&lt;span class="k">foreach&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nv">$col&lt;/span> &lt;span class="k">in&lt;/span> &lt;span class="nv">$cols&lt;/span>&lt;span class="p">){&lt;/span>
&lt;span class="nv">$bulkCopy&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">ColumnMappings&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">Add&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nv">$col&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="nv">$col&lt;/span>&lt;span class="p">)&lt;/span>
&lt;span class="p">}&lt;/span>
&lt;span class="k">try&lt;/span> &lt;span class="p">{&lt;/span>
&lt;span class="nb">Write-Host&lt;/span> &lt;span class="s2">&amp;#34;Starting Migration&amp;#34;&lt;/span>
&lt;span class="nv">$bulkCopy&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">ColumnMappings&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">Add&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;OriginalLogTable&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;OriginalLogTable&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;span class="nv">$bulkCopy&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">WriteToServer&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nv">$SqlReader&lt;/span>&lt;span class="p">)&lt;/span>
&lt;span class="nv">$total&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="no">[System.Data.SqlClient.SqlBulkCopyExtension]&lt;/span>&lt;span class="p">::&lt;/span>&lt;span class="n">RowsCopiedCount&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nv">$bulkcopy&lt;/span>&lt;span class="p">)&lt;/span>
&lt;span class="nb">Write-Host&lt;/span> &lt;span class="s2">&amp;#34;$total total rows written&amp;#34;&lt;/span>
&lt;span class="p">}&lt;/span>
&lt;span class="k">catch&lt;/span> &lt;span class="p">{&lt;/span>
&lt;span class="nv">$ex&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="nv">$_&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">Exception&lt;/span>
&lt;span class="nb">Write-Error&lt;/span> &lt;span class="nv">$ex&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">Message&lt;/span>
&lt;span class="k">While&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nv">$ex&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">InnerException&lt;/span>&lt;span class="p">)&lt;/span>
&lt;span class="p">{&lt;/span>
&lt;span class="nv">$ex&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="nv">$ex&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">InnerException&lt;/span>
&lt;span class="nb">Write-Error&lt;/span> &lt;span class="nv">$ex&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">Message&lt;/span>
&lt;span class="p">}&lt;/span>
&lt;span class="p">}&lt;/span>
&lt;span class="k">Finally&lt;/span>
&lt;span class="p">{&lt;/span>
&lt;span class="nv">$SqlReader&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">Close&lt;/span>&lt;span class="p">()&lt;/span>
&lt;span class="nv">$bulkCopy&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">Close&lt;/span>&lt;span class="p">()&lt;/span>
&lt;span class="nv">$SrcConn&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">Close&lt;/span>&lt;span class="p">()&lt;/span>
&lt;span class="p">}&lt;/span>
&lt;span class="c"># once done update statistics on the new table&lt;/span>
&lt;span class="nv">$statsCmdText&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;update statistics &amp;#34;&lt;/span>&lt;span class="p">+&lt;/span>&lt;span class="nv">$NewTableName&lt;/span>
&lt;span class="p">+&lt;/span>&lt;span class="s2">&amp;#34; HAProxyLogs_&amp;#34;&lt;/span>&lt;span class="p">+(&lt;/span>&lt;span class="nv">$i&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">ToString&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;yyyy_MM&amp;#34;&lt;/span>&lt;span class="p">))+&lt;/span>&lt;span class="s2">&amp;#34;_OriginalLogTable&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;span class="nb">Invoke-DbaQuery&lt;/span> &lt;span class="n">-SqlInstance&lt;/span> &lt;span class="nv">$ServerName&lt;/span> &lt;span class="n">-Query&lt;/span> &lt;span class="nv">$statsCmdText&lt;/span>&lt;span class="p">;&lt;/span>
&lt;span class="nb">Write-Host&lt;/span> &lt;span class="s2">&amp;#34;After migration there are $total rows
&lt;/span>&lt;span class="s2"> were written to $NewTableName table from $OldTableName&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;span class="k">if&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nv">$originalRowCount&lt;/span> &lt;span class="o">-ne&lt;/span> &lt;span class="nv">$total&lt;/span>&lt;span class="p">){&lt;/span>
&lt;span class="nb">Write-Host&lt;/span> &lt;span class="s2">&amp;#34;total rows migrated does not match. Stopping operation&amp;#34;&lt;/span>
&lt;span class="k">break&lt;/span>&lt;span class="p">;&lt;/span>
&lt;span class="p">}&lt;/span>
&lt;span class="nv">$total&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;span class="p">}&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;p>All of my options were slow.&lt;/p>
&lt;p>I ended up using the PowerShell option because I could easily run multiple months at once. I had 3 PowerShell sessions going, which let me chew through 3 months at a time. It was far easier to run several months in PowerShell than with the C# script. Utilizing PowerShell allowed me to get through a lot more data in a shorter amount of time. Don’t get me wrong it was still slow - each session was moving about a month of tables in about a week of work - but at least I was moving more data and chasing the target slightly faster.&lt;/p>
&lt;p>While the PowerShell script wasn’t perfect, and I still hit issues with timeouts and errors, it overall worked better. Now that I had a somewhat working process that didn’t need daily hand-holding, it was time to focus on some of the other problems with the migration.&lt;/p>
&lt;h2 id="continuous-issues-of-no-free-disk-space">Continuous Issues of No Free Disk Space&lt;/h2>
&lt;p>When your servers are already limited in free space and you have to move 40TB of data around on the same server, it becomes a little like &lt;a href="https://en.wikipedia.org/wiki/Whac-A-Mole">whack-a-mole&lt;/a> to get things moved. I had to get creative to free up space while continuing to move all the data to the new format.&lt;/p>
&lt;h3 id="deleting-and-shrinking">Deleting and Shrinking&lt;/h3>
&lt;p>Yes, you read that right. Shrinking. Before I get to that, let me explain why I needed to do this.&lt;/p>
&lt;p>We didn&amp;rsquo;t have new hardware yet and these were production SQL Servers - meaning the old database was still receiving new data and we were adding a new table everyday. As I was moving tables to the new database, we were adding tables with hundreds of millions of rows of data to the old database. This was all happening on the exact same server and drive with minimal free space.&lt;/p>
&lt;p>Hence the need to juggle.&lt;/p>
&lt;p>After moving each month, I would do a final validation, then it was time for some cleanup. This included deleting all of the daily tables for the month that were just migrated, as well as dropping the &lt;code>OriginalLogTable&lt;/code> column from the monthly table in the new database. The daily tables were no longer needed because we had the data in the new table, so getting rid of duplicate data would allow me to gain back disk space&amp;hellip;sort of.&lt;/p>
&lt;p>I say sort of, because I was just moving data on the same drives. We changed formats from daily tables to monthly tables, and while we got better compression in the new tables, there weren’t huge gains in free space. We had the same number of rows of data stored in fewer tables, which meant less bits in the long run, but in order to gain back the space allocated to the old data files, I had to shrink them.&lt;/p>
&lt;p>&lt;img src="https://tarynpivots.com/image/2020/scales.png#floatright" alt="Balancing Scales">&lt;/p>
&lt;p>As I moved the data from the old files, I was increasing the size of the new data files. This was all being done on the E: drive that was 85-90% full, which I mentioned above. I was constantly trying to keep the used disk space in check.&lt;/p>
&lt;p>It was quite a balancing act.&lt;/p>
&lt;p>Each month of data was anywhere from 300GB to 1TB in size, and the 3 old data files were approximately 12TB each. At the end of each migration, I tried to shrink the old database files by the amount we just deleted. This took several attempts to get something that worked well.&lt;/p>
&lt;p>Initially, I naively tried to run &lt;code>DBCC SHRINKFILE(FileName, TRUNCATEONLY)&lt;/code> to free up space. As I expected, that did not work.&lt;/p>
&lt;p>Next, I tried to run &lt;code>DBCC SHRINKFILE(FileName, CurrentFileSize - SizeOfDataJustDeleted)&lt;/code> on each file. That didn’t work either. Well to be clear, it would have if we had more drive space on the D: drive where the log file was stored. The D: drive was already about 85% full with tempdb, the HAProxyLogs log file, and another datafile. When you run &lt;code>DBCC SHRINKFILE&lt;/code> and let’s say you want to shrink a file by 300GB, then you &lt;!-- raw HTML omitted -->need to have the ability to increase your database transaction log file by the amount you’re trying to shrink&lt;!-- raw HTML omitted -->. Since we were already constrained on drive space where the t-log was stored, I wasn’t able to shrink the files by the amount I deleted because I kept running out of space on the D: drive.&lt;/p>
&lt;p>I had a couple of options to free up space on the D: drive - move &lt;code>tempdb&lt;/code> or move the other log/database files to our newly configured F: drive with 14TB of free space. I decided to move &lt;code>tempdb&lt;/code> because it was as easy as executing the following command and restarting the SQL service.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-sql" data-lang="sql">&lt;span class="k">alter&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">database&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">tempdb&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">modify&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">file&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">NAME&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">tempdev&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">FILENAME&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;F:\Data\tempdev.mdf&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">SIZE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">20000&lt;/span>&lt;span class="n">MB&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">alter&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">database&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">tempdb&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">modify&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">file&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">NAME&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">templog&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">FILENAME&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;F:\Data\templog.ldf&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">SIZE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">1000&lt;/span>&lt;span class="n">MB&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">alter&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">database&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">tempdb&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">modify&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">file&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">NAME&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">tempdev2&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">FILENAME&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;F:\Data\tempdev2.mdf&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">SIZE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">20000&lt;/span>&lt;span class="n">MB&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">alter&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">database&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">tempdb&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">modify&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">file&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">NAME&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">tempdev3&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">FILENAME&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;F:\Data\tempdev3.mdf&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">SIZE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">20000&lt;/span>&lt;span class="n">MB&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">alter&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">database&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">tempdb&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">modify&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">file&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">NAME&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">tempdev4&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">FILENAME&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;F:\Data\tempdev4.mdf&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">SIZE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">1000&lt;/span>&lt;span class="n">MB&lt;/span>&lt;span class="p">);&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;p>By moving &lt;code>tempdb&lt;/code> we had minimal downtime and I gained approximately 122GB in free space. While that gave me a little more room to shrink the old data files, it still wasn’t quite working as I wanted, so it was back to the drawing board.&lt;/p>
&lt;p>Trying to shrink the files in huge chunks of 300GB or more was too much, it was slow and resulted in blocking of the inserts into the old database. I found a &lt;a href="https://medium.com/@anna.f/shrink-oversized-data-files-in-microsoft-sql-server-53fb640f893e">solution that seemed promising&lt;/a> and would involve shrinking the files in much smaller bits in a loop, so I figured I’d try it. It took a little bit of poking to get the &lt;a href="https://twitter.com/tarynpivots/status/1095430853507788802">right number to shrink in every execution&lt;/a>, but I eventually ended up using something similar to this:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-sql" data-lang="sql">&lt;span class="k">declare&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="k">from&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">int&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">declare&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">leap&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">int&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">declare&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="k">to&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">int&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">declare&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">datafile&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">varchar&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">128&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">declare&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">cmd&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">varchar&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">512&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">declare&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">starttime&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">datetime&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">declare&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">endtime&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">datetime&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="cm">/*settings*/&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">set&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="k">from&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">13533200&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="cm">/*Current size in MB*/&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">set&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="k">to&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">11600000&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="cm">/*Goal size in MB*/&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">set&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">datafile&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;HAProxyLogs1&amp;#39;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="cm">/*Datafile name*/&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">set&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">leap&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">10000&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="cm">/*Size of leaps in MB*/&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="n">while&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">((&lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="k">from&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">leap&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="k">to&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">begin&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">set&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">starttime&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">getdate&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">print&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">concat&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;started: &amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">starttime&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">set&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="k">from&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="k">from&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">leap&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">set&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">cmd&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;DBCC SHRINKFILE (&amp;#39;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">datafile&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="s1">&amp;#39;, &amp;#39;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">cast&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="k">from&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">as&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">varchar&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">20&lt;/span>&lt;span class="p">))&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;)&amp;#39;&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">print&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">cmd&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">exec&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">cmd&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">print&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;==&amp;gt; SHRINK SCRIPT - &amp;#39;&lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">cast&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">((&lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="k">from&lt;/span>&lt;span class="o">-@&lt;/span>&lt;span class="k">to&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">as&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">varchar&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">20&lt;/span>&lt;span class="p">))&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;MB LEFT&amp;#39;&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">set&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">endtime&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">getdate&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">print&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">concat&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;ended: &amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">getdate&lt;/span>&lt;span class="p">(),&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39; took :&amp;#39;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">datediff&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">minute&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">starttime&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">endtime&lt;/span>&lt;span class="p">))&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">end&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">set&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">cmd&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;DBCC SHRINKFILE (&amp;#39;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">datafile&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="s1">&amp;#39;, &amp;#39;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">cast&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="k">to&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">as&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">varchar&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">20&lt;/span>&lt;span class="p">))&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;)&amp;#39;&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="n">print&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">cmd&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">exec&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">cmd&lt;/span>&lt;span class="p">)&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;p>I’d run the script against each file separately to fully recover the disk space from the month that was deleted. Again it was slow, but it worked.&lt;/p>
&lt;p>I’m aware of all the issues with shrinking database files, and no, I wouldn’t normally do it. However, shrinking the files was necessary to move the project forward, as I had to get around the lack of disk space.&lt;/p>
&lt;h3 id="just-use-the-new-drive">Just Use the New Drive&lt;/h3>
&lt;p>Now that I had a script to migrate the data relatively quickly (the PowerShell one), and had a script to shrink the old data files, you’d think I’d be all set to just run with this project, right?&lt;/p>
&lt;p>Not so fast.&lt;/p>
&lt;p>When I set up the new TrafficLogs database I put the data files for the archive (the old stuff being migrated) on the drive with the spinny disks. We didn’t think about using the new F: drive with 14TB of free SSD space to use, mainly because it wasn’t going to be enough for the entire migration. But after a few weeks, I decided to use that drive for a few reasons:&lt;/p>
&lt;ul>
&lt;li>They were NVMe SSDs. Which meant writing to them was much faster than writing to the the spinny drives&lt;/li>
&lt;li>It was about 14TB of free space, which let me move a huge amount of data without worrying about shrinking immediately. I could punt some of the whack-a-mole with the disk space.&lt;/li>
&lt;/ul>
&lt;p>Unfortunately, the only issue with doing this was once I filled the 14TB of disk space, I had to migrate the datafiles all back to the spinny drives. Which I did at the end of March 2019 after I ran out of drive space.&lt;/p>
&lt;p>Was this ideal? No. Was it a pain? Yes. But again, it moved the entire project forward and I was still slowly getting all the data migrated over.&lt;/p>
&lt;h3 id="drives-please-dont-die">Drives, Please Don&amp;rsquo;t Die&lt;/h3>
&lt;p>In early May 2019, I was still plugging away at migrating data. At this point, I was back to where I started - copying data from the old database on the spinny drives, inserting it into the new database, deleting the old data, and then shrinking the files all on the same drives.&lt;/p>
&lt;p>&lt;img src="https://tarynpivots.com/image/2020/edrive_slowness.png#floatright" alt="Read/Write Stats of Spinny Drives">&lt;/p>
&lt;p>After months of doing this, I was seeing a noticeable slowdown in the process. The read/write delays on the drives had increased since January. The &lt;a href="https://twitter.com/tarynpivots/status/1126581287471407104">average delays&lt;/a> of reads were over 100 milliseconds and about 80 milliseconds for writes.&lt;/p>
&lt;p>The &lt;a href="https://twitter.com/tarynpivots/status/1131620681794240513">overall response time&lt;/a> was terrible.&lt;/p>
&lt;p>I was struggling with &lt;a href="https://twitter.com/tarynpivots/status/1130566711088799744">&lt;code>PAGEIOLATCH_SH&lt;/code> waits&lt;/a> as I was trying to insert data into the new tables.&lt;/p>
&lt;p>We had been using the spinny drives for years, and I had been hitting them non-stop for about 4 months. I was &lt;a href="https://twitter.com/tarynpivots/status/1127320396619890688">very worried the drives were going to fail&lt;/a> before I was done migrating all of the data.&lt;/p>
&lt;p>Thankfully, they didn&amp;rsquo;t. I eventually was able to move all of the old data over to the new database. It only &lt;a href="https://twitter.com/tarynpivots/status/1144251803820810241">took 6 months to complete&lt;/a>.&lt;/p>
&lt;p>&lt;img src="https://tarynpivots.com/image/2020/traffliclog_tables.jpg" alt="Traffic Logs Done">&lt;/p>
&lt;p>I was done, but not really. We still didn’t have the new servers which meant every table created would still need to be migrated until we had them, and had cutover to the new servers and database.&lt;/p>
&lt;p>The moving target was still moving.&lt;/p>
&lt;h2 id="finally-new-hardware">Finally, New Hardware&lt;/h2>
&lt;p>The plan was to get the new servers in June/July, but the purchase was delayed several months. After respeccing them to have all NVMe SSD storage, they finally arrived in late September. To make the final migration of the new hardware as easy as possible, from June through the end of September, I moved one table a day, keeping things up to date.&lt;/p>
&lt;p>In early October, the new servers were ready for me to start installing SQL Server on them. The end was near.&lt;/p>
&lt;p>After lots of fussing with the Windows Server install, I eventually got installed SQL Server 2017 on both of them. It was time for the next obstacle moving the gigantic databases over.&lt;/p>
&lt;h3 id="backup-and-restore-failure">Backup and Restore Failure&lt;/h3>
&lt;p>The most obvious way to move a database from the old server to the new server is, of course, to take a backup and then restore it. So, that’s what &lt;a href="https://twitter.com/tarynpivots/status/1182779767621181440">I did&lt;/a>, or I should say, tried to do. I say tried because when I took the backup, I set the script to write it out on the new server, but unfortunately, &lt;a href="https://twitter.com/tarynpivots/status/1184804705844613125">the backup took most of the space&lt;/a> which meant I couldn’t restore it. It was a bit of a &lt;a href="https://en.wikipedia.org/wiki/Facepalm#:~:text=A%20facepalm%20is%20the%20physical,in%20contact%20with%20the%20face">facepalm moment&lt;/a>.&lt;/p>
&lt;p>We&amp;rsquo;re a very lean shop when it comes to hardware, and this is by far our largest chunk of data we have to store, so there wasn’t a place anywhere on the network to store a 33-38TB full backup temporarily, while it was being restored to a server.&lt;/p>
&lt;p>Time for more &lt;a href="https://twitter.com/tarynpivots/status/1184893381308084226">juggling&lt;/a>.&lt;/p>
&lt;h3 id="copying-tablesagain">Copying Tables&amp;hellip;Again&lt;/h3>
&lt;p>Since I wasn’t able to take a backup and restore it, I was back to square one, trying to figure out how to quickly move this data. After spending almost the entire year moving tables one by one, the idea of having to do it all over again pretty much drove me to tears, but that’s what I tried next.&lt;/p>
&lt;p>I would be moving from server to server within the same datacenter and rack, so I wanted to see how fast it would be to use various methods to bulk insert the data from the old database to the new one. I tried:&lt;/p>
&lt;p>&lt;img src="https://tarynpivots.com/image/2020/slow_migrations.jpg#floatright" alt="Slow Migration Time">&lt;/p>
&lt;ul>
&lt;li>&lt;a href="https://twitter.com/tarynpivots/status/1185194987488567297">PowerShell method&lt;/a> - took &lt;a href="https://twitter.com/tarynpivots/status/1185304145361637377">36 hours&lt;/a> to move one table with 4 billion rows&lt;/li>
&lt;li>SSIS version - took 2.5 days to move the same table&lt;/li>
&lt;li>&lt;a href="https://docs.microsoft.com/en-us/sql/t-sql/functions/openrowset-transact-sql?view=sql-server-ver15">&lt;code>OPENROWSET&lt;/code>&lt;/a> - took 16 hours to move 650 million rows&lt;/li>
&lt;/ul>
&lt;p>PowerShell was the winner when it came to speed, but Nick Craver wasn’t convinced. He decided to try it. He wrote a little LINQPad script and after running it &lt;a href="https://twitter.com/Nick_Craver/status/1186456942400737281">he finally believed me that moving this much data sucked&lt;/a>.&lt;/p>
&lt;p>At this point, &lt;a href="https://twitter.com/tarynpivots/status/1186465114754445313">I was devastated&lt;/a> because it looked like I was going to have to migrate everything for a second time.&lt;/p>
&lt;h3 id="time-to-try-an-availability-group">Time to Try an Availability Group&lt;/h3>
&lt;p>I really, really, really did not want to move hundreds of tables again, so I threw out the idea of using an &lt;a href="https://docs.microsoft.com/en-us/sql/database-engine/availability-groups/windows/overview-of-always-on-availability-groups-sql-server?view=sql-server-ver15">availability group&lt;/a> to &lt;a href="https://docs.microsoft.com/en-us/sql/database-engine/availability-groups/windows/automatically-initialize-always-on-availability-group?view=sql-server-ver15">automatically seed the databases&lt;/a> to the new servers. We already have AGs throughout our infrastructure, so I was familiar with using them, but I was unsure if it would successfully work for a database of this size.&lt;/p>
&lt;p>Before starting, I pinged &lt;a href="https://www.seangallardy.com/">Sean Gallardy&lt;/a> and asked if he thought it was possible to automatically seed a database of between 30-40TB - he guessed it&amp;rsquo;d be slow but possible, maybe taking 1-3 days. I was 100% ok with waiting 1-3 days, if it saved me months of moving tables again.&lt;/p>
&lt;p>I decided to spin up the availability group on the Colorado server first, and if it was successful, I&amp;rsquo;d move to New York. In order to set this up, I would need to go through all these steps:&lt;/p>
&lt;ul>
&lt;li>Set up new Windows Failover Cluster with the two servers&lt;/li>
&lt;li>Take another backup, as I couldn&amp;rsquo;t use the first one because the database was in &lt;a href="https://docs.microsoft.com/en-us/sql/relational-databases/backup-restore/recovery-models-sql-server?view=sql-server-ver15">simple recovery instead of full&lt;/a>. This meant waiting another &lt;a href="https://twitter.com/tarynpivots/status/1186819184249856000">11.5 hours&lt;/a> for the backup of the 33TB database in Colorado to finish&lt;/li>
&lt;li>Set up the AG and fingers crossed let it seed&lt;/li>
&lt;li>Initiate failover to the new server&lt;/li>
&lt;li>Destroy AG and shutdown old server&lt;/li>
&lt;/ul>
&lt;p>After backup in Colorado finished, I realized that we might have a small problem when trying to autoseed. The &lt;a href="ttps://docs.microsoft.com/en-us/sql/database-engine/availability-groups/windows/automatically-initialize-always-on-availability-group?view=sql-server-ver15">docs explain&lt;/a>:&lt;/p>
&lt;blockquote>
&lt;p>Automatic seeding requires that the data and log file paths are the same on every SQL Server instance participating in the availability group&lt;/p>
&lt;/blockquote>
&lt;p>The old servers had the data on E: and F:, and the new servers were using D:. To be certain there were no issues, I quickly renamed the drive on the new server to E: and moved the files over using:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-sql" data-lang="sql">&lt;span class="n">use&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">master&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="err">​&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">alter&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">database&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">TrafficLogs&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">modify&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">file&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">name&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">TrafficLogs_Current&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">filename&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;E:\Data\TrafficLogs_Current.mdf&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="err">​&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">alter&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">database&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">TrafficLogs&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">modify&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">file&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">name&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">TrafficLogs_log&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">filename&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;E:\Data\TrafficLogs_log.ldf&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;p>By changing the assigned letter to the drive, I also had to move &lt;code>tempdb&lt;/code> to the new drive, but once all that was done it was time to try setting up the availability group.&lt;/p>
&lt;p>I spun up the temporary AG in Colorado, waited and watched SQL Server. I was constantly checking both DMVs &lt;code>sys.dm_hadr_automatic_seeding&lt;/code> and &lt;code>sys.dm_hadr_physical_seeding_stats&lt;/code> to see the &lt;a href="https://docs.microsoft.com/en-us/sql/database-engine/availability-groups/windows/automatically-initialize-always-on-availability-group?view=sql-server-ver15#monitor-automatic-seeding-availability-group">status of the seeding process&lt;/a>. I was concerned it would fail and we&amp;rsquo;d be back to the drawing board. After about 6 hours, it looked like we had seeded about 17TB of the database.&lt;/p>
&lt;p>We also could see from our SignalFX monitoring of network traffic that something was happening. The SignalFX chart below shows we were averaging about 150 - 180 Mb/sec of traffic to a box that wasn&amp;rsquo;t being used by anything except for the database seeding.&lt;/p>
&lt;p>&lt;img src="https://tarynpivots.com/image/2020/co_log_transfer_inprogress.png" alt="Network Traffic Flowing">&lt;/p>
&lt;p>By the next morning, the seeding had finished and we had a database with all the tables on the new Colorado server. Progress!!&lt;/p>
&lt;p>&lt;img src="https://tarynpivots.com/image/2020/opserver_seeding_co.png" alt="We have tables">&lt;/p>
&lt;p>Before initiating the failover, I ran a &lt;a href="https://twitter.com/tarynpivots/status/1187516892170223616">&lt;code>DBCC CHECKDB&lt;/code>&lt;/a> on the new server aka the secondary in the availability group to be sure the database was in a good state, and after &lt;a href="https://twitter.com/tarynpivots/status/1187690141940244480">22 hours&lt;/a> it completed with no issues reported.&lt;/p>
&lt;p>It was time for the failover. I was a little nervous about doing it, but we were ecstatic, shocked, and impressed (all-in-one) that it worked.&lt;/p>
&lt;p>While all of this work was happening in Colorado, I kicked off &lt;a href="https://twitter.com/tarynpivots/status/1187179353425113089">the process on the New York server&lt;/a> since the database was larger, and I knew it was going to take a long time. After taking &lt;a href="https://twitter.com/tarynpivots/status/1187736811675668480">41 hours to backup the 40TB database&lt;/a>, I was ready to follow all the same steps on the New York server. I set up the cluster, availability group, seeded the database, ran a very long &lt;code>DBCC CHECKDB&lt;/code>, and finished with a successful failover.&lt;/p>
&lt;p>The hardest parts were done, but it wasn&amp;rsquo;t quite time to celebrate. I still had some final work to do.&lt;/p>
&lt;h3 id="final-cleanup">Final Cleanup&lt;/h3>
&lt;p>&lt;img src="https://tarynpivots.com/image/2020/fireworks.jpg#floatright" alt="Celebration Fireworks">&lt;/p>
&lt;p>We finally had the new servers in place with the databases, but we didn’t have the new traffic log data flowing to the new servers quite yet. The service that sends the traffic logs (Traffic Processing Service aka TPS) was still pointing to the old database. This was done purposely, to not interrupt any of the teams using the data. The idea was, once the servers were in place, we could side-by-side release the new version to push data in both places for a period of time (by sending syslog traffic from HAProxy to both servers). This would allow teams to port their processes to the new database and table structures in a gradual way.&lt;/p>
&lt;p>The week after the failover, we released the new traffic processing service and I did a few more days of data migration from the old server. I also destroyed the availability group and the windows failover clusters. Once all that was done, it was finally time to celebrate.&lt;/p>
&lt;h2 id="final-thoughts">Final Thoughts&lt;/h2>
&lt;p>During my time as the DBA at Stack Overflow, I’ve had the opportunity to work on various large projects (some documented &lt;a href="https://www.tarynpivots.com/post/how-we-upgraded-stackoverflow-to-sql-server-2017/">here&lt;/a> and &lt;a href="https://www.tarynpivots.com/post/how-stack-overflow-upgraded-from-windows-2012/">here&lt;/a>), but none have taken as long as this one. This took &lt;a href="https://twitter.com/tarynpivots/status/1191458709630672896">about 11 months&lt;/a> with all of the moving, validating, deleting, and juggling space.&lt;/p>
&lt;p>I honestly love taking on these big projects. Planning the entire thing, and taking it from start to finish is extremely gratifying. Even though I automated the majority of the work, this project was incredibly frustrating and I definitely don’t want to do it again anytime soon (unless I have plenty of drive space and don’t have to juggle things for 11 months). This project was challenging in many ways, and considering I had to do the same work on two servers it was doubly challenging. In the end, I was very relieved to successfully use an availability group to seed the databases to the new servers, and finally be done with moving all the things.&lt;/p>
&lt;p>Have you ever had to move multiple databases of this size? Were you lucky enough to have extra disk space to get it done? I’d be curious how others would have solved the problem of moving about 40TB of data (twice) with minimal free disk space? If you have thoughts, let me know.&lt;/p></description></item><item><title>SQL Server 2019 Tuple Mover Behavior Change</title><link>https://tarynpivots.com/post/sql-server-2019-tuple-mover-behavior-change/</link><pubDate>Mon, 20 Jul 2020 04:00:00 +0000</pubDate><guid>https://tarynpivots.com/post/sql-server-2019-tuple-mover-behavior-change/</guid><description>&lt;p>This is a follow-up to &lt;a href="https://www.tarynpivots.com/post/aggressive-clustered-columnstore-cleanup/">my post about an issue with clustered columnstore&lt;/a>, when upgrading from SQL Server 2017 to SQL Server 2019. After extensive testing and working with support, I wanted to share some information about a change in SQL Server 2019 that might impact others.&lt;/p>
&lt;h2 id="overview">Overview&lt;/h2>
&lt;p>I suggest reading &lt;a href="https://www.tarynpivots.com/post/aggressive-clustered-columnstore-cleanup/">my other post first&lt;/a>, it&amp;rsquo;ll only take a few minutes. I&amp;rsquo;ll wait&amp;hellip;&lt;/p>
&lt;p>However, if you really don&amp;rsquo;t want to read it, here&amp;rsquo;s a quick recap on the initial issue.&lt;/p>
&lt;p>In early February 2020, a lot of data was deleted from some &lt;a href="https://docs.microsoft.com/en-us/sql/relational-databases/indexes/columnstore-indexes-described?view=sql-server-2014">clustered columnstore indexes&lt;/a> in our PRIZM database. Some of the tables were rebuilt, but 11 tables weren&amp;rsquo;t since we don&amp;rsquo;t have maintenance windows, and that would involve downtime. The rebuilds would happen once we upgraded to SQL Server 2019, to take advantage of the ability to rebuild those columnstore indexes online.&lt;/p>
&lt;p>The day we upgraded to SQL Server 2019, there was rapid growth of the transaction logs for PRIZM. The only way to get the growth to stop, was to enable both &lt;a href="https://docs.microsoft.com/en-us/sql/t-sql/database-console-commands/dbcc-traceon-trace-flags-transact-sql?view=sql-server-ver15">trace flags 634 and 661&lt;/a>.&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Trace Flag&lt;/th>
&lt;th>Definition&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>634&lt;/td>
&lt;td>Disables the background columnstore compression task. SQL Server periodically runs the tuple mover background task that compresses columnstore index rowgroups with uncompressed data, one such rowgroup at a time.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>661&lt;/td>
&lt;td>Disables the ghost record removal process. For more information, see this &lt;a href="https://support.microsoft.com/kb/920093%22">Microsoft Support article.&lt;/a>&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>After working with support, we were able to remove trace flag 661, but still had 634 in place, so more work needed to be done.&lt;/p>
&lt;h2 id="enabling-another-trace-flag">Enabling Another Trace Flag&lt;/h2>
&lt;p>Removing trace flag 634 from the production server was the next step, but &lt;a href="https://www.tarynpivots.com/post/aggressive-clustered-columnstore-cleanup/#oh-tuple-mover-what-are-you-doing">every attempt&lt;/a>, led to the rapid increase in the transaction logs and a spike in query timeouts on the server.&lt;/p>
&lt;p>After passing lots of details to support, they requested we enable an undocumented trace flag - 10257 - to see if we still experienced the transaction log growth. Since this trace flag isn&amp;rsquo;t documented, the description we were given is:&lt;/p>
&lt;blockquote>
&lt;p>10257 - disables the columnstore tuple mover background merge process. The tuple mover background merge runs a reorganize on a few rowgroups at a time with more conservative thresholds.&lt;/p>
&lt;/blockquote>
&lt;p>Time for more testing!!&lt;/p>
&lt;p>I removed trace flag 634, and enabled trace flag 10257 to see how the transaction logs responded. The swap was done for about an hour and during this time the transaction logs didn&amp;rsquo;t have the rapid growth, but there still was an increase in query timeouts.&lt;/p>
&lt;p>The rowgroup states also changed:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Rowgroup State&lt;/th>
&lt;th>Before with TF 634 (only)&lt;/th>
&lt;th>After with TF 10257 (only)&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Compressed&lt;/td>
&lt;td>9844&lt;/td>
&lt;td>9927&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Closed&lt;/td>
&lt;td>261&lt;/td>
&lt;td>194&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Open&lt;/td>
&lt;td>721&lt;/td>
&lt;td>721&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Tombstone&lt;/td>
&lt;td>48&lt;/td>
&lt;td>68&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>The change in the numbers showed the tuple mover was running, but with the background merge process disabled there wasn&amp;rsquo;t the rapid growth of the logs (which was a good thing). However, we had to deal with query timeouts when trace flag 634 was removed. Now what?&lt;/p>
&lt;h2 id="more-investigating">More Investigating&lt;/h2>
&lt;p>I wanted a backup from before the upgrade to do two things:&lt;/p>
&lt;ol>
&lt;li>Check the state of the rowgroups before our initial upgrade. I was hoping to see how many were tombstones, open, closed, compressed?&lt;/li>
&lt;li>Could I recreate the issue by upgrading a server from SQL Server 2017 to SQL Server 2019?&lt;/li>
&lt;/ol>
&lt;p>It had been a month since we upgraded our SQL Servers, which meant our backups were only available offsite. After a &lt;a href="https://twitter.com/tarynpivots/status/1255883993670578176">little fighting&lt;/a>, I was finally able to pull it from storage.&lt;/p>
&lt;p>I initially restored the database to one of our existing test servers with SQL Server 2019 on it. I wanted to see the state of the rowgroups on the tables that had not been rebuilt. Did we have hundreds of tombstones or not? Unfortunately, after restoring the database &lt;a href="https://twitter.com/tarynpivots/status/1256012151447158784">it didn&amp;rsquo;t show me what I was looking for&lt;/a>. Based on a suggestion from Andy Mallon (&lt;a href="https://am2.co/">b&lt;/a> | &lt;a href="https://twitter.com/AMtwo">t&lt;/a>), I tried to &lt;a href="https://twitter.com/AMtwo/status/1256017346449342465">restore with &lt;code>STANDBY&lt;/code>&lt;/a> to see if that made any difference&amp;hellip;it didn&amp;rsquo;t. I was able to get stats, but it didn&amp;rsquo;t have the rowgroups in a tombstone state.&lt;/p>
&lt;p>Next, I decided to restore the database to a server with SQL Server 2017 on it, but since I already upgraded all of our servers to 2019, one wasn&amp;rsquo;t readily available. Thankfully, I have the ability to spin up test servers as needed, so that&amp;rsquo;s what I did.&lt;/p>
&lt;p>After restoring the database to the 2017 SQL Server, it had the following:&lt;/p>
&lt;ul>
&lt;li>No ghost records&lt;/li>
&lt;li>No TOMBSTONE rowgroups&lt;/li>
&lt;li>666 OPEN rowgroups&lt;/li>
&lt;li>0 closed rowgroups&lt;/li>
&lt;li>The transaction log was approximately 19.5 GB&lt;/li>
&lt;/ul>
&lt;p>Hmmm, ok there was nothing too concerning in the restored database. No red flags or alarm bells. It was time to upgrade to SQL Server 2019 to see what happens.&lt;/p>
&lt;p>Immediately after upgrading to SQL Server 2019, everything appeared ok. Then, I waited about 30 minutes to capture the stats again. After 30 minutes on 2019, we had:&lt;/p>
&lt;ul>
&lt;li>24k ghost records&lt;/li>
&lt;li>79 TOMBSTONE rowgroups&lt;/li>
&lt;li>666 OPEN rowgroups&lt;/li>
&lt;li>0 closed rowgroups&lt;/li>
&lt;li>and a transaction log that is 43GB&lt;/li>
&lt;/ul>
&lt;p>Initially, I didn&amp;rsquo;t have either trace flag (634, or 661) enabled. Once the trace flags were enabled, the growth of the transaction logs stopped. Welp, I had reproduced the issue.&lt;/p>
&lt;p>Support also asked me to grab the results of the following query when the database was still on SQL Server 2017, and again, after I upgraded to SQL Server 2019 to get the status of deleted rows:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-sql" data-lang="sql">&lt;span class="k">select&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">i&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">name&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">rg&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">object_id&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">rg&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">partition_number&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">rg&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">row_group_id&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">rg&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">delta_store_hobt_id&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">rg&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">state_desc&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">rg&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">total_rows&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">rg&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">deleted_rows&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">100&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">ISNULL&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">rg&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">deleted_rows&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">))&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">total_rows&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">AS&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;Fragmentation&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">rg&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">created_time&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">rg&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">closed_time&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">from&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">sys&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">dm_db_column_store_row_group_physical_stats&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">rg&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">inner&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">join&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">sys&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">indexes&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">i&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">on&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">rg&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">object_id&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">i&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">object_id&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">and&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">rg&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">index_id&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">i&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">index_id&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">order&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">by&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">i&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">name&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;p>I passed this info along to them, and we were finally able to put it all together.&lt;/p>
&lt;h2 id="putting-the-pieces-together">Putting the Pieces Together&lt;/h2>
&lt;p>Now that we had more data points, we could step back and look at the big picture, which allowed us to determine what caused the problem in the first place, and how to prevent it going forward.&lt;/p>
&lt;h3 id="the-root-cause">The Root Cause&lt;/h3>
&lt;p>The deletions we did in early February were the start of the problem. Without having maintenance windows, we were unable to rebuild the tables, leaving rowgroups in a highly fragmented state with a lot of deleted rows - many of them had 100% deletion. We didn&amp;rsquo;t see an issue in SQL Server 2017, because the tuple mover only triggered when a rowgroup closed and the tuple mover moved it to a compressed state.&lt;/p>
&lt;h3 id="behavior-change">Behavior Change&lt;/h3>
&lt;p>In SQL Server 2019, the tuple mover has a helper called the background merge task. The &lt;a href="https://docs.microsoft.com/en-us/sql/relational-databases/indexes/columnstore-indexes-query-performance?view=sql-server-ver15">docs have some details about it&lt;/a>:&lt;/p>
&lt;blockquote>
&lt;p>Starting with SQL Server 2019 (15.x), the tuple-mover is helped by a background merge task that automatically compresses smaller OPEN delta rowgroups that have existed for some time as determined by an internal threshold, or merges COMPRESSED rowgroups from where a large number of rows has been deleted. This improves the columnstore index quality over time.&lt;/p>
&lt;/blockquote>
&lt;blockquote>
&lt;p>If deleting large amounts of data from the columnstore index is required, consider splitting that operation into smaller delete batches over time, allowing the background merge task to handle the task of merging smaller rowgroups and improve index quality, eliminating the need to schedule index reorganization maintenance windows after data deletion.&lt;/p>
&lt;/blockquote>
&lt;p>In other words, the new background merge task has benefits, such as, helping the tuple mover clean up smaller rowgroups to improve the overall quality of the index. This task also automatically kicks in to merge compressed rowgroups where the number of deleted rows hits an internal threshold&amp;hellip;kinda sounds a little like what we had.&lt;/p>
&lt;h3 id="the-log-growth-issue">The Log Growth Issue&lt;/h3>
&lt;p>We experienced rapid transaction log growth the day we upgraded to SQL Server 2019 because the background merge task was triggered by the deleted rows we had in various tables. The log growth was due to the merge task trying to free the space in the rowgroup.&lt;/p>
&lt;p>Since we had a lot of rowgroups with deleted rows, the merge task was aggressively cleaning them up, which led to exponential growth of the logs. By enabling trace flag 634, we disabled the tuple mover process which stopped the logs from growing. If we wanted to keep the tuple mover enabled, then we&amp;rsquo;d need to use the new trace flag 10257 to disable the new background merge task.&lt;/p>
&lt;p>Initially, the new background merge task was extremely aggressive, which is why we saw so many query timeouts. There were a lot of rowgroups that needed cleaning, so the process ran all the time, resulting in contention with our normal load on the server.&lt;/p>
&lt;p>We never saw this behavior in any of our testing because we weren&amp;rsquo;t deleting data from our development environments.&lt;/p>
&lt;h3 id="the-resolution">The Resolution&lt;/h3>
&lt;p>In order to remove trace flag 634 from production, we had a couple of options:&lt;/p>
&lt;ol>
&lt;li>Disable the trace flag and let the tuple mover finish its business&lt;/li>
&lt;li>Rebuild the tables&lt;/li>
&lt;/ol>
&lt;p>We went with option 1.&lt;/p>
&lt;p>I disabled the trace flag and monitored the transaction log which took several hours to finish. During which, we experienced minimal log growth. We also saw query timeouts, but these dropped significantly as the number of rowgroups being processed by the background merge task steadily dropped to zero. Eventually, we had no rowgroups that needed to be cleaned up, which meant no further log growth, and no additional query timeouts from the tuple mover background merge task.&lt;/p>
&lt;p>We finally resolved this issue that lingered from the day we upgraded.&lt;/p>
&lt;h2 id="avoiding-this-issue">Avoiding this Issue&lt;/h2>
&lt;p>If you&amp;rsquo;re planning on moving to SQL Server 2019, and you have clustered columnstore indexes, and deleted data from them, then check the total number of deleted rows. If you have a lot, plan to perform maintenance before upgrading, or to utilize the trace flags to keep the tuple mover in check while cleaning the tables.&lt;/p>
&lt;p>Don&amp;rsquo;t be like us, especially if you delete a lot of data from your clustered columnstore indexes.&lt;/p>
&lt;p>A critical thing to remember with SQL Server 2019 is, if you need to delete data from your columnstore indexes, do it in smaller batches and trickle deletes. This allows the background merge task to perform the clean up slowly, and minimizes some of the pain we experienced when upgrading.&lt;/p>
&lt;p>As a reminder, before enabling any trace flags on your server (especially in production) make sure you know what you&amp;rsquo;re doing, or, you&amp;rsquo;re working with support and they suggest using it. The new background merge task is there to assist the tuple mover in keeping columnstore indexes in a better state. In other words, ideally, you&amp;rsquo;ll leave it alone and let it do its job. If you&amp;rsquo;re performing maintenance on your columnstore indexes, the background merge task should not negatively impact you, and should actually help with index quality.&lt;/p>
&lt;h2 id="final-thoughts">Final Thoughts&lt;/h2>
&lt;p>I tested SQL Server 2019 for months before we upgraded, and didn&amp;rsquo;t see this behavior at any point. But, as many of us know, upgrades are always fun. There is always a risk of issues when installing new versions. It doesn&amp;rsquo;t matter how much you test or plan, there&amp;rsquo;s always a possibility of something going wrong.&lt;/p>
&lt;p>I hope this helps someone who is thinking about upgrading, uses clustered columnstore, and doesn&amp;rsquo;t have an easy way to perform maintenance in their production environment. Check those deleted rows, and test, test, and test again, if you&amp;rsquo;re planning on upgrading.&lt;/p>
&lt;p>At some point soon, I&amp;rsquo;ll put pen to paper or fingers to keyboard and start writing about the upgrade to SQL Server 2019, along with what benefits we&amp;rsquo;ve gotten from it so far.&lt;/p>
&lt;p>I also would like to personally thank Pedro Lopes (&lt;a href="https://twitter.com/SQLPedro">t&lt;/a>), for helping with this issue and others we encountered after upgrading to SQL Server 2019, as well as reviewing this post.&lt;/p></description></item><item><title>SQL Server 2019 Aggressive Clustered Columnstore Cleanup</title><link>https://tarynpivots.com/post/aggressive-clustered-columnstore-cleanup/</link><pubDate>Fri, 24 Apr 2020 04:00:00 +0000</pubDate><guid>https://tarynpivots.com/post/aggressive-clustered-columnstore-cleanup/</guid><description>&lt;p>In late March 2020, we upgraded our production SQL Servers to &lt;a href="https://www.microsoft.com/en-us/sql-server/sql-server-2019">SQL Server 2019&lt;/a> with CU3. After finishing the upgrade, we hit an issue with &lt;a href="https://docs.microsoft.com/en-us/sql/relational-databases/indexes/columnstore-indexes-described?view=sql-server-2014">clustered columnstore&lt;/a> that we hadn&amp;rsquo;t experienced in the previous version of SQL, SQL Server 2017. The issue also wasn&amp;rsquo;t something we encountered during our extensive testing on various servers in development, which dated back to September 2019. The problem has been mitigated, but I wanted to share our experience.&lt;/p>
&lt;h2 id="background">Background&lt;/h2>
&lt;p>We have one database in production, called PRIZM, which is made up exclusively of tables with clustered columnstore indexes. PRIZM is used for event tracking, and it backs our A/B testing, feature funnels, etc. in hopes of improving most of what we do. Because we use PRIZM everywhere, it&amp;rsquo;s one of our larger databases with lots of tables. At the time of the upgrade, the database was approximately 500GB with about 1000 tables.&lt;/p>
&lt;p>There were various driving forces for it, but in early February we needed to purge data from PRIZM. Some of the reasons for doing so included:&lt;/p>
&lt;ul>
&lt;li>disk space being utilized&lt;/li>
&lt;li>general cleanup/removal of old events we no longer needed to maintain&lt;/li>
&lt;li>ease of GDPR compliance processing&lt;/li>
&lt;/ul>
&lt;p>We, or I should say &lt;a href="https://twitter.com/marcgravell">Marc Gravell&lt;/a>, deleted approximately 8 billion rows across 912 tables in the database. By doing this, we gained back drive space and removed data we no longer needed to keep.&lt;/p>
&lt;p>After performing the deletions, we did some of the recommended maintenance on columnstore indexes. While many of the smaller tables were rebuilt, a handful of tables (11 total) weren&amp;rsquo;t rebuilt due to their size. Since we don’t have maintenance windows, it would be high-impact to attempt these rebuilds on a very active production environment.&lt;/p>
&lt;p>The plan was to perform these table rebuilds after upgrading to 2019. SQL Server 2019 added the ability &lt;a href="http://www.nikoport.com/2018/06/21/columnstore-indexes-part-123-clustered-columnstore-index-online-rebuild/">to rebuild clustered columnstore indexes online&lt;/a> and that feature was one of the main reasons we wanted to upgrade.&lt;/p>
&lt;h2 id="upgrade-day">Upgrade Day&lt;/h2>
&lt;p>I&amp;rsquo;m not going to cover our full upgrade to 2019 - that will hopefully be a later post. At this point, let&amp;rsquo;s just assume everything went as planned and this is picking up after the main failovers of the servers that run Stack Overflow and the rest of the public network.&lt;/p>
&lt;p>After we finished the failovers for our public production servers, there was some behind the scenes work that needed to be done. This included patching and upgrading the former primary servers, and a failover of our reporting servers. The reporting cluster is a member of several &lt;a href="https://docs.microsoft.com/en-us/sql/database-engine/availability-groups/windows/distributed-availability-groups?view=sql-server-ver15">distributed availability groups&lt;/a>. When we&amp;rsquo;re doing upgrades, we move the primary server from New York (NY-RPTSQL01) to Colorado (CO-RPTSQL01). Once all the upstream SQL Servers are upgraded, there&amp;rsquo;s one last failover to get the primary back to NY-RPTSQL01. I&amp;rsquo;m specifically mentioning these servers because they receive data from PRIZM, so they are impacted by any activity in that database.&lt;/p>
&lt;h2 id="transaction-logs-exponentially-growing">Transaction Logs Exponentially Growing&lt;/h2>
&lt;p>As I was upgrading the former primaries, I noticed the transaction log files for PRIZM were growing. Initially, I just assumed it was normal and I went about finishing my other tasks.&lt;/p>
&lt;p>After about an hour, the log had grown again, and was growing rapidly. Like really rapidly, which was unusual. &lt;strong>The logs were growing about 10GB every 5 minutes.&lt;/strong> We have lower traffic on the weekend so the amount of data being inserted into the database should not have been anywhere near this level.&lt;/p>
&lt;p>Normally large transaction logs might raise my eyebrows, but I wouldn&amp;rsquo;t be super concerned. However, I needed to perform the failover of the reporting cluster, and with the logs increasing at the rate they were, it was going to make that more difficult, if not impossible.&lt;/p>
&lt;p>If you&amp;rsquo;re not familiar with availability groups, the transaction logs from the primary server need to be written to the secondary servers. This same thing happens with distributed availability groups, but at a larger scale. In this case the transaction logs for the databases, including PRIZM which is on our primary server for Stack Overflow, needed to be flushed to the reporting cluster.&lt;/p>
&lt;p>As the logs were increasing in PRIZM, they needed to be written to the reporting servers and CO-RPTSQL01 had a huge &lt;code>log send queue&lt;/code>, and NY-RPTSQL01 had a huge &lt;code>recovery queue&lt;/code>. Both of those are defined in the &lt;a href="https://docs.microsoft.com/en-us/previous-versions/sql/sql-server-2012/ff878356(v=sql.110)">Microsoft docs&lt;/a>, but I’ve included the definitions below:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Log send queue&lt;/strong> - Amount of log records in the log files of the primary database, in kilobytes, that has not yet been sent to the secondary replica. This value is sent to the secondary replica from the primary replica. Queue size does not include FILESTREAM files that are sent to a secondary.&lt;/li>
&lt;li>&lt;strong>Recovery queue&lt;/strong> - Amount of log records in the log files of the secondary replica that has not yet been redone.&lt;/li>
&lt;/ul>
&lt;p>The &lt;code>recovery queue&lt;/code> needs to be at zero to failover - when it&amp;rsquo;s zero it means the server is caught up to the primary. Since there were transaction logs that needed to be written to the secondary in the reporting cluster, NY-RPTSQL01, I couldn&amp;rsquo;t failover.&lt;/p>
&lt;h3 id="what-on-earth-is-happening">What on Earth is Happening?&lt;/h3>
&lt;p>When I first noticed the &lt;code>log send queue&lt;/code> size, it was about 90GB. Within just a few hours it was at 320GB, and growing.&lt;/p>
&lt;p>We knew normal traffic wasn&amp;rsquo;t causing the increase. We also knew there wasn’t a delete job that would have triggered the massive growth of transaction logs, especially at the rate we were seeing. Now it was time to try and figure out what was causing the rapid growth.&lt;/p>
&lt;p>I looked at our instance of &lt;a href="https://github.com/opserver/Opserver">OpServer&lt;/a> to see if it reported any queries running against PRIZM. Nothing.&lt;/p>
&lt;p>I executed &lt;a href="http://whoisactive.com/">&lt;code>sp_whoisactive&lt;/code>&lt;/a>. Nothing.&lt;/p>
&lt;p>From what I was seeing it appeared &amp;ldquo;something&amp;rdquo; was crawling through our CCI tables and processing &amp;ldquo;something&amp;rdquo;. Like all the hand-wavy &amp;ldquo;something was doing something&amp;rdquo;? Yeah me too. We didn&amp;rsquo;t know what was happening and I was frantically trying to get it to stop.&lt;/p>
&lt;p>I then executed &lt;code>sp_who2&lt;/code> and saw some PRIZM related things as background/system tasks, so I quickly wrote a script that would let me filter only for PRIZM:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-sql" data-lang="sql">&lt;span class="k">DECLARE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="k">Table&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">TABLE&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">SPID&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">INT&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Status&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">VARCHAR&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">MAX&lt;/span>&lt;span class="p">),&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">LOGIN&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">VARCHAR&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">MAX&lt;/span>&lt;span class="p">),&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">HostName&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">VARCHAR&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">MAX&lt;/span>&lt;span class="p">),&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">BlkBy&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">VARCHAR&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">MAX&lt;/span>&lt;span class="p">),&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">DBName&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">VARCHAR&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">MAX&lt;/span>&lt;span class="p">),&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Command&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">VARCHAR&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">MAX&lt;/span>&lt;span class="p">),&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">CPUTime&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">INT&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">DiskIO&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">INT&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">LastBatch&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">VARCHAR&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">MAX&lt;/span>&lt;span class="p">),&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ProgramName&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">VARCHAR&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">MAX&lt;/span>&lt;span class="p">),&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">SPID_1&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">INT&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">REQUESTID&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">INT&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">INSERT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">INTO&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="k">Table&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">EXEC&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">sp_who2&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">SELECT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">FROM&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="k">Table&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">WHERE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">DBName&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;PRIZM&amp;#39;&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;p>The output of &lt;code>sp_who2&lt;/code> was repeatedly showing &lt;code>GHOST CLEANUP&lt;/code> and &lt;code>CREATE INDEX&lt;/code>. Over and over and over again. To be clear, I’m not a clustered columnstore expert, I know enough to be able to maintain them as needed. I went to &lt;a href="https://twitter.com/tarynpivots/status/1243984519314558976">Twitter&lt;/a> and mentioned what I was seeing. I was advised by &lt;a href="https://twitter.com/sqL_handLe/status/1243988928484511746">@sqL_handLe&lt;/a> to try trace flag 661 which disables the ghost record removal process, and by Joe Obbish via &lt;a href="https://twitter.com/erikdarlingdata/status/1244005847140884480">Erik Darling&lt;/a> to enable trace flag 634 to disable the tuple mover background task.&lt;/p>
&lt;p>Initially, we enabled trace flag 634, but the logs continued to grow. We disabled trace flag 634. Then we enabled trace flag 661, and the logs continued to grow, so we disabled it. Finally, we tried enabling both of the trace flags. The big jumps stopped, but we now had about 400GB of logs that needed to be written to the reporting cluster before we could perform the failover.&lt;/p>
&lt;p>While the logs were exploding we wondered if whatever was happening might have been caused by the deletions we did in early February. But why would they be triggered by the upgrade to SQL Server 2019?&lt;/p>
&lt;p>We figured we could just let whatever was running, keep running, and maybe it&amp;rsquo;d eventually stop. However remember I mentioned one of the reasons we deleted a bunch of data from PRIZM was due to disk space being utilized? Well, the t-log growth was rapidly taking that back. The morning of the upgrade we were at 83% being used, as the day went on we were approaching 90%. This became an urgent issue because the growth was not slowing down and honestly we were worried we&amp;rsquo;d run out of disk space if we just let it play out.&lt;/p>
&lt;p>It was time to open a support ticket. Hopefully support could help us.&lt;/p>
&lt;p>A brief side note, before we enabled the trace flags we were getting lots of exceptions across the network. Once we enabled them, the exceptions slowed. Coincidence?&lt;/p>
&lt;h2 id="time-to-investigate">Time to Investigate&lt;/h2>
&lt;p>The next morning we still had logs that were being written to the reporting cluster, 195GB to be exact. Yes I know that as ridiculously high still, but we have throughput issues between our two datacenters (that’s a whole different story). Due to the size of the recovery queue that meant no failover until that flushed.&lt;/p>
&lt;p>We heard back from support, so it was time to investigate. They requested that we take a full dump file, which is pretty difficult since we have 1.5TB of memory on the Stack Overflow SQL Server and the C: drive isn&amp;rsquo;t large enough to capture that, so we went with a smaller filtered dump and 3 minidumps. That meant &lt;a href="https://twitter.com/tarynpivots/status/1244257820905627653">another public outage&lt;/a>, but we needed the files for them.&lt;/p>
&lt;p>When we took the dump files, we disabled both trace flags (634 and 661), during the &lt;a href="https://twitter.com/tarynpivots/status/1244408854768578561">10 minutes the trace flags were off, the transaction logs increased another 25-30GB&lt;/a>.&lt;/p>
&lt;h3 id="were-being-haunted-by-ghosts">We’re Being Haunted by Ghosts&lt;/h3>
&lt;p>After providing the dump files, they requested that we check the status of the ghost records in the database using and provide the results:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-sql" data-lang="sql">&lt;span class="k">SELECT&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">database_id&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">object_name&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">object_id&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">as&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Name&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">index_id&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">i&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">record_count&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">GHOST_RECORD_COUNT&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Version_ghost_record_count&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">INDEX_TYPE_DESC&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ALLOC_UNIT_TYPE_DESC&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">FROM&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">sys&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">dm_db_index_physical_stats&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">DB_ID&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">N&lt;/span>&lt;span class="s1">&amp;#39;PRIZM&amp;#39;&lt;/span>&lt;span class="p">),&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">object_id&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;&amp;lt;TableName&amp;gt;&amp;#39;&lt;/span>&lt;span class="p">),&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NULL&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NULL&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;DETAILED&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">i&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">ORDER&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">BY&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">GHOST_RECORD_COUNT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">desc&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;p>The results of the query were:&lt;/p>
&lt;p>&lt;img src="https://tarynpivots.com/image/2020/ghost_records_original.png" alt="Total Ghost Records">&lt;/p>
&lt;p>After running the query, I performed another test for support, which involved:&lt;/p>
&lt;ul>
&lt;li>Taking a t-log backup&lt;/li>
&lt;li>Removing both trace flags 634 and 661 allowing the logs to increase&lt;/li>
&lt;li>Enabling the trace flags&lt;/li>
&lt;li>Taking another log backup&lt;/li>
&lt;/ul>
&lt;p>Disabling the trace flags for just a few minutes resulted in the logs increasing rapidly, but in looking at the results from the query above, the total number of ghost records dropped.&lt;/p>
&lt;p>I decided to test further to see if disabling the trace flags would fully remove the &lt;code>GHOST_RECORD_COUNT&lt;/code>. Each time both trace flags were disabled, the total ghost records would drop and the t-logs would explode in size, but we weren&amp;rsquo;t sure which trace flag we should keep enabled, so it was time for yet another test.&lt;/p>
&lt;p>I tried just disabling trace flag 661, which would allow the ghost record process to run. I wanted to see if the cleanup process would finish, and the logs would stop growing. Eventually, the total number of ghost records dropped to zero, and the logs seemed to stop growing. Problem partially solved.&lt;/p>
&lt;h3 id="oh-tuple-mover-what-are-you-doing">Oh, Tuple Mover what are you doing?&lt;/h3>
&lt;p>Now that one of the trace flags was disabled, we had to deal with trace flag 634 which we did not have enabled on SQL Server 2017. I started querying the status of the rowgroups on each table and I found something that appeared odd. I ran the following query for a specific table in PRIZM:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-sql" data-lang="sql">&lt;span class="k">select&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">i&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">name&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">rg&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">object_id&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">rg&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">partition_number&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">rg&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">row_group_id&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">rg&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">delta_store_hobt_id&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">rg&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">state_desc&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">rg&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">total_rows&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">rg&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">deleted_rows&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">100&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">ISNULL&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">rg&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">deleted_rows&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">))&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">total_rows&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">AS&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;Fragmentation&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">rg&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">created_time&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">rg&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">closed_time&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">from&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">sys&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">dm_db_column_store_row_group_physical_stats&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">rg&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">inner&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">join&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">sys&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">indexes&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">i&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">on&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">rg&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">object_id&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">i&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">object_id&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">and&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">rg&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">index_id&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">i&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">index_id&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">order&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">by&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">i&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">name&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;p>The results showed a lot of rowgroups in a &lt;code>TOMBSTONE&lt;/code> state:&lt;/p>
&lt;p>&lt;img src="https://tarynpivots.com/image/2020/rowgroups.png" alt="TOMBSTONES">&lt;/p>
&lt;p>This was curious to me because, in my limited experience with clustered columnstore, these older closed rowgroups in a &lt;code>TOMBSTONE&lt;/code> state should have been cleaned up a long time ago, well before we upgraded to SQL Server 2019.&lt;/p>
&lt;p>On April 15th, I ran a test disabling trace flag 634. As soon as the trace flag was disabled, we experienced several things:&lt;/p>
&lt;ul>
&lt;li>transaction log growth&lt;/li>
&lt;li>a drop in the total number of &lt;code>TOMBSTONE&lt;/code> rowgroups&lt;/li>
&lt;li>and, a dramatic increase in query timeouts on the server&lt;/li>
&lt;/ul>
&lt;p>I left the trace flag disabled for several hours, all the while monitoring the 3 things above.&lt;/p>
&lt;p>About mid-day, I re-enabled the trace flag on the server. When I did that, the logs stopped growing, the total number of &lt;code>TOMBSTONE&lt;/code> rowgroups stopping decreasing, and the flood of query timeouts we saw throughout the entire test slowed to a trickle. All of this information was passed along to support. They suggested that the &lt;code>TUPLE_MOVER&lt;/code> doesn’t touch the &lt;code>TOMBSTONE&lt;/code> rowgroups and that should only be handled by the ghost cleanup process, but that was not the behavior we were seeing. They requested another test, but this time they also wanted us to keep track of the &lt;code>OPEN&lt;/code> and &lt;code>CLOSED&lt;/code> rowgroups.&lt;/p>
&lt;p>The next day, it was time to try this again. Prior to disabling trace flag 634, we had the following rowgroup states in the PRIZM database:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Total&lt;/th>
&lt;th>State&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>581&lt;/td>
&lt;td>TOMBSTONE&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>672&lt;/td>
&lt;td>OPEN&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>167&lt;/td>
&lt;td>CLOSED&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>I left the trace flag off for about 2 hours. While it was disabled, the transaction logs grew 136GB, and we also experienced significant &lt;code>CXPACKET&lt;/code> waits across the server which resulted in a lot of queries timing out.&lt;/p>
&lt;p>The total number of &lt;code>TOMBSTONE&lt;/code> row groups dropped during that time, eventually dropping to ~13 rowgroups. Once the &lt;code>TOMBSTONE&lt;/code> totals bottomed out, the number of &lt;code>CLOSED&lt;/code> rowgroups started to drop, and the total &lt;code>TOMBSTONE&lt;/code> rowgroups started to increase again. When the test was complete, the rowgroup state totals were:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Total&lt;/th>
&lt;th>State&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>48&lt;/td>
&lt;td>TOMBSTONE&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>672&lt;/td>
&lt;td>OPEN&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>131&lt;/td>
&lt;td>CLOSED&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>I could only run the test for about 2 hours due to the amount of exceptions we saw. The curious thing is that when we enable the trace flag again, the number of queries that timeout on significantly decreases and the amount of &lt;code>CXPACKET&lt;/code> waits we see also drops.&lt;/p>
&lt;h2 id="next-steps">Next Steps&lt;/h2>
&lt;p>We&amp;rsquo;re still working with support on this, but we need to do one of a two choices:&lt;/p>
&lt;ol>
&lt;li>
&lt;p>Disable trace flag 634 and let it just finish whatever it&amp;rsquo;s doing while monitoring the rowgroup states. Maybe it would eventually stop aggressively increasing the transaction logs, we’re not sure. We have not done this yet because having this backlog allows us to test other fixes, but it also just fixes the immediate issue, it doesn&amp;rsquo;t prevent us from hitting this issue again later if we need to perform another large delete in PRIZM.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Manually rebuild the 11 tables that were never rebuilt. I tried to do this online several times now that we&amp;rsquo;re on SQL Server 2019, but it results in blocking of data being inserted to PRIZM, which is a shame since we were hoping to use that feature. In order for us to rebuild, we&amp;rsquo;ll need to disable any of the processes that use PRIZM first, then rebuild each table, so that will be done at a later date.&lt;/p>
&lt;/li>
&lt;/ol>
&lt;p>We have some other issues in progress with SQL Server 2019, so I&amp;rsquo;m juggling those issues and this one. I&amp;rsquo;m hoping for a resolution soon on this, but before I forgot any of the details I wanted to share them.&lt;/p>
&lt;h3 id="final-thoughts">Final Thoughts&lt;/h3>
&lt;p>We still have a lot of outstanding questions on this, the biggest one being, why did the upgrade to SQL Server 2019 trigger what appears to be a massive cleanup of these clustered columnstore indexes? We didn&amp;rsquo;t have either trace flag enabled on SQL Server 2017, so there shouldn&amp;rsquo;t have been anything preventing the clean-up of the ghost records or the &lt;code>TOMBSTONE&lt;/code> rowgroups, so why did this happen immediately after we failed over to 2019? Is there something different about it being an in-place upgrade?&lt;/p>
&lt;p>If you have tested SQL Server 2019 and you use clustered columnstore indexes, have you seen this behavior? If so, what did you do to fix the issue? Did you just enable the trace flags and leave them on?&lt;/p>
&lt;p>And yes we were eventually able to failover the reporting cluster&amp;hellip;I know you were worried. :)&lt;/p>
&lt;p>A follow-up on the next steps has been posted &lt;a href="https://www.tarynpivots.com/post/sql-server-2019-tuple-mover-behavior-change/">here&lt;/a>.&lt;/p></description></item><item><title>A gotcha when upgrading to SQL Server 2019</title><link>https://tarynpivots.com/post/system-view-gotcha-with-sql-server-2019/</link><pubDate>Fri, 21 Feb 2020 06:00:38 +0000</pubDate><guid>https://tarynpivots.com/post/system-view-gotcha-with-sql-server-2019/</guid><description>&lt;p>In my &lt;a href="https://www.tarynpivots.com/post/recovering-lost-linked-servers/">last post&lt;/a>, I mentioned that I started the process of upgrading Stack Overflow to &lt;a href="https://docs.microsoft.com/en-us/sql/sql-server/what-s-new-in-sql-server-ver15?view=sql-server-ver15">SQL Server 2019&lt;/a>. This week I tackled our first production servers and after upgrading, we hit a small issue aka a gotcha because we were using an old system view. Below is a recap of what I encountered.&lt;/p>
&lt;h2 id="a-little-background">A Little Background&lt;/h2>
&lt;p>The servers I upgraded were the three SQL Servers that run &lt;a href="https://stackoverflow.com/teams">Stack Overflow for Teams&lt;/a>. For those not familiar with our setup, we use &lt;a href="https://docs.microsoft.com/en-us/sql/database-engine/availability-groups/windows/overview-of-always-on-availability-groups-sql-server?view=sql-server-ver15">availability groups&lt;/a> (AGs) across all of our main SQL Servers. This allows us to use the primary server for read/writes, and use the secondaries for a lot of the read-only traffic. Since we utilize both the primary and secondaries for read purposes, logins need to be the same across all of the servers in the AGs. In order to keep the logins in sync, we have a job that periodically runs on each server and creates new logins using dynamic SQL.&lt;/p>
&lt;h2 id="upgrade-day">Upgrade Day&lt;/h2>
&lt;p>Since I previously upgraded our servers to &lt;a href="https://www.tarynpivots.com/post/how-we-upgraded-stackoverflow-to-sql-server-2017/">SQL Server 2017&lt;/a>, I knew what to expect as I moved through the cluster. Once I started with the secondary servers the databases would &lt;a href="https://twitter.com/tarynpivots/status/1227676918083772416">not be accessible&lt;/a> until the failover was complete. After upgrading a secondary, I would see this if I tried to access a database:&lt;/p>
&lt;p>&lt;img src="https://tarynpivots.com/image/2020/database_unavailable.jpg" alt="Database not accessible">&lt;/p>
&lt;p>This also meant that any of the jobs touching the databases would fail, so I disabled SQL Agent jobs to minimize noisy alerts to my email. After the upgrade and failover, I&amp;rsquo;d enable all the jobs and be on my merry way.&lt;/p>
&lt;p>The upgrade of both secondary servers went totally as planned, without any issues. The evening rolled around and despite &lt;a href="https://twitter.com/Nick_Craver/status/1229933221565038592">Nick Craver&amp;rsquo;s popcorn&lt;/a>, the failover was successful, and we &lt;a href="https://twitter.com/tarynpivots/status/1229936342471036928">finally had SQL Server 2019&lt;/a> running in production.&lt;/p>
&lt;p>I upgraded the last server in the cluster (the former primary), and did some other clean-up tasks, including enabling all the SQL Agent jobs, before calling it a night. Once the jobs were enabled I started getting emails that one was failing — the Login Replication job.&lt;/p>
&lt;p>Something&amp;rsquo;s broken - time to investigate.&lt;/p>
&lt;h3 id="the-failing-job">The Failing Job&lt;/h3>
&lt;p>I pulled up the history on the job and saw the error:&lt;/p>
&lt;blockquote>
&lt;p>Message&lt;/p>
&lt;/blockquote>
&lt;blockquote>
&lt;p>Executed as user: &amp;lt;username&amp;gt;. Invalid value given for parameter PASSWORD. Specify a valid parameter value. [SQLSTATE 42000] (Error 15021). The step failed.&lt;/p>
&lt;/blockquote>
&lt;p>&lt;em>Sigh&lt;/em> ok, something is really broken because this was working before we failed over.&lt;/p>
&lt;p>The code for the login replication basically does the following via a cursor (yeah, I know, but it works&amp;hellip;normally):&lt;/p>
&lt;ol>
&lt;li>Select from the primary via &lt;code>OPENQUERY&lt;/code> to query the logins and passwords&lt;/li>
&lt;li>Using &lt;a href="https://support.microsoft.com/en-us/help/918992/how-to-transfer-logins-and-passwords-between-instances-of-sql-server">&lt;code>sp_hexadecimal&lt;/code>&lt;/a> convert the &lt;code>varbinary&lt;/code> password to a &lt;code>string&lt;/code> value&lt;/li>
&lt;li>Create a string to be executed, i.e. dynamic SQL that runs a &lt;code>CREATE LOGIN&lt;/code>&lt;/li>
&lt;/ol>
&lt;p>I have trimmed the code because it&amp;rsquo;s long, but the key parts query the login and password.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-sql" data-lang="sql">&lt;span class="k">SELECT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">FROM&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">OPENQUERY&lt;/span>&lt;span class="p">([&lt;/span>&lt;span class="k">SQL&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">AG&lt;/span>&lt;span class="p">],&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;
&lt;/span>&lt;span class="s1"> SELECT p.sid sid_varbinary, p.name, p.default_database_name,
&lt;/span>&lt;span class="s1"> CAST(l.password AS varbinary (256)) pwd_varbinary,
&lt;/span>&lt;span class="s1"> CASE sl.is_policy_checked WHEN 1 THEN &amp;#39;&amp;#39;ON&amp;#39;&amp;#39; WHEN 0 THEN &amp;#39;&amp;#39;OFF&amp;#39;&amp;#39; ELSE NULL END is_policy_checked,
&lt;/span>&lt;span class="s1"> CASE sl.is_expiration_checked WHEN 1 THEN &amp;#39;&amp;#39;ON&amp;#39;&amp;#39; WHEN 0 THEN &amp;#39;&amp;#39;OFF&amp;#39;&amp;#39; ELSE NULL END is_expiration_checked
&lt;/span>&lt;span class="s1"> FROM sys.server_principals p
&lt;/span>&lt;span class="s1"> JOIN sys.syslogins l ON l.name = p.name
&lt;/span>&lt;span class="s1"> JOIN sys.sql_logins sl ON l.name = sl.name
&lt;/span>&lt;span class="s1"> WHERE p.type = &amp;#39;&amp;#39;S&amp;#39;&amp;#39;
&lt;/span>&lt;span class="s1"> AND p.name &amp;lt;&amp;gt; &amp;#39;&amp;#39;sa&amp;#39;&amp;#39;
&lt;/span>&lt;span class="s1"> AND l.denylogin = 0
&lt;/span>&lt;span class="s1"> AND l.hasaccess = 1
&lt;/span>&lt;span class="s1"> AND p.is_disabled = 0
&lt;/span>&lt;span class="s1"> ORDER BY p.name&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;p>The value of &lt;code>pwd_varbinary&lt;/code> and &lt;code>sid&lt;/code> are then used with &lt;code>sp_hexadecimal&lt;/code>:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-sql" data-lang="sql">&lt;span class="k">EXEC&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">sp_hexadecimal&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">PWD_varbinary&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">PWD_string&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">OUT&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">EXEC&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">sp_hexadecimal&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">SID_varbinary&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">SID_string&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">OUT&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;p>Finally, we concatenate them into a SQL string that we execute:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-sql" data-lang="sql">&lt;span class="k">SET&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">sqlString&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;&amp;#39;&lt;/span>&lt;span class="k">CREATE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">LOGIN&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;&amp;#39;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">QUOTENAME&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">name&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;&amp;#39;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">WITH&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">PASSWORD&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;&amp;#39;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">PWD_string&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;&amp;#39;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">HASHED&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">SID&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;&amp;#39;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">SID_string&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">DEFAULT_DATABASE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;&amp;#39;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">defaultdb&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;&amp;#39;&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="s1">&amp;#39;&amp;#39;&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">CHECK_POLICY&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;&amp;#39;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">is_policy_checked&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">CHECK_EXPIRATION&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;&amp;#39;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">is_expiration_checked&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;p>When I ran the &lt;code>SELECT&lt;/code> statement, I noticed the issue — the value of &lt;code>pwd_varbinary&lt;/code> was &lt;code>null&lt;/code> which obviously was wrong, and since we were passing a &lt;code>null&lt;/code> as the password to &lt;code>sp_hexadecimal&lt;/code> the job was failing.&lt;/p>
&lt;p>Great, so now what?&lt;/p>
&lt;h3 id="the-fix">The Fix&lt;/h3>
&lt;p>We couldn&amp;rsquo;t just stop replicating the logins across all servers, due to our usage of the secondaries, and we needed to do this automatically on a regular interval. We had to figure out a solution. Thankfully the fix was easy.&lt;/p>
&lt;p>After seeing that the &lt;code>null&lt;/code> value of the password was coming from &lt;code>sys.syslogins&lt;/code>, and since we were already querying &lt;a href="https://docs.microsoft.com/en-us/sql/relational-databases/system-catalog-views/sys-sql-logins-transact-sql?view=sql-server-ver15">&lt;code>sys.sql_logins&lt;/code>&lt;/a> we could easily replace:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-sql" data-lang="sql">&lt;span class="k">CAST&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">l&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">password&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">AS&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">varbinary&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">256&lt;/span>&lt;span class="p">))&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">pwd_varbinary&lt;/span>&lt;span class="p">,&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;p>from &lt;code>sys.syslogins&lt;/code> with&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-sql" data-lang="sql">&lt;span class="n">sl&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">password_hash&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">pwd_varbinary&lt;/span>&lt;span class="p">,&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;p>from &lt;code>sys.sql_logins&lt;/code> (h/t to &lt;a href="https://twitter.com/Nick_Craver">Nick Craver&lt;/a>). With this one change to the query, the new &lt;code>SELECT&lt;/code> statement was:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-sql" data-lang="sql">&lt;span class="k">SELECT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">FROM&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">OPENQUERY&lt;/span>&lt;span class="p">([&lt;/span>&lt;span class="k">SQL&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">AG&lt;/span>&lt;span class="p">],&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;
&lt;/span>&lt;span class="s1"> SELECT p.sid sid_varbinary, p.name, p.default_database_name,
&lt;/span>&lt;span class="s1"> sl.password_hash pwd_varbinary,
&lt;/span>&lt;span class="s1"> CASE sl.is_policy_checked WHEN 1 THEN &amp;#39;&amp;#39;ON&amp;#39;&amp;#39; WHEN 0 THEN &amp;#39;&amp;#39;OFF&amp;#39;&amp;#39; ELSE NULL END is_policy_checked,
&lt;/span>&lt;span class="s1"> CASE sl.is_expiration_checked WHEN 1 THEN &amp;#39;&amp;#39;ON&amp;#39;&amp;#39; WHEN 0 THEN &amp;#39;&amp;#39;OFF&amp;#39;&amp;#39; ELSE NULL END is_expiration_checked
&lt;/span>&lt;span class="s1"> FROM sys.server_principals p
&lt;/span>&lt;span class="s1"> JOIN sys.syslogins l ON l.name = p.name
&lt;/span>&lt;span class="s1"> JOIN sys.sql_logins sl ON l.name = sl.name
&lt;/span>&lt;span class="s1"> WHERE p.type = &amp;#39;&amp;#39;S&amp;#39;&amp;#39;
&lt;/span>&lt;span class="s1"> AND p.name &amp;lt;&amp;gt; &amp;#39;&amp;#39;sa&amp;#39;&amp;#39;
&lt;/span>&lt;span class="s1"> AND l.denylogin = 0
&lt;/span>&lt;span class="s1"> AND l.hasaccess = 1
&lt;/span>&lt;span class="s1"> AND p.is_disabled = 0
&lt;/span>&lt;span class="s1"> ORDER BY p.name&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;p>Once it was updated, the job successfully ran, and our logins were replicating again without issue. Now, I really called it a night.&lt;/p>
&lt;h2 id="ok-but-what-was-the-gotcha">Ok, but what was the gotcha?&lt;/h2>
&lt;p>Remember I said that this was working fine on SQL Server 2017?&lt;/p>
&lt;p>When we started looking for solutions, I looked up the Microsoft Docs for &lt;a href="https://docs.microsoft.com/en-us/sql/relational-databases/system-compatibility-views/sys-syslogins-transact-sql?view=sql-server-ver15">&lt;code>sys.syslogins&lt;/code>&lt;/a> and right at the top of the doc it says:&lt;/p>
&lt;blockquote>
&lt;p>This SQL Server 2000 system table is included as a view for backward compatibility. We recommend that you use the current SQL Server system views instead. To find the equivalent system view or views, see &lt;a href="https://docs.microsoft.com/en-us/sql/relational-databases/system-tables/mapping-system-tables-to-system-views-transact-sql?view=sql-server-ver15">Mapping System Tables to System Views (Transact-SQL)&lt;/a>. This feature will be removed in a future version of Microsoft SQL Server. Avoid using this feature in new development work, and plan to modify applications that currently use this feature.&lt;/p>
&lt;/blockquote>
&lt;p>The doc shows the description and value of &lt;code>password&lt;/code>:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Column&lt;/th>
&lt;th>Data type&lt;/th>
&lt;th>Description&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>password&lt;/td>
&lt;td>nvarchar(128)&lt;/td>
&lt;td>Returns NULL.&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>Hmm, that&amp;rsquo;s weird because the &lt;code>password&lt;/code> column contains an actual value in SQL Server 2017. Something obviously changed. Yes, we were risky because we were using an old view, but the important thing is the value in the &lt;code>password&lt;/code> column changed, and it bit us.&lt;/p>
&lt;p>In SQL Server 2017, there is still a value for &lt;code>password&lt;/code> in &lt;code>sys.syslogins&lt;/code>, but in SQL Server 2019 it is now &lt;code>null&lt;/code>.&lt;/p>
&lt;p>If this was mentioned somewhere, I missed it. Thankfully, this wasn&amp;rsquo;t a critical job otherwise it could have been more problematic. We don&amp;rsquo;t have the Login Replication job running in development, so we didn&amp;rsquo;t hit the issue until we moved to production.&lt;/p>
&lt;p>Technically it&amp;rsquo;s on us because we were using an old system view and didn&amp;rsquo;t check for changes, but keep in mind if you&amp;rsquo;re using the &lt;code>sys.syslogins&lt;/code> view anywhere, and rely on the &lt;code>password&lt;/code>, you&amp;rsquo;ll need to make a code update before moving to SQL Server 2019.&lt;/p>
&lt;p>If you&amp;rsquo;ve upgraded to SQL Server 2019, have you hit any of these issues yet?&lt;/p></description></item><item><title>Recovering Lost Linked Servers</title><link>https://tarynpivots.com/post/recovering-lost-linked-servers/</link><pubDate>Thu, 06 Feb 2020 06:00:38 +0000</pubDate><guid>https://tarynpivots.com/post/recovering-lost-linked-servers/</guid><description>&lt;p>Recently, I kicked off a project to start moving us to &lt;a href="https://docs.microsoft.com/en-us/sql/sql-server/what-s-new-in-sql-server-ver15?view=sql-server-ver15">SQL Server 2019&lt;/a>. During my initial review of our servers, I found quite a few (9 total) that were still running on Windows Server 2012 R2. This meant that I would need to upgrade the operating system and move us to SQL Server 2019. Having completed plenty of &lt;a href="https://www.tarynpivots.com/post/how-we-upgraded-stackoverflow-to-sql-server-2017/">SQL Server&lt;/a> upgrades, as well as &lt;a href="https://www.tarynpivots.com/post/how-stack-overflow-upgraded-from-windows-2012/">operating system upgrades&lt;/a>, I couldn&amp;rsquo;t possibly make a mistake, right? Wrong&amp;hellip;I completely forgot to script out the linked servers on the server I upgraded this week. I screwed up and decided to write about how I went about fixing it.&lt;/p>
&lt;h2 id="my-plan">My Plan&lt;/h2>
&lt;p>This week, I &lt;a href="https://meta.stackexchange.com/q/342991/164200">tackled the server&lt;/a> running &lt;a href="https://data.stackexchange.com/">Stack Exchange Data Explorer (SEDE)&lt;/a>. In the past, when I have upgraded an operating system on a SQL Server I work off a list:&lt;/p>
&lt;ol>
&lt;li>Take a backup of all system databases - if I&amp;rsquo;m reinstalling the same version of SQL Server, I could restore the &lt;code>master&lt;/code>, &lt;code>model&lt;/code>, and &lt;code>msdb&lt;/code> databases to minimize some of my setup again&lt;/li>
&lt;li>Script out any objects in the &lt;code>master&lt;/code> that we need, this would be tables and stored procedures. We store objects for maintenance purposes in &lt;code>master&lt;/code>&amp;hellip;please don&amp;rsquo;t scream at me.&lt;/li>
&lt;li>Generate creation scripts for all jobs&lt;/li>
&lt;li>Export logins and users from the server&lt;/li>
&lt;li>Make sure I have everything else I need scripted before kicking off a rebuild&lt;/li>
&lt;/ol>
&lt;h2 id="upgrade-day">Upgrade Day&lt;/h2>
&lt;p>Before starting the rebuild, I did one final update of SQL Server 2017 to bring it to the latest patch we were using, &lt;a href="https://support.microsoft.com/en-us/help/4527377/cumulative-update-18-for-sql-server-2017">CU 18&lt;/a>. This was to make sure it was fully up-to-date before I rebuilt the server.&lt;/p>
&lt;p>You might be curious why I patched SQL Server 2017 when we were going to SQL Server 2019? I did this because the server that runs data explorer has a lot of specific permissions, and it also has about 350 databases. My initial goal was to restore the system databases to minimize the risk of missing a permission or something in the shuffle when setting up the new server. Once the system databases were restored, I&amp;rsquo;d upgrade to SQL Server 2019.&lt;/p>
&lt;p>After patching SQL Server, I checked all the steps above, and thought I had everything I needed to recreate the server, so I kicked off the rebuild. About 30 minutes into the process, I had a total facepalm moment and realized I forgot to script the linked servers. It was too late to do anything about it at that time, but thankfully I had the system databases which would save me. Once Windows Server 2016 was done installing, it was time to install SQL Server 2017 and then &lt;em>attempt&lt;/em> the restore of the system databases.&lt;/p>
&lt;p>I say &lt;em>attempt&lt;/em> to restore the system databases because while I&amp;rsquo;ve restored the &lt;code>master&lt;/code> database before, &lt;a href="https://twitter.com/tarynpivots/status/1225131722397741056">it&amp;rsquo;s not fun&lt;/a>, or at least, not my definition of fun. The biggest pain point I have is getting the single connection to the server. I know how to put the server in &lt;a href="https://docs.microsoft.com/en-us/sql/relational-databases/backup-restore/restore-the-master-database-transact-sql?view=sql-server-ver15">single user mode to restore the &lt;code>master&lt;/code> database&lt;/a>, but at Stack Overflow we have a lot of monitoring systems that are actively checking the server status. Unless I disabled all those logins, it&amp;rsquo;d be nearly impossible to get that single connection.&lt;/p>
&lt;p>I opened up a command window and started going through all the steps to restore the &lt;code>master&lt;/code> database. I executed:&lt;/p>
&lt;pre>&lt;code>net stop MSSQLSERVER
net start MSSQLSERVER /m
&lt;/code>&lt;/pre>
&lt;p>Then, I opened another command window and connected to the instance by using:&lt;/p>
&lt;pre>&lt;code>sqlcmd -s .\MSSQLSERVER
&lt;/code>&lt;/pre>
&lt;p>When I hit go, I received the dreaded error:&lt;/p>
&lt;blockquote>
&lt;p>Login failed for user &lt;code>&amp;lt;xxx&amp;gt;&lt;/code> Reason: Server is in single user mode. Only one administrator can connect at this time.&lt;/p>
&lt;/blockquote>
&lt;p>Boo. I tried connecting a few more times and each time I couldn&amp;rsquo;t get the single connection. I disabled some logins for our monitoring services and couldn&amp;rsquo;t connect to the instance because something was getting the single connection first. I may also have been hitting my head on my desk out of frustration. After fighting with it for far longer than I&amp;rsquo;m going to admit, I gave up. I knew I was going to just have to rebuild the server from my scripts, and attempt to recreate the linked servers from scratch. Knowing it was inevitable, I went ahead and proceeded with the upgrade to SQL Server 2019. When that was done, I executed all my scripts, got the databases back in place, and released &lt;a href="https://twitter.com/tarynpivots/status/1225164597822246912">data explorer&lt;/a> back to the world.&lt;/p>
&lt;p>The only things still missing were the linked servers. While I was happy to have moved the SQL upgrades along, I was &lt;a href="https://twitter.com/tarynpivots/status/1225216451746775042">very angry with myself for missing that script&lt;/a> in the first place. The server had not received much love, and I didn&amp;rsquo;t have the linked server, which was created long before me, scripted out and uploaded to GitHub (yeah, I know that was also dumb). I setup the linked servers by hand but couldn&amp;rsquo;t get them to connect. After a very long day I knew I wouldn&amp;rsquo;t need the linked server until the weekend, so I left it to be fixed the next morning.&lt;/p>
&lt;h2 id="recovering-the-lost-linked-servers">Recovering the Lost Linked Servers&lt;/h2>
&lt;p>The next day, I had to get the linked servers working. My attempts to create them by hand failed miserably. I had the system backups from before the upgrade, so I wanted to try to get the details out of them. I did several things that didn&amp;rsquo;t work, including restoring the &lt;code>master&lt;/code> database to a different server, under a different name (i.e. &lt;code>master_old&lt;/code>) and trying to query the linked server info.&lt;/p>
&lt;p>Finally, I decided to create a new test server, install SQL Server 2017, and restore the &lt;code>master&lt;/code> database to it. I setup a new build process to create a VM with a single disk drive, with just enough space for Windows and SQL to be installed on it. I installed SQL Server 2017 to the same CU I needed, and it was time to try the restore again. My thinking was, I could pretty much do anything to this SQL instance in order to get to the linked server data I wanted.&lt;/p>
&lt;p>Using the same commands as above, I set the instance to single user mode, and then attempted to connect to the instance. No error message this time. I was in the instance as the sole user&amp;hellip;success. Now that I was connected it was time to restore:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-sql" data-lang="sql">&lt;span class="n">RESTORE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">DATABASE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">master&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">FROM&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">DISK&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;C:\Backups\master.bak&amp;#39;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">WITH&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">REPLACE&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;p>I received no errors. It looked like everything worked. I opened up the SQL Server Configuration Manager, started the SQL service, and refreshed the page. The SQL service stopped. Uh oh, something is wrong. I went to the error log and popped it open. There was a giant error:&lt;/p>
&lt;blockquote>
&lt;p>2020-02-06 16:42:24.74 spid9s Starting up database &amp;lsquo;model&amp;rsquo;.&lt;/p>
&lt;/blockquote>
&lt;blockquote>
&lt;p>2020-02-06 16:42:24.74 spid9s Error: 17204, Severity: 16, State: 1.&lt;/p>
&lt;/blockquote>
&lt;blockquote>
&lt;p>2020-02-06 16:42:24.74 spid9s FCB::Open failed: Could not open file C:\Program Files\Microsoft SQL Server\MSSQL12.MSSQLSERVER\MSSQL\DATA\model.mdf for file number 1. OS error: 3(The system cannot find the path specified.).&lt;/p>
&lt;/blockquote>
&lt;blockquote>
&lt;p>2020-02-06 16:42:24.74 spid9s Database &amp;lsquo;model&amp;rsquo; cannot be opened due to inaccessible files or insufficient memory or disk space. See the SQL Server errorlog for details.&lt;/p>
&lt;/blockquote>
&lt;blockquote>
&lt;p>2020-02-06 16:42:24.74 spid9s SQL Server shutdown has been initiated&lt;/p>
&lt;/blockquote>
&lt;blockquote>
&lt;p>2020-02-06 16:42:24.74 spid9s SQL Trace was stopped due to server shutdown. Trace ID = &amp;lsquo;1&amp;rsquo;. This is an informational message only; no user action is required.&lt;/p>
&lt;/blockquote>
&lt;p>I saw the issue right away. The server was looking for the &lt;code>model&lt;/code> database in the &lt;code>MSSQL12.MSSQLSERVER&lt;/code> directory, but that didn&amp;rsquo;t exist any longer. The old server had it&amp;rsquo;s files in the directory for SQL Server 2014 aka MSSQL12, but on this new server everything is located in the directory for &lt;code>MSSQL14.MSSQLSERVER&lt;/code>. As I mentioned, I didn&amp;rsquo;t really care what I did to this test machine, so I manually created the directory that it was looking for - &lt;code>C:\Program Files\Microsoft SQL Server\MSSQL12.MSSQLSERVER\MSSQL\DATA&lt;/code> and made sure that the SQL user account had access to the directory. Then I just copied and pasted the &lt;code>model&lt;/code> database from &lt;code>MSSQL14.MSSQLSERVER&lt;/code> and dropped it into the &lt;code>MSSQL12.MSSQLSERVER&lt;/code> directory.&lt;/p>
&lt;p>I attempted to start the SQL service, and again it failed. Back to the error log, I went. This time the error was:&lt;/p>
&lt;blockquote>
&lt;p>2020-02-06 16:48:42.11 spid9s Clearing tempdb database.&lt;/p>
&lt;/blockquote>
&lt;blockquote>
&lt;p>2020-02-06 16:48:42.11 spid9s Error: 5123, Severity: 16, State: 1.&lt;/p>
&lt;/blockquote>
&lt;blockquote>
&lt;p>2020-02-06 16:48:42.11 spid9s CREATE FILE encountered operating system error 3(The system cannot find the path specified.) while attempting to open or create the physical file &amp;lsquo;D:\Data\tempdb.mdf&amp;rsquo;.&lt;/p>
&lt;/blockquote>
&lt;blockquote>
&lt;p>2020-02-06 16:48:42.12 spid9s Could not create tempdb. You may not have enough disk space available. Free additional disk space by deleting other files on the tempdb drive and then restart SQL Server. Check for additional errors in the operating system error log that may indicate why the tempdb files could not be initialized.&lt;/p>
&lt;/blockquote>
&lt;blockquote>
&lt;p>2020-02-06 16:48:42.12 spid9s SQL Server shutdown has been initiated&lt;/p>
&lt;/blockquote>
&lt;blockquote>
&lt;p>2020-02-06 16:48:42.12 spid9s SQL Trace was stopped due to server shutdown. Trace ID = &amp;lsquo;1&amp;rsquo;. This is an informational message only; no user action is required.&lt;/p>
&lt;/blockquote>
&lt;p>There was some progress because it was a different error, right?&lt;/p>
&lt;p>Now it&amp;rsquo;s complaining because I don&amp;rsquo;t have a &lt;code>D:\&lt;/code> drive on my test instance. I built the server with a single drive, but luckily I can easily fix that for a VM. I logged into our VM platform, quickly added a tiny new disk drive, then formatted it on my test server. Once the &lt;code>D:\&lt;/code> drive was available, I created &lt;code>D:\Data&lt;/code> and granted permission to the SQL user account to access the directory.&lt;/p>
&lt;p>It was time to go back to the SQL Server Configuration Manager to start the service. As I kicked the service to start, I kept thinking &amp;lsquo;please work, please work&amp;rsquo;. This time the service started and stayed running. I logged into SSMS, connected to the instance, and went looking for my Linked Servers.&lt;/p>
&lt;p>Guess what? They were there. Somehow it worked. I was able to script out both linked servers and execute them where they belong. After some testing, I confirmed that the linked servers were working on the original server I built.&lt;/p>
&lt;p>It&amp;rsquo;s quite possible that there are other ways to recover something like this. I&amp;rsquo;m also &lt;a href="https://twitter.com/tarynpivots/status/1225475415822589952">relieved&lt;/a> this worked and I know I&amp;rsquo;m lucky to have the tools to be able to quickly spin up a new test SQL Server to hack away on. I learned a valuable lesson - verify I have everything that I need when rebuilding servers.&lt;/p>
&lt;p>Oh, and by the way, yes I did upload those linked server creation scripts to GitHub.&lt;/p></description></item><item><title>The Perils of Querying SQL Server Replicas Under Load</title><link>https://tarynpivots.com/post/perils-querying-sql-replica-under-load/</link><pubDate>Wed, 15 Jan 2020 06:00:38 +0000</pubDate><guid>https://tarynpivots.com/post/perils-querying-sql-replica-under-load/</guid><description>&lt;p>Last week at Stack Overflow we had an internal hack-a-thon, or as we call it, a make-a-thon. I was on the bug-bashing team, which is the team that attempts to fix smallish bugs we haven’t gotten around to fixing, due to other time-constraints. I was &lt;a href="https://meta.stackoverflow.com/q/384675/426671">tagged to investigate a bug&lt;/a> about duplicate badges being awarded because it looked to possibly be an easy fix in SQL. At first glance it looked simple enough, but once I started digging in, I figured out very quickly it wouldn’t be.&lt;/p>
&lt;h2 id="a-little-background">A Little Background&lt;/h2>
&lt;p>If you&amp;rsquo;re not familiar with &lt;a href="https://stackoverflow.com/help/badges">badges&lt;/a> on Stack Overflow, they are awarded for performing actions on the site. For example, the badge in the bug report, &lt;a href="https://stackoverflow.com/help/badges/804/suffrage">Suffrage&lt;/a> is awarded for voting 30 times in a day. Some of our badges can be awarded multiple times while others are awarded only once. The Suffrage badge is supposed to be a one time badge, which is why it is odd that someone had it twice.&lt;/p>
&lt;h2 id="initial-investigation">Initial Investigation&lt;/h2>
&lt;p>Based on the bug report, I knew three pieces of information: 1) the badge, 2) who it was awarded to and 3) when it happened. I turned to the database to see if I could get any more info.&lt;/p>
&lt;p>My first step was to see if there were other cases where the Suffrage badge was awarded twice.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-sql" data-lang="sql">&lt;span class="k">SELECT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">count&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">FROM&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Users2Badges&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">WHERE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">BadgeId&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">804&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">-- Suffrage
&lt;/span>&lt;span class="c1">&lt;/span>&lt;span class="k">HAVING&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">COUNT&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">1&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;p>Nope. Only one user was lucky enough to get it twice. Then I decided to see if anything was unusual about the day it was awarded. When I say unusual, I mean, did we have any outages, or, was there anything that could have glitched when the badges were granted?&lt;/p>
&lt;p>The badge in question was awarded on June 22nd 2018, so you might be wondering how in the world would I be able to track down outages, etc. from 1.5 years ago?&lt;/p>
&lt;p>I&amp;rsquo;m glad you asked. When we throw certain exceptions on the site, they get sent to our internal chatrooms. All I had to do was go back to the transcript for that specific day, and do a quick scan to see if anything jumped out at me. Lo and behold something did. A whole lot of badge grant exceptions:&lt;/p>
&lt;p>&lt;img src="https://tarynpivots.com/image/2020/chat_exceptions.png" alt="Chat Exceptions">&lt;/p>
&lt;h3 id="a-bit-more-background">A Bit More Background&lt;/h3>
&lt;p>Around that time, June 2018, we were feeling the crunch of low free space on our primary SQL Server SSDs. We had &amp;lt; 10% free, which wasn&amp;rsquo;t great, so we investigated what, if anything, we could do without having to buy new drives. During that research, we came across our &lt;code>Log&lt;/code> table, which exists in all of our databases. These tables are used to capture messages from the application. Seems harmless, right? Normally, yes, but we had been logging data into these tables for years and never deleted a single row for most log types. The &lt;code>Log&lt;/code> tables were storing a ridiculous number of rows across &lt;strong>ALL OF OUR DATABASES&lt;/strong>. We were going to gain a ton of space back by purging this old data. Here is just one database as an example:&lt;/p>
&lt;blockquote>
&lt;p>&lt;strong>StackExchange.Tor:&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>Database Size: 10.3 GB&lt;/li>
&lt;li>Log Table Size: 10.12 GB&lt;/li>
&lt;/ul>
&lt;/blockquote>
&lt;p>Since we needed to purge data from these tables across the entire network, we used our scheduler to do it. The scheduler would allow us to do this in batches, in order to not slam the SQL Servers. The goal was to chew through the deletions, but not cause blocking or timeouts on any other process. It was a game of cat and mouse with the servers as we attempted to figure out the correct batch size. We basically were going to execute this everywhere:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-sql" data-lang="sql">&lt;span class="k">DELETE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Top&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">50000&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">FROM&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Log&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">WHERE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">LogEntryType&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">30&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">AND&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">CreationDate&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">GETUTCDATE&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">60&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;p>We tried a 50k batch size which resulted in timeouts, similar to the screenshot above. We tried 25k which worked. We upped it again to 40k, and it failed. Finally, we dropped it back down to 25k and just let it delete as needed. Any time the scheduled deletions threw exceptions, we had a record of it in our chatroom. These messages in chat were critical to see what was happening when the duplicate badge was granted.&lt;/p>
&lt;p>After looking through the chat transcripts, I had a far better understanding of what was taking place when the badge was awarded incorrectly. I also was pretty sure I knew the cause of the duplicate awards, I just needed to prove it.&lt;/p>
&lt;h2 id="digging-deeper">Digging Deeper&lt;/h2>
&lt;p>Based on what I saw in the transcripts, my gut was telling me the issue had to do with the massive influx to the transaction logs making the &lt;code>log_send_queue_size&lt;/code> sky-rocket. This resulted in a much larger amount of data that needed to be written to the secondary, in other words the &lt;code>redo_queue_size&lt;/code> was also extremely large (both values are available by querying &lt;a href="https://docs.microsoft.com/en-us/sql/relational-databases/system-dynamic-management-views/sys-dm-hadr-database-replica-states-transact-sql?view=sql-server-ver15">&lt;code>sys.dm_hadr_database_replica_states&lt;/code>&lt;/a>). As a result, we were reading dirty data when we awarded the badges. We award badges in this manner:&lt;/p>
&lt;ol>
&lt;li>Query the readable secondary to verify who should get the badge — we use the secondary to help spread out our read workload&lt;/li>
&lt;li>Award the badge on the primary&lt;/li>
&lt;/ol>
&lt;p>We grant badges every 5 minutes, so if the secondary server was overloaded in trying to write a lot of transactions due to another process, we might be reading stale data on the next execution. I had one example of this from the bug report, I just needed a few more to prove that was the case. Time to go back to the database.&lt;/p>
&lt;p>I wrote up an ugly little query to get a list of all duplicate badges awarded.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-sql" data-lang="sql">&lt;span class="p">;&lt;/span>&lt;span class="k">WITH&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">IncorrectAwards&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">as&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">SELECT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ub&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">UserId&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ub&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">BadgeId&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Total&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">count&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">FROM&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Users2Badges&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ub&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">WHERE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">EXISTS&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">SELECT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">FROM&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Badges&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">b&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">WHERE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">b&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">Single&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">AND&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ub&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">BadgeId&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">b&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">Id&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">GROUP&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">BY&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ub&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">UserId&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ub&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">BadgeId&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">HAVING&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">count&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">SELECT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">FROM&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Users2Badges&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ub&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">WHERE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">EXISTS&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">SELECT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">FROM&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">IncorrectAwards&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ia&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">WHERE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ub&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">UserId&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ia&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">UserId&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">and&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ub&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">BadgeId&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ia&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">BadgeId&lt;/span>&lt;span class="p">)&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;p>Using the results and the exceptions in our chatroom, I was able to confirm that we had other instances where a huge process hit the servers, overloaded the transaction logs, and resulted in duplicate badges. And what do you know I found a bunch of them!&lt;/p>
&lt;p>I didn&amp;rsquo;t need to look very far back in time either. Back in November 2019, we performed a &lt;a href="https://stackoverflow.blog/2019/11/13/were-rewarding-the-question-askers/">massive reputation recalculation&lt;/a> which involved awarding reputation to question askers across the entire network. Let&amp;rsquo;s just say we overwhelmed our servers just a little bit during this time and the redo of the transaction logs in our Availability Groups and Distributed Availability Groups was incredibly slow. Since we were hammering the secondaries with a lot of transactions, we unfortunately were reading staler and staler data when we awarded badges which lead to other duplicates.&lt;/p>
&lt;h2 id="fixing-the-issueor-not">Fixing the Issue&amp;hellip;Or Not&lt;/h2>
&lt;p>Now that I discovered the issue, it should be an easy fix, right? Well, sort of. Yeah, I could easily delete the duplicate badges and move on — which of course fixes the initial bug report, but doesn&amp;rsquo;t solve the underlying issue.&lt;/p>
&lt;p>What happens the next time we run something massive on the primary and we overload the transaction logs, creating a delay in the data on the secondary? The same thing would most likely happen. The problem is, we need to use the secondary to check to see who should get the badge, we can&amp;rsquo;t move that back to the primary. Honestly, we&amp;rsquo;re not exactly sure how we&amp;rsquo;re going to fix it yet. We have some ideas like maybe checking the size of the redo queue before we execute the badge grants and if it&amp;rsquo;s &lt;code>some undetermined size threshold&lt;/code> we skip the next badge grant, or some other fix we haven&amp;rsquo;t thought of yet.&lt;/p>
&lt;h2 id="final-thoughts">Final Thoughts&lt;/h2>
&lt;p>You&amp;rsquo;re probably wondering why I wrote this post since we didn&amp;rsquo;t even fix the bug? Well, mainly because we use our readable secondary a lot and I&amp;rsquo;m sure we aren&amp;rsquo;t the only ones. While I knew we always needed to be mindful of the redo of the transaction logs to our secondaries in the availability groups, I need to be more vigilant in watching the impact to hopefully prevent a lot of clean-up later on.&lt;/p>
&lt;p>By the way, since this initial bug was caused by purging the &lt;code>Log&lt;/code> tables due to low free drive space, I figured I&amp;rsquo;d let you know that purging all that old unneeded data brought us from 90%+ used to &amp;lt;50% used. It triggered some duplicate badges, but it saved us lots of money in buying new SSDs and has given us a few more years of breathing room.&lt;/p>
&lt;p>Also if you&amp;rsquo;ve hit any issues like this and have suggestions on how to fix it, let me know. I&amp;rsquo;m super curious.&lt;/p></description></item><item><title>T-SQL Tuesday #122 - Impostor Syndrome</title><link>https://tarynpivots.com/post/t-sql-tuesday-122-impostor-syndrome/</link><pubDate>Tue, 14 Jan 2020 06:00:38 +0000</pubDate><guid>https://tarynpivots.com/post/t-sql-tuesday-122-impostor-syndrome/</guid><description>&lt;p>&lt;a href="https://jonshaulis.com/index.php/2020/01/07/t-sql-tuesday-122-imposter-syndrome/">&lt;img src="https://tarynpivots.com/image/2020/T-SQL-Tuesday-Logo.jpg#floatright" alt="T-SQL Tuesday Logo">&lt;/a>&lt;/p>
&lt;p>This month&amp;rsquo;s T-SQL Tuesday is hosted by &lt;a href="https://twitter.com/JonShaulis">Jon Shaulis&lt;/a>, who has asked that we talk about &lt;a href="https://jonshaulis.com/index.php/2020/01/07/t-sql-tuesday-122-imposter-syndrome/">impostor syndrome&lt;/a>. This subject is close to my heart, as I have dealt with it throughout my career.&lt;/p>
&lt;p>I didn&amp;rsquo;t study computer science or engineering in college. I never even took a computer class. I sort of just fell into working as a web developer because I had &lt;a href="https://tarynpivots.com/post/ch-ch-ch-changes-are-afoot/">someone tell me they wouldn’t hire me due to lack of experience&lt;/a>.&lt;/p>
&lt;p>Early in my career, I was a web developer, an MS Access developer, and a C#/WinForms Software Engineer, and I never knew what I was doing in any of those roles. For all of them, I used books, blogs, and &lt;a href="http://stackoverflow.com/">Stack Overflow&lt;/a> to learn what I needed, in order to complete some of my day to day tasks. I did whatever it took to get the job done, and learned everything I could in the process. Each day I hoped people wouldn’t realize I didn’t really know what I was doing.&lt;/p>
&lt;p>Eventually, I found my niche. I loved working with databases and writing SQL. I worked as a Database Developer/DBA for quite a few years and then took a break from it, but always knew I’d go back. After a two year hiatus, I went back as the DBA at Stack Overflow.&lt;/p>
&lt;p>Once I returned as a DBA, I struggled daily with impostor syndrome. Imagine, after a two year break from SQL Server, returning to work on the servers that run one of the most visited websites in the world. And on top of that, working with people who know far more about our servers and SQL than I ever did. I completely felt out of my element and struggled significantly with impostor syndrome.&lt;/p>
&lt;p>To be honest, I still struggle with it. I work with incredibly smart people. Far smarter than I am. I suggest things that are shot down. I propose both good and bad ideas that are sometimes accepted, and sometimes not.&lt;/p>
&lt;h3 id="how-do-i-deal-with-it">How do I deal with it?&lt;/h3>
&lt;p>I continue to suggest things that should be changed. They may be right or wrong, but I still do it. We discuss the pros and cons of the suggestions, and each time I learn from it.&lt;/p>
&lt;p>Additionally, I&amp;rsquo;m trying to write more. I know there are things people can learn from me. I&amp;rsquo;m not the most confident in my writing (insert more impostor syndrome), but I&amp;rsquo;m working to overcome that by contributing more to the SQL community where I can. I fully expect I&amp;rsquo;ll make mistakes along the way, but that&amp;rsquo;s the only way I&amp;rsquo;ll be able to learn and get past my impostor syndrome.&lt;/p></description></item><item><title>Hitting Parallel_Redo_Flow_Control waits with Availability Groups</title><link>https://tarynpivots.com/post/parallel-redo-flow-control-waits-and-availability-groups/</link><pubDate>Mon, 09 Dec 2019 06:00:38 +0000</pubDate><guid>https://tarynpivots.com/post/parallel-redo-flow-control-waits-and-availability-groups/</guid><description>&lt;p>In late June 2019, &lt;a href="https://twitter.com/StackStatus/status/1143971084623941632">June 26th&lt;/a> to be exact, we experienced an outage on Stack Overflow for about 11 minutes. It&amp;rsquo;s not unusual that we had an outage. They happen. Not often, but they do still happen. This one, however, was a little different because it was caused by a maintenance job that was running on our primary SQL Server for Stack Overflow.&lt;/p>
&lt;p>The job that caused it was something I&amp;rsquo;d noticed about a month prior, but &lt;a href="https://twitter.com/tarynpivots/status/1130962290033844224">had stopped it&lt;/a> before an actual outage occurred. Once corrected, I didn&amp;rsquo;t see any sign of it again, so I figured we were fine. I was wrong. This time around, the job triggered the same issue and unfortunately, took us down for longer than I would like.&lt;/p>
&lt;p>You&amp;rsquo;re probably thinking — wait a minute, a maintenance job triggered an outage? What type of job did that?&lt;/p>
&lt;h2 id="hang-on-please-explain-yourself">Hang On, Please Explain Yourself&lt;/h2>
&lt;p>While I can&amp;rsquo;t be 100% sure of the trigger, I&amp;rsquo;m 99.9% sure, because the job was running before the outage, so the timing is right. After looking through our monitoring logs, everything pointed to the job being the cause, so yes, I&amp;rsquo;m confident it caused it.&lt;/p>
&lt;p>We don&amp;rsquo;t have regular maintenance windows for any of our servers, so we run jobs throughout the week, and if possible, try to schedule them during low-usage times. In this case, the job was an index maintenance job.&lt;/p>
&lt;p>Now, before you scream at me about running an index maintenance job, I&amp;rsquo;m not going to argue the pros and cons of using it or whether or not we should run it — we can do that at another time. For this post, just accept the fact that we were running a job to rebuild/reorganize indexes.&lt;/p>
&lt;h3 id="some-background">Some Background&lt;/h3>
&lt;p>We run &lt;a href="https://docs.microsoft.com/en-us/sql/sql-server/editions-and-components-of-sql-server-2017?view=sql-server-ver15">SQL Server 2017 Enterprise Edition&lt;/a> which gives us the ability to rebuild indexes online, but we never had a scheduled job for it. If an index needed to be rebuilt, we&amp;rsquo;d manually do it. Ideally, we would automate this and target the indexes suffering from the most fragmentation, rebuild it, and move on to the next one.&lt;/p>
&lt;p>After I &lt;a href="https://tarynpivots.com/post/how-stack-overflow-upgraded-from-windows-2012/">finished the OS upgrade of our main servers&lt;/a> we were in a better place to try running an index optimize job on a regular basis. We waited until after the upgrade from Windows Server 2012 because of the improved throughput we would get with our secondaries. Once the upgrades were done, we knew we could try to run the job more often. (Side note: why we waited until after we did the upgrade to run the index job is a story for another time, but let&amp;rsquo;s just say it caused a different outage from running out of disk space.)&lt;/p>
&lt;p>Using most of &lt;a href="https://ola.hallengren.com/">Ola Hallengren&amp;rsquo;s maintenance solution&lt;/a> across our servers, I set up the Index Optimize job to look for specific fragmentation levels, and depending on the server, I spaced out the execution frequency. I needed to give enough time in between executions for the logs to transmit to our secondaries. On some servers, I scheduled the job to run hourly, and on others it was scheduled to run every two hours.&lt;/p>
&lt;p>In addition to frequency, I also had to be mindful of how long they ran. For example, running the job on the server that holds the Stack Exchange network of databases (~375 databases), would take hours to run through all the indexes to be defragmented. For that server, the job was scheduled to look for indexes with X% fragmentation and would run for 5-10 minutes. This spacing prevented overloading the transaction logs in between log backups, and would help minimize what needed to be transported to each of our secondaries.&lt;/p>
&lt;p>My thinking was, we&amp;rsquo;d be far more efficient biting off smaller chunks of changes 5-10 minutes at a time, versus running it for many, many hours, and generating tons of log data that needed to be pushed to the secondaries. Doing this in bits also allowed our remote secondaries in Colorado to keep up with the transaction log backup. If I ran the job for too long, Colorado would fall further and further behind while trying to write the transaction log, so it made far more sense to do this a little at a time.&lt;/p>
&lt;h2 id="enough-of-the-background-how-did-you-break-production">Enough of the Background, How Did you Break Production?&lt;/h2>
&lt;p>About a month before the outage, I caught the issue and stopped it before we got to the level of the outage&amp;hellip;well, that didn&amp;rsquo;t happen this time.&lt;/p>
&lt;p>I&amp;rsquo;m going to take another step back and give a bit more background.&lt;/p>
&lt;p>As mentioned in some of my other posts, we use &lt;a href="https://docs.microsoft.com/en-us/sql/database-engine/availability-groups/windows/always-on-availability-groups-sql-server?view=sql-server-ver150">SQL Server&amp;rsquo;s Always On Availability Groups&lt;/a>. Most of our clusters have 3 servers - one primary, one local secondary, and one remote secondary. In our primary datacenter - we have two servers - the primary receives write and read traffic, while our local secondary receives a lot of the read-only traffic.&lt;/p>
&lt;p>We offload a lot to our readable secondary (at some point, if there&amp;rsquo;s interest, I&amp;rsquo;ll provide an overview of what we do). The secondary, in both of our main clusters, is used for many of our scheduled jobs and many of these scheduled jobs run hourly from across our web tier.&lt;/p>
&lt;h3 id="outage-timeline">Outage Timeline&lt;/h3>
&lt;p>The timeline of the events will give a bit of insight into what exactly happened when we went offline. Using our monitoring tools, I was able to determine the timeline as:&lt;/p>
&lt;ul>
&lt;li>The index maintenance job ran on the primary server&lt;/li>
&lt;li>The job rebuilt/reorganized an index against a specific table&lt;/li>
&lt;li>The index rebuild was in progress of being replayed to the local secondary - the NY secondary server&lt;/li>
&lt;li>As the change to the index was in the process of being written to the secondary, the scheduler triggered a job which needed to query the secondary and the table where the log changes being written to&lt;/li>
&lt;li>The scheduler hit blocking when attempting to query the table on the secondary server because the index rebuild was still writing to it&lt;/li>
&lt;li>The replay of the index rebuild was running just as the scheduler attempting to query the same table, resulting in a significant number of sessions being blocked&lt;/li>
&lt;li>The secondary was overloaded with queries attempting to query the table (both the scheduled queries and normal traffic against that table) and we basically fell over due to blocked sessions resulting in connection pool exhaustion upstream&lt;/li>
&lt;/ul>
&lt;p>The outage was the result of a domino effect, but the starting domino was the index maintenance job.&lt;/p>
&lt;p>The size of the index, and the criticality of the table also added to the issue, but the redo to the secondary while we were trying to query it pushed us over the edge and the server we couldn&amp;rsquo;t keep up. This resulted in hundreds of sessions blocked. Since nothing could query the required tables, we went offline.&lt;/p>
&lt;p>As soon as we started to hit the blocking sessions, I received an alert from our instance of &lt;a href="https://www.sentryone.com/products/sentryone-platform/sql-sentry/sql-server-performance-monitoring">Sentry One SQL Sentry&lt;/a> that there was blocking on the server. I quickly opened the email and saw that we were hitting &lt;a href="https://blogs.msdn.microsoft.com/sql_server_team/sql-server-20162017-availability-group-secondary-replica-redo-model-and-performance/">&lt;code>PARALLEL_REDO_FLOW_CONTROL&lt;/code> waits&lt;/a>. The Microsoft Docs explain that this wait&lt;/p>
&lt;blockquote>
&lt;p>Occurs when the main redo thread cannot dispatch more transaction log records when log cache array for dispatched transaction log is full.&lt;/p>
&lt;/blockquote>
&lt;blockquote>
&lt;p>Indicates that one or more parallel redo worker threads cannot keep up with main redo thread transaction log dispatching speed or are blocked by some resources such as other type of waits. When this wait occurs frequently, parallel redo worker threads does not function efficiently&lt;/p>
&lt;/blockquote>
&lt;p>It makes sense that this was our issue. We were overloading the server with the underlying change to the index. The transaction log was very large during this time, and was trying to write to the secondary server. At this exact same moment, we also needed to query that table for scheduled jobs. Since the table was unavailable for use, the application could not continue and we went offline.&lt;/p>
&lt;p>Since I saw the behavior in the previous month, I knew what to do. I quickly connected to the secondary server and ran &lt;a href="http://whoisactive.com/">Adam Machanic&amp;rsquo;s &lt;code>sp_whoisactive&lt;/code>&lt;/a> to see all the sessions attempting to query, and see what was being blocked. Once I had that list, it was time to &lt;code>kill&lt;/code> all the sessions. I needed to give a bit of time for the index rebuild to write to the server, and by killing the sessions it was just enough to let things settle down. Unfortunately, we were down for about 11 minutes, but between the monitoring from Sentry One and seeing the issue pop up the month before, I knew that I would give us some breathing room by killing &lt;code>spids&lt;/code>. Once the sessions were killed (helping unblock application connection pools as well), the log was able to write to the server and we came back up.&lt;/p>
&lt;p>While this explains how we fixed the outage, that&amp;rsquo;s not the point of this post.&lt;/p>
&lt;h2 id="then-what-is-the-point-of-this">Then, What is the Point of This?&lt;/h2>
&lt;p>If you run Always On Availability Groups, and you use a readable secondary, you need to be mindful of the issues with &lt;code>PARALLEL_REDO_FLOW_CONTROL&lt;/code>. These waits resulted in enough blocking to take us offline.&lt;/p>
&lt;p>Yes, I&amp;rsquo;m aware that there is a &lt;a href="https://docs.microsoft.com/en-us/sql/t-sql/database-console-commands/dbcc-traceon-trace-flags-transact-sql?view=sql-server-ver15">trace flag 3459&lt;/a> that we can turn on, but we don&amp;rsquo;t have that enabled for our Stack Overflow SQL Server. Most of the time, it doesn&amp;rsquo;t make sense for us to disable parallelism (due to scale), so we don&amp;rsquo;t.&lt;/p>
&lt;p>In order to prevent this from happening again during the index maintenance, I moved the impacted database to a separate index maintenance job that runs only on the weekend, when we have lower traffic and less opportunities to collide with other jobs. Once I moved that database to a separate job, we stopped hitting blocking issues with &lt;code>PARALLEL_REDO_FLOW_CONTROL&lt;/code> and that job.&lt;/p>
&lt;p>This solution works for us, but your situation might be different. If you run Always On Availability Groups, and you utilize a readable secondary, be mindful of those &lt;code>PARALLEL_REDO_FLOW_CONTROL&lt;/code> waits, since they can result in significant blocking for an application reading from the secondary. In our case, adjusting when we ran our job helped especially since we don&amp;rsquo;t use the Trace Flag 3459, but that trace flag might work for you. My recommendation would be to test different situations with your AGs and maintenance jobs to see what works.&lt;/p>
&lt;p>Have you hit this same issue? Did you fix it using the trace flag or some other way?&lt;/p></description></item><item><title>Finally, My first PASS Summit</title><link>https://tarynpivots.com/post/my-first-pass-summit/</link><pubDate>Mon, 02 Dec 2019 08:00:00 +0000</pubDate><guid>https://tarynpivots.com/post/my-first-pass-summit/</guid><description>&lt;p>I&amp;rsquo;ve been working with SQL Server for a long time, and have always wanted to attend PASS. For one reason of another, with numerous employers, I hadn’t been able to go. The answer was always no. &lt;code>&amp;lt;insert sad face&amp;gt;&lt;/code>&lt;/p>
&lt;p>The biggest blocker was always the cost.&lt;/p>
&lt;p>The conference is expensive.&lt;/p>
&lt;p>Seattle is expensive.&lt;/p>
&lt;p>It has always been impossible to get my employer to foot the bill for it.&lt;/p>
&lt;p>This year I was incredibly lucky that my employer, &lt;a href="https://stackoverflow.com">Stack Overflow&lt;/a>, paid for my trip to PASS Summit. I was thrilled to finally be able to attend. Since I was given the opportunity to go, I wanted to write out my thoughts as a PASS &amp;ldquo;First-Timer&amp;rdquo;, as well as some session highlights.&lt;/p>
&lt;h2 id="a-bit-about-me">A bit about me&lt;/h2>
&lt;p>I&amp;rsquo;m an introvert. Walking into a place&amp;hellip;by myself, where I know no one is incredibly difficult. It triggers lots of anxiety which makes the idea of attending a conference with thousands of people who I don&amp;rsquo;t know, insanely hard. Sure, I know people at PASS Summit because I have seen their faces online from following them on Twitter, and have interacted with them virtually, but I have never met most of them in person. In order to meet them, I need to actually meet them&amp;hellip;like talk to them&amp;hellip;which is a struggle. I was going to try my best to work past that and take in everything I could.&lt;/p>
&lt;h2 id="scheduling-overload">Scheduling Overload&lt;/h2>
&lt;p>The week before PASS, I sat down and filled out my schedule which was a bit challenging. There were so many sessions that I wanted to attend. My final schedule had a ton of sessions at the same time. I did this in the event I would change my mind last minute, so I’d have an alternate option. I knew I was overplanning, and was being overly ambitious, but I was going to push myself to hit as many sessions as I could.&lt;/p>
&lt;h2 id="first-timer-buddy-program">First Timer Buddy Program&lt;/h2>
&lt;p>I saw mentions of the Buddy Program and wasn&amp;rsquo;t sure if I was going to sign up, but I&amp;rsquo;m glad I did. For those who don&amp;rsquo;t know, the Buddy program at PASS Summit pairs you with someone who has been to the conference before, and they offer suggestions on events and sessions to attend. If you&amp;rsquo;re lucky and if time allows, you&amp;rsquo;ll meet them and have a familiar face in the thousands of people. I was lucky to get paired with &lt;a href="https://twitter.com/sqlatspeed">Matt Gordon&lt;/a>. Matt reached out a couple of weeks before the Summit and suggested a couple of sessions, then the week of the conference we met and had lunch together as a group. It was great to meet other first-timers in a smaller setting.&lt;/p>
&lt;h2 id="wit-reception">WIT Reception&lt;/h2>
&lt;p>I signed up for the WIT Reception which took place on the Tuesday night before the official start of the conference. I loved this event. It was smaller than many of the other events at the conference, and it was a great way to meet people before the conference officially started, and gave me a chance to have some familiar faces to find in the crowds of people the rest of the week. Not to mention, I met some really awesome people at the event.&lt;/p>
&lt;p>The WIT happy hour overlapped with the First Timers Welcome Reception. Since I was comfortable at the WIT event, I did not leave to attend the First Timer Event. Was that the right decision? I don&amp;rsquo;t know. I met some great people at the WIT event and didn&amp;rsquo;t want to leave, however, I know that if I went to the First Timer Event, I probably would have met other newbies, which might have been beneficial.&lt;/p>
&lt;h2 id="day-one">Day One&lt;/h2>
&lt;h3 id="performance-tuning-azure-sql-databaseshttpswwwpassorgsummit2019learnsessiondetailsaspxsid92582-with-david-maxwell">&lt;a href="https://www.pass.org/summit/2019/Learn/SessionDetails.aspx?sid=92582">Performance Tuning Azure SQL Databases&lt;/a> with David Maxwell&lt;/h3>
&lt;p>At this time, we only use physical SQL Servers in our datacenters. As a result, I don&amp;rsquo;t have a lot of hands-on experience with Azure SQL, so I figured I would take the opportunity to learn a bit while at PASS. I started with a how to performance tune Azure SQL databases. This session was really good to hear about some of the differences in on-prem and Azure SQL and how you tackle performance issues using Extended Events and Automating Tuning.&lt;/p>
&lt;h3 id="improving-availability-in-sql-server-and-azure-sql-database-with-accelerated-database-recovery-and-resumable-operationshttpswwwpassorgsummit2019learnsessiondetailsaspxsid98848-with-pam-lahoud">&lt;a href="https://www.pass.org/summit/2019/Learn/SessionDetails.aspx?sid=98848">Improving Availability in SQL Server and Azure SQL Database with Accelerated Database Recovery and Resumable Operations&lt;/a> with Pam Lahoud&lt;/h3>
&lt;p>This session showed off one of the new features of SQL Server 2019 that I&amp;rsquo;m excited to test in our environments, Accelerated Database Recovery. It allows the databases to recover faster from long-running transactions that were aborted and are rolling back.&lt;/p>
&lt;p>Prior to SQL Server 2019, you might have to wait hours for a rollback of transaction on a long-running query before the server would recover, all the while you could have queries blocked, etc. With the new release, if there is a long running transaction that gets killed, then the database will be unblocked immediately. This is a cool new feature of SQL Server 2019 that has some overhead, but I&amp;rsquo;m excited to test it out.&lt;/p>
&lt;p>The session also covered Resumable Index Operations which allows an index rebuild operation to be paused and then resumed or aborted at another time. Again, a cool feature I&amp;rsquo;m excited to play with.&lt;/p>
&lt;h2 id="day-two">Day Two&lt;/h2>
&lt;h3 id="keynote-with-tarah-wheeler---cybersecurity-is-everyones-problem">Keynote with Tarah Wheeler - Cybersecurity is Everyone&amp;rsquo;s Problem&lt;/h3>
&lt;p>There is no doubt this was one of my favorite sessions of the conference. &lt;a href="https://twitter.com/tarah">Tarah Wheeler&lt;/a> is an expert in cybersecurity and an author. She talked about the conflict between cybersecurity and data science - the struggle between keeping or purging data in the age of three internets - the European Union (EU and GDPR), China, and then the rest of the world. Since data is constantly growing, how do you satisfy the need to delete data under different regulatory requirements. The problem is huge and is going to keep compounding as we continue to store data. The question she posed in this slide captures how complicated the situation is.&lt;/p>
&lt;p>&lt;img src="https://tarynpivots.com/image/2019/keynote_tarahwheeler.jpg" alt="Asking the tough questions">&lt;/p>
&lt;p>The Keynote was incredible on every level and hands down, it was the best thing I attended all week.&lt;/p>
&lt;h3 id="columnstore-indexes-in-2017-2019httpswwwpassorgsummit2019learnsessiondetailsaspxsid92589-with-niko-neugebauer">&lt;a href="https://www.pass.org/summit/2019/Learn/SessionDetails.aspx?sid=92589">Columnstore Indexes in 2017-2019&lt;/a> with Niko Neugebauer&lt;/h3>
&lt;p>I have read many of Niko&amp;rsquo;s blog posts on Clustered Columnstore, so getting the opportunity to see him talk about them in person was a no brainer.&lt;/p>
&lt;p>Within just a few minutes, I thought, &amp;ldquo;hey that might be our problem.&amp;rdquo; The biggest lightbulb moment in this session was how dictionary size and memory pressure can break row groups in ways that are potentially unexpected. As soon as I heard this, I knew what I was going to investigate it when I was back in the office. This session was great because I had takeaways that I could possibly use or research immediately.&lt;/p>
&lt;h3 id="azure-sql-database---lessons-learned-from-the-trencheshttpswwwpassorgsummit2019learnsessiondetailsaspxsid90954-with-jose-manuel-jurado-diaz-and-roberto-cavalcanti">&lt;a href="https://www.pass.org/summit/2019/Learn/SessionDetails.aspx?sid=90954">Azure SQL Database - Lessons Learned from the Trenches&lt;/a> with Jose Manuel Jurado Diaz and Roberto Cavalcanti&lt;/h3>
&lt;p>I also loved this session. The speakers went through different scenarios that customers have hit while using Azure SQL Databases.&lt;/p>
&lt;p>Seeing different tips and tricks to look for when performance tuning in that environment gave me a lot to think about for future work in the cloud. Things as simple as an implicit conversion can perform significantly different on Azure SQL Database once you hit an increase in data that an old cached plan can no longer handle.&lt;/p>
&lt;h3 id="improving-columnstore-load-scalability-on-large-servershttpswwwpassorgsummit2019learnsessiondetailsaspxsid91912-with-joe-obbish">&lt;a href="https://www.pass.org/summit/2019/Learn/SessionDetails.aspx?sid=91912">Improving Columnstore Load Scalability on Large Servers&lt;/a> with Joe Obbish&lt;/h3>
&lt;p>I had heard there were 180 slides for this session and was sure my head would be spinning from the amount of information. Instead of being overwhelmed by it, Joe did a great job of going over how to optimize loading millions of rows of data into a clustered columnstore table in just 6 minutes.&lt;/p>
&lt;p>He went through each step in his optimization process, explaining what he looked for to tune in the next step. With each attempt the processing time dropped considerably.&lt;/p>
&lt;p>Besides watching how to performance tune huge data loads into clustered columnstore tables, I learned several bits that I could take back and see about implementing in our environment. This was another top session for the week.&lt;/p>
&lt;h2 id="day-three">Day Three&lt;/h2>
&lt;h3 id="batch-execution-mode-on-rowstore-indexeshttpswwwpassorgsummit2019learnsessiondetailsaspxsid92529-with-niko-neugebauerh4">&lt;a href="https://www.pass.org/summit/2019/Learn/SessionDetails.aspx?sid=92529">Batch Execution Mode on Rowstore Indexes&lt;/a> with Niko Neugebauer&lt;!-- raw HTML omitted -->&lt;/h3>
&lt;p>This session included some great demos on the new feature in SQL Server 2019, Batch Execution Mode on traditional rowstore indexes. I&amp;rsquo;m not sure how much benefit we will get with this in our environment, but I&amp;rsquo;m curious to get SQL Server 2019 installed on more servers to see if we do.&lt;/p>
&lt;h3 id="deep-dive-into-blocking-and-deadlocks-troubleshootinghttpswwwpassorgsummit2019learnsessiondetailsaspxsid92528-with-dmitri-korotkevitch">&lt;a href="https://www.pass.org/summit/2019/Learn/SessionDetails.aspx?sid=92528">Deep Dive into Blocking and Deadlocks Troubleshooting&lt;/a> with Dmitri Korotkevitch&lt;/h3>
&lt;p>Blocking and Deadlocks every DBAs nemesis. At our scale, I fight with them a lot, so having the chance to attend a session about troubleshooting was a must. Even though I have experience with dealing with both blocking and deadlocks, it was a great overview on why they occur, and how best to troubleshoot them. This was a solid session to attend.&lt;/p>
&lt;h2 id="final-thoughts">Final Thoughts&lt;/h2>
&lt;p>The week flew by. It was information overload in each session that I attended, but I&amp;rsquo;m so happy I finally had the chance to go. Even though I was out of my comfort zone, I met a ton of people and learned a lot while I was there.&lt;/p>
&lt;p>I hope I&amp;rsquo;ll be able to attend again in the future&amp;hellip;maybe even next year in Houston.&lt;/p></description></item><item><title>How Stack Overflow upgraded from Windows Server 2012</title><link>https://tarynpivots.com/post/how-stack-overflow-upgraded-from-windows-2012/</link><pubDate>Thu, 18 Jul 2019 06:00:38 +0000</pubDate><guid>https://tarynpivots.com/post/how-stack-overflow-upgraded-from-windows-2012/</guid><description>&lt;p>&lt;strong>Warning:&lt;/strong> This post is long. While working through this massive server upgrade/migration process, tears were shed, many cuss words were said, along with a general feeling of frustration, which ultimately culminated into extreme happiness once the migration was completed. The scale and complexity of the implementation factor into the length of this post, and I’ll share my thought process on how this was executed, so here goes.&lt;/p>
&lt;p>Last year, when we upgraded to &lt;a href="https://tarynpivots.com/post/how-we-upgraded-stackoverflow-to-sql-server-2017/">SQL Server 2017&lt;/a> we didn&amp;rsquo;t make any changes to the operating system on our main production servers. They were Windows Server 2012 (not R2), and we knew that moving to another operating system would be painful because it would involve tearing down each of the clusters, rebuilding them, and potentially having extended downtime - which we really can&amp;rsquo;t have. It sounded too difficult at the time, so it was punted&amp;hellip;again.&lt;/p>
&lt;p>When I was mapping out projects for 2019, at the top of my list was to move from Windows Server 2012 and move to Windows Server 2019, because &amp;lsquo;hey, it&amp;rsquo;s 2019 let&amp;rsquo;s move away from 7 year old OS&amp;rsquo;. From the start, it was obvious this would be extremely complicated, but why not start the new year off with a crazy project that gets us in a position to move to SQL Server 2019. I had never done anything like this before, but in January, I started working out how we were going to upgrade our existing production SQL Servers from Windows 2012. And in this very long post I explain all my planning, testing, unexpected issues, and implementation of this move.&lt;/p>
&lt;h2 id="reasoning">Reasoning&lt;/h2>
&lt;p>My first step was to identify the benefits of migrating. I would be spending a considerable amount of time on the project, so it was important to know what we would gain from the upgrade. There were two obvious wins 1) moving away from a 7 year old operating system, and 2) it also would allow us to move to SQL Server 2019 (yay&amp;hellip; more upgrades). However, the biggest benefit we were hoping for, was an improvement with our availability group log transport throughput. Our current production clusters each have 3 nodes - 2 (primary and local secondary) in New York (actually New Jersey) and 1 (remote secondary) in Colorado - and we tend to see significant delays and non-synchronizing databases in Colorado. Upgrading from 2012 to Windows 2016+ would get us gains in performance, and hopefully reduce some of our syncing issues. For me, that would be a huge plus, as I wanted to avoid seeing stuff like this repeatedly during the week:&lt;/p>
&lt;p>&lt;img src="https://tarynpivots.com/image/2019/notsyncing.jpg" alt="Not Synchronizing databases">&lt;/p>
&lt;h2 id="phase-1-lab-testingall-the-things">Phase 1: Lab Testing&amp;hellip;All The Things&lt;/h2>
&lt;p>I&amp;rsquo;m incredibly lucky to have a lab environment to play with. At the start of this project, I had two lab Windows Server Failover Clusters (&lt;a href="https://docs.microsoft.com/en-us/windows-server/failover-clustering/failover-clustering-overview">WSFC&lt;/a>) - each with 2 nodes that were running Windows Server 2016, SQL Server 2017, and each cluster had availability groups (AG), as well as a distributed availability group (DAG) between the two clusters. Since I didn&amp;rsquo;t want to destroy these clusters for the test, I needed to create new servers for testing.&lt;/p>
&lt;p>My goal was to replicate our production set-up on a much smaller scale. From there, I’d work through different scenarios to get to the final desired result. The 2012 production clusters looked like this:&lt;/p>
&lt;p>&lt;img src="https://tarynpivots.com/image/2019/2012cluster.jpg" alt="Stack Overflow Windows 2012 Clusters">&lt;/p>
&lt;p>Production had two WSFCs running Windows Server 2012, each with 3 nodes. Both clusters have at least one availability group, as well as one distributed availability group going to a reporting cluster. Once this project was complete, the new clusters would look the same, however, these would have a new OS, new cluster names, and when finished, the primary SQL Server in each availability group would be the current NY Secondary.&lt;/p>
&lt;p>In order to properly test, I needed 3 servers running Windows Server 2012. Well, guess what? We didn&amp;rsquo;t even have a way to install Windows Server 2012, and no longer had an image of the software. This left me hunting for a copy of it. Eventually I got one, but then our deployment process needed to be setup to work with 7 year old software. Once all those bits were in place, I was able to &lt;a href="https://twitter.com/tarynpivots/status/1083158959937093632">spin up my 3 servers to test with&lt;/a>. At this point, I had a new 2012 cluster with 3 nodes (2 in NY, 1 in CO). All were running SQL Server 2017 with 2 availability groups, one AG that was limited to this cluster, and a second AG that was modeled after one in a distributed availability group.&lt;/p>
&lt;h3 id="the-will-this-even-work-test">The Will This Even Work Test&lt;/h3>
&lt;p>Before I started breaking this new test cluster, we had the idea of creating another server with Windows Server 2019 to see if it would work with the new lab cluster - basically, a test to see if the data would sync. I spun up another new server; this time it was on a fancy new operating system, Windows Server 2019, with SQL Server 2017 and was all ready to start testing. The goal was to insert the 2019 server into the mix with 2012, so it would receive data from the old server cluster. I wanted it to look like this:&lt;/p>
&lt;p>&lt;img src="https://tarynpivots.com/image/2019/Lab2012Cluster_2019.png" alt="Lab Cluster - single 2019 server">&lt;/p>
&lt;p>Considering I had no idea what I was doing, I tried many things to get this to work. I even tried things I knew wouldn&amp;rsquo;t work just to cross it off the list of things tested. Here&amp;rsquo;s a brief list of some of the things I tried:&lt;/p>
&lt;ul>
&lt;li>Putting the 2019 server into the existing 2012 cluster - as expected, this fails due to the operating systems being different&lt;/li>
&lt;li>Attempted to just add it to the existing AGs when not being in a cluster - this fails because it&amp;rsquo;s not in a cluster&lt;/li>
&lt;li>Created a separate single node cluster for the 2019 server and attempted to add as a replica to the AGs - this fails as well&lt;/li>
&lt;/ul>
&lt;p>&lt;a href="https://twitter.com/tarynpivots/status/1088158572486090752">None of these things worked.&lt;/a> I was beginning to wonder how we were going to do this. My next test was to create a new distributed availability group for each existing availability group, using that as a way to insert the 2019 server into the mix. I finally hit on something that worked for the single server. After creating a new DAG between the 2012 and 2019 cluster, I had data syncing between two clusters on different operating systems. I was ecstatic to get this to work with one server, but how would I do this with the 3 servers in a single cluster, with all the AGs and distributed AGs already in play?&lt;/p>
&lt;h3 id="the-mock-up-production-test">The Mock-up Production Test&lt;/h3>
&lt;p>Once I knew I could have clusters with different operating systems, synchronizing data via distributed AGs, I needed to attempt this with the new 2012 lab cluster. Since it was already working as a stand-alone cluster with SQL Server running, I wanted to set up a test that was as close as possible to what we have in production. I needed a replica of our reporting cluster. A bell went off in my head, ‘Ding! Ding!’ I had an existing 2016 cluster in my original lab environment - that would be perfect to use for this.&lt;/p>
&lt;p>My plan of attack for this test was to first setup a DAG between the 2012 cluster and the original lab cluster running on 2016. This would be similar to what we had in production at the time. Basically we&amp;rsquo;d have the following:&lt;/p>
&lt;p>&lt;img src="https://tarynpivots.com/image/2019/Lab2012_2016Cluster.png" alt="Lab Cluster - 2012 to 2016">&lt;/p>
&lt;p>When that was setup and synchronizing, I had a decent, albeit, small version of our production setup. It was now time to start breaking things. My thinking was to start with the NY secondary server and perform the following steps:&lt;/p>
&lt;ol>
&lt;li>Evict it from the existing 2012 cluster&lt;/li>
&lt;li>Rebuild it with the Windows Server 2019&lt;/li>
&lt;li>Create a new WSFC with one node&lt;/li>
&lt;li>Install SQL Server 2017&lt;/li>
&lt;li>Finally, create new distributed AGs from the old cluster to the new one to sync the databases with that AG&lt;/li>
&lt;/ol>
&lt;p>My next step was to do the same thing with the CO secondary in the 2012 cluster. The difference being, it could just be added as a node to the new WSFC, and as a replica to the AGs in the new cluster. At this point in the process, I&amp;rsquo;d have the old 2012 cluster with a single server sending data to two clusters - the mocked up 2016 reporting cluster and the new 2019 cluster. Visually it&amp;rsquo;d look like this:&lt;/p>
&lt;p>&lt;img src="https://tarynpivots.com/image/2019/Lab2012_2016_2019cluster.png" alt="Lab Cluster - 2012 to 2016 and 2019">&lt;/p>
&lt;p>Before upgrading the last 2012 server, I would need to perform a failover of the distributed AGs from the 2012 cluster to the new 2019 cluster. In looking at this, there was one glaring problem&amp;hellip;the reporting cluster. If I performed a failover of the distributed AGs to the new 2019 cluster, the reporting cluster would stop getting data. I saw there were two options:&lt;/p>
&lt;ol>
&lt;li>Perform the failover and let the reporting cluster fall out of sync until I could get it everything back in place&lt;/li>
&lt;li>Move the reporting cluster and it&amp;rsquo;s distributed AGs to the receive data before I failed over to the new 2019 cluster and &amp;ldquo;hope&amp;rdquo; things just starting syncing again.&lt;/li>
&lt;/ol>
&lt;p>Either way, the databases on the reporting cluster were going to fall out of sync, so I chose the first option for the lab.&lt;/p>
&lt;p>Now that the decision was made, it was easy peasy to finish the move in the lab. There was only one server left in the old cluster, so my steps were to failover the distributed AGs to the new 2019 cluster (yes, I tested failing back just in case), destroy the 2012 cluster, rebuild the server with Windows 2019, add it to the 2019 WSFC, install SQL Server, and add it as a replica to all the AGs. Yay, everything was done! What’s left was just a bit of clean-up of the reporting cluster distributed AGs, and then I was ready to move to production with the plan.&lt;/p>
&lt;p>I spent the next week writing up all the steps to move to production. There were a lot of moving pieces for production. I had to move the following:&lt;/p>
&lt;ul>
&lt;li>2 WSFC&lt;/li>
&lt;li>6 servers with new OS, fresh SQL Server installs&lt;/li>
&lt;li>5 availability groups with a total of about 385 databases&lt;/li>
&lt;li>5 AGs means 5 temporary distributed AGs to help with the move&lt;/li>
&lt;/ul>
&lt;p>This also meant we needed new IP addresses for the clusters, AG listeners, as well as new names for the clusters, AGs, distributed AGs, and listeners. During my testing I discovered that you can&amp;rsquo;t use the same names for these objects, even when they’re on different servers, which resulted in a lot of legwork to get prepped for the move to production, but I was ready&amp;hellip;or so I thought.&lt;/p>
&lt;h2 id="phase-2-the-part-where-i-broke-dev">Phase 2: The Part Where I Broke Dev&lt;/h2>
&lt;p>Pretty much the entire month of January, I tested and worked through how this would be done in production. I originally targeted February to start the production servers. I was wrong.&lt;/p>
&lt;p>During the final stages of review, a suggestion was made to test an upgrade to Windows Server 2019 on a few development servers, to ensure we wouldn’t hit any hiccups with the OS when it went to production. Our main development servers for Core Q&amp;amp;A have the same hardware and set-up as production, but don&amp;rsquo;t run any AGs - the servers only have copies of the databases for development. In testing the OS against these servers, we’d be able to perform some load testing, and I could make sure our deployment process of the OS would work.&lt;/p>
&lt;p>I picked three servers to test against - two VMs and one physical server. We use &lt;a href="https://theforeman.org/">Foreman&lt;/a> to automatically (re)build servers, which made the process relatively painless to deploy and rebuild servers. Since Windows Server 2019 had never been deployed, it wasn&amp;rsquo;t setup in Foreman. This meant I had to upgrade the servers by hand, as an in-place upgrade - this was done on the two VMs. Aside from some minor issues, everything went according to plan. The OS deployed just fine, and I easily reinstalled SQL Server across the board. The servers were back up and running within a few hours.&lt;/p>
&lt;p>Next, it was time to deploy Windows Server 2019 to a physical server. That&amp;rsquo;s when everything went off the rails.&lt;/p>
&lt;p>As I mentioned, our main dev servers have similar hardware to our production servers, which means we have a drive for the OS, a data drive for SQL with NVMe/PCIe, and possibly a third drive with spinny disks. The physical server I tested the upgrade against had all three drives. I kicked off the rebuild, and about two hours into the process the dreaded blue screen of death hit:&lt;/p>
&lt;p>&lt;img src="https://tarynpivots.com/image/2019/BSOD.jpg" alt="BSOD">&lt;/p>
&lt;p>Very quickly, we spotted the issue. The NVMe drivers didn&amp;rsquo;t work for Windows Server 2019. Oh crap. Thankfully, I have awesome teammates who jumped into a hangout and helped debug. After discussing, we attempted to upgrade the driver to the new one, and, of course, that bombed. Then one of the NVMe drives looked like it failed. After some poking, the &amp;ldquo;failed&amp;rdquo; drive came back online and we were able to get the server back to its 2016 state pre-upgrade attempt.&lt;/p>
&lt;p>Now what?&lt;/p>
&lt;p>We still wanted to move to 2019, but the new driver wasn&amp;rsquo;t working with our NVMe RAID, which meant time to contact both Dell and Intel. With their help, we might be able to get the correct drivers and software to work with our drives and the NVMe RAID. Eventually, we got the necessary bits and had the new drivers in place with software that recognized our RAID. Next, it was time to test another 2019 deployment. After two hours into rebuild #2, I was staring at a 45% completion:&lt;/p>
&lt;p>&lt;img src="https://tarynpivots.com/image/2019/2hUpdates.jpg" alt="Upgrade Progress">&lt;/p>
&lt;p>I tried not to worry and went on to other tasks. At hour 6 of being stuck at 45%, I was concerned. Even though this deployment hadn&amp;rsquo;t finished, it was obviously stuck, so we decided to attempt another fresh rebuild&amp;hellip;#3.&lt;/p>
&lt;p>When rebuild 3 began, everything seemed to be going ok. Once up and rebooted, we were back to a 2016 Windows Server. WTH?!?! This time we also had a freshly formatted 2TB NVMe partition&amp;hellip;meaning all of our SQL data on the NVMe drives was gone. We decided to try once more, this time disabling the PCIe slots in the BIOS and trying another fresh rebuild - this time #4.&lt;/p>
&lt;p>Our confidence level in moving to Windows Server 2019 was quickly dropping, but we wanted to see if we could get it to work. Our 4th rebuild, finally got 2019 installed on the server, but we hit another issue. When the PCIe slots were re-enabled, we only had 2 disks show up in the RSTe manager and in disk management on the server, but everything was showing in the Device Manager.&lt;/p>
&lt;p>Ugh, why are they not matching? There were so many weird problems with this server.&lt;/p>
&lt;p>The next step was to send someone to the data center to physically unplug &amp;amp; drain the power from the box to see if the issues with the PCIe slots would sort itself out - we have done this in the past and it worked. Unfortunately, our trick of draining power didn&amp;rsquo;t work. We still only had two disks show up&amp;hellip;&lt;em>sigh&lt;/em>. That means time for another rebuild.&lt;/p>
&lt;p>This time we were going to try to rebuild the server to 2016 with the old drivers for the SSDs to see if we could at least get it back to the state it was before the upgrade to 2019. The hope was that if we rolled back the driver, the PCIe slots might come back to life and the drives would miraculously work again. Off to rebuild #5.&lt;/p>
&lt;p>After fighting with the server to use the older drivers, I finally got them installed and kicked off another rebuild. Unfortunately, this time we had a failure because the deployment failed to join the server to the domain and we couldn&amp;rsquo;t login to the server&amp;hellip;seriously, the server was possessed.&lt;/p>
&lt;p>Yay, another rebuild (#6) to try to get it on the domain, with Windows Server 2016, and old drivers so the SSDs work. Guess what? This failed again, due to issues joining the domain. Again, my teammates rescued me and got into a hangout to beat the server into submission and get it joined to the network. Once it was finally joined to the domain, there was another freaking issue.&lt;/p>
&lt;p>&lt;img src="https://tarynpivots.com/image/2019/rebuild7.png" alt="Rebuild 7 Failure">&lt;/p>
&lt;p>It was getting ridiculous with this server. I found a way to &lt;a href="https://www.thewindowsclub.com/computer-restarted-unexpectedly-encountered-unexpected-error">hack it back into submission&lt;/a> because at this point anything was worth trying. The OS finally installed, but we had two very dead SSDs. They were showing up as 0GB. We tried upgrading firmware&amp;hellip;nothing worked. The drives were dead. It was time to open a ticket with Intel since the drives were still under warranty. It was now a waiting game, until Intel confirmed the drives were fried.&lt;/p>
&lt;p>While we were waiting for new drives from Intel, my teammate, &lt;a href="https://twitter.com/thefarseeker">Mark Henderson&lt;/a>, spent time fixing up our Foreman deployment to work with 2019 and to bypass the NVMe drives when installing an OS. You know what that means&amp;hellip;more rebuilds. Actually 3 more - one to 2016, one to 2019, and then one back to 2016 for a total of 9 rebuilds of Windows (so far) on a single server.&lt;/p>
&lt;p>You might be wondering why we went back to 2016 after we finally successfully installed 2019?&lt;/p>
&lt;p>This was done because our confidence level in using 2019 with our SSDs was pretty much gone. Intel no longer makes the drivers public, and referred us to Dell, and then Dell sent us back to Intel - it was a back and forth game - which wasn’t a good position to be in for production. Combined with multiple failures and dead SSDs, we didn’t feel comfortable moving forward with Windows Server 2019 on our production servers. We couldn’t risk killing our SSDs due to the install. We knew that 2016 worked with them because Dev had been using it for years, and we&amp;rsquo;d get the benefits of moving off of Windows Server 2012, so we made the decision to skip Windows Server 2019 at this time.&lt;/p>
&lt;p>About 2 weeks later, we finally had new SSDs. It was now time to get them installed and test the deployment process again. Here we go with rebuild #10. Time to hopefully get a clean install of Windows Server 2016 - I spoke too soon. The rebuild failed. No, I&amp;rsquo;m really not joking.&lt;/p>
&lt;p>On top of using Foreman, we use &lt;a href="https://puppet.com/">Puppet&lt;/a> to get our servers in a desired state - the failure this time appeared to be due to a Puppet issue, but there was, of course, nothing in the error log that pointed to the issue. We had yet another failure, and we wanted one clean install on this server, meaning no issues whatsoever. That meant another rebuild to see if we could get an install with zero errors. This server wasn&amp;rsquo;t quite ready to be nice, we hit various issues with the install process and it took 4 more times before we had a clean build of the server for a total of 14 times!! After all the issues, we finally had a dev server almost back to its original state - Windows Server 2016, now it just needed SQL Server and that&amp;rsquo;s super easy to install, right?&lt;/p>
&lt;p>I moved on to installing SQL Server 2017 again, and since this server was terrible on every level we ran into more issues. I have a couple of PowerShell scripts that I use to install SQL Server which work all the time, except on this server. There were errors installing the SqlServer module, then there were really odd issues with SPNs for the server not being tied to the proper account and the server was associated to me - it really was cursed. After a ton of more issues (you really don&amp;rsquo;t want details on everything), we finally had a dev server back in place. All databases were restored, and things were working again.&lt;/p>
&lt;p>Now what? Well, we had another dev server to test this against, this time our NY dev server. We decided to try a clean install against that server, but first we spent about a week trying to fix all of the weird issues we were hitting with the CO dev server&amp;hellip;yes, that means more rebuilds. Once everything was working without issue, it was time to move to NY Dev. Our NY Dev server is critical path for pushes to production, so that rebuild involved a bit of juggling, but eventually I was able to rebuild it and yay, there were no issues with SSDs or really anything else that would be a blocker for moving to production.&lt;/p>
&lt;p>By the time I finally got done with NY Dev, it was about 2 weeks into April, that means it was over two months of working through all the issues with the development deploys. I was totally ready to move this project along and touch production.&lt;/p>
&lt;h2 id="phase-3-time-for-production">Phase 3: Time For Production&lt;/h2>
&lt;p>It was now into April, and I thought this was going to be finished in February, so you could say I was extremely anxious to finish this project and move on to other things. Since our development environment was finally done and it appeared that our deployment process was working, I revisited the plan I wrote in January for our production environment.&lt;/p>
&lt;p>I opened the Google Doc that I initially wrote and started expanding it into a very detailed plan on the deployment process. I spent several days writing out each step for every server we would be moving around. I tried to include everything I could think of. The step for each server even had the code that would be executed on it. This was probably the most detailed plan I had ever written, I attempted to cover everything I would think of. &lt;a href="https://twitter.com/Nick_Craver">Nick Craver&lt;/a>, then added more details because the connection strings for each application would be changing due to moving to new listeners.&lt;/p>
&lt;p>I was ready to go. We were ready to go. The runbook had about &lt;a href="https://twitter.com/tarynpivots/status/1116831409681756160">35 pages of notes and code to execute&lt;/a> in the entire plan.&lt;/p>
&lt;p>Every server had a detailed list of steps including obvious things to more complicated pieces. Inside of the document, you could look up any server and find a list similar to this:&lt;/p>
&lt;p>&lt;!-- raw HTML omitted -->Server Name (Date)&lt;!-- raw HTML omitted -->&lt;/p>
&lt;ol>
&lt;li>Stop Transaction Log backups on primary&lt;/li>
&lt;li>Remove read-only routing from the AGs (this is done on the primary) - including code snippet to run on the primary&lt;/li>
&lt;li>Flush connections in the app&lt;/li>
&lt;li>Disable full backups on primary&lt;/li>
&lt;li>Remove server from the existing AGs on primary&lt;/li>
&lt;li>Evict server from current cluster&lt;/li>
&lt;li>Setup server in Foreman for 2016 deployment, kick off the rebuild&lt;/li>
&lt;li>Make sure puppet is up and running after the rebuild&lt;/li>
&lt;li>Once done rebuilding, add Failover Cluster role to the server (this forces a reboot)&lt;/li>
&lt;li>Create new WSFC (only on the first server in the cluster)&lt;/li>
&lt;li>Once the Cluster Object is created in AD, then move it, if needed, to the proper Cluster Objects OU&lt;/li>
&lt;li>After cluster creation, verify the IP addresses are correct and edit them, as needed&lt;/li>
&lt;li>Make sure the WFSC cluster object has permissions to create/delete computer objects in AD&lt;/li>
&lt;li>Install Dell NVMe tools&lt;/li>
&lt;li>Install SQL Server – if the tempdb is still located on D:\Data - delete all the files first&lt;/li>
&lt;li>Turn on trace flags – restart SQL service&lt;/li>
&lt;li>Execute scripts to add logins / users / stored procs / linked server&lt;/li>
&lt;li>Create New AGs&lt;/li>
&lt;li>For each AG create new temporary distributed AG aka the TAG (our temporary distributed availability group for the migration) - created on the current primary and then the secondary&lt;/li>
&lt;li>At this point everything should be syncing to the newly built server, and it’s a single server on its own cluster&lt;/li>
&lt;li>Re-create sql jobs&lt;/li>
&lt;li>Disable backup jobs for user databases&lt;/li>
&lt;li>Re-enable backup jobs on primary server in old cluster&lt;/li>
&lt;/ol>
&lt;p>While the steps were different for each server, this was basically what I needed to do. By the end of Wednesday, April 17th, 2019, if we were lucky I&amp;rsquo;d have moved 3 servers to the new clusters, and would have SQL Server installed with everything syncing as I tested. The goal by the end of Day 3 was to have things looks a bit like this:&lt;/p>
&lt;p>&lt;img src="https://tarynpivots.com/image/2019/Day3Goal.jpg" alt="Goal by Day 3">&lt;/p>
&lt;p>I &lt;a href="https://meta.stackexchange.com/revisions/326718/3">announced&lt;/a> we were going to start on &lt;a href="https://twitter.com/Nick_Craver/status/1116445650453966857">Monday, April 15th&lt;/a> and finish the following week. I felt pretty confident that we were ready for production. There was absolutely no way we&amp;rsquo;d have issues like we did on the development servers, right? Wrong. Oh did we ever hit unexpected issues.&lt;/p>
&lt;h3 id="day-1">Day 1&lt;/h3>
&lt;p>The very &lt;a href="https://twitter.com/tarynpivots/status/1117724066884931584">early morning of April 15th rolled around&lt;/a>, and I kicked off the plan for the very first server, NY-SQL03, the NY secondary in the SE cluster. Everything went according to plan, Windows installed, SQL Server installed, new cluster set-up - all 4 temporary distributed availability groups (aka our TAGs) were in place. We had all databases syncing and reporting green in Opserver at the end of the end. Day 1 done, so far, so good - one server down, 5 more to go.&lt;/p>
&lt;h3 id="day-2">Day 2&lt;/h3>
&lt;p>The day started just as early because if things went well, I was going to tackle two servers, NY-SQL01, the NY secondary in the SO cluster, as well as CO-SQL03, our Colorado remote secondary in the SE cluster. I kicked off the rebuild on both servers, and NY-SQL01 was back in working order within a few hours with everything syncing as we expected. The other server, CO-SQL03, was another story.&lt;/p>
&lt;p>While CO-SQL03 was rebuilding, I started to notice some weirdness&amp;hellip;yes, weirdness (that&amp;rsquo;s a technical term, right?) with the server I rebuilt the day before - NY-SQL03. As a quick reminder, this server was currently the only node in the new 2016 cluster. It had about 375 databases which were in 4 availability groups, and 4 distributed availability groups - the distributed AGs were being used to keep the data in sync between the old 2012 cluster and the new cluster. The breakdown of number of databases per AG was:&lt;/p>
&lt;ul>
&lt;li>&lt;code>AG-NYOnly&lt;/code> - 6 databases syncing via distributed AG - &lt;code>NYOnly_TAG&lt;/code>&lt;/li>
&lt;li>&lt;code>AG-Misc&lt;/code> - 10 databases syncing via distributed AG - &lt;code>Misc_TAG&lt;/code>&lt;/li>
&lt;li>&lt;code>AG-Chat&lt;/code> - 3 databases syncing via distributed AG - &lt;code>Chat_TAG&lt;/code>&lt;/li>
&lt;li>&lt;code>AG-SENetwork&lt;/code> - 354 databases syncing via distributed AG - &lt;code>SENetwork_TAG&lt;/code>&lt;/li>
&lt;/ul>
&lt;p>Now, that you&amp;rsquo;ve had a reminder of what we had on the server, let me explain what I meant by &amp;ldquo;weirdness&amp;rdquo;. One of the primary ways we monitor our servers is through &lt;a href="https://github.com/opserver/Opserver">Opserver&lt;/a>. After hours of what appeared to be databases syncing without any issues I started to notice that we were hitting blocking when trying to get the status of the availability groups and databases when querying the DMVs as Opserver does. And not just a little bit of blocking. This was significant blocking on everything. We couldn&amp;rsquo;t get any details in SSMS on the databases or availability groups. If you attempted to open any details in SSMS (AGs, databases), it would lock your session and you&amp;rsquo;d have to kill it in Task Manager.&lt;/p>
&lt;p>Everything we tried to get the state of the AGs was locking up, even &lt;a href="https://twitter.com/Nick_Craver/status/1118316671721250816">directly querying the DMVs in SSMS&lt;/a>. We were seeing tons of &lt;a href="https://www.sqlskills.com/help/waits/hadr_ag_mutex/">HADR_AG_MUTEX&lt;/a> waits:&lt;/p>
&lt;p>&lt;img src="https://tarynpivots.com/image/2019/HADR_blocking.png" alt="Waits NY-SQL03">&lt;/p>
&lt;p>In addition to the locking, we noticed a lot of inconsistencies in what was being returned via the queries. One minute it looked like the availability groups were fine, the next it was in a not-synchronizing state. We realized that we couldn&amp;rsquo;t trust the state of the AGs on the server. After hours of looking at this and trying to debug, I decided to drop our temporary distributed AGs, the TAGs, to see if rebuilding them would work because nothing else was working at this point.&lt;/p>
&lt;p>I started recreating some of the TAGs to see if things would sync, as I created each one, they would start to sync, but we were still hitting significant waits and locking.&lt;/p>
&lt;p>We tried restarting the SQL Server service. We tried stopping the availability groups in the WSFC. We were seeing errors in the logs that the AGs were in a failed state, we couldn&amp;rsquo;t query the DMVs, we didn&amp;rsquo;t have any idea what was happening. It looked like the 2 distributed AGs that were small were working, even though checking their health was very slow.&lt;/p>
&lt;p>It was time to try to recreate the &lt;code>SENetwork_TAG&lt;/code>, the one with the 354 databases. Early on in this process, we stopped Transaction Log backups and Daily Backups, so we had a couple of options:&lt;/p>
&lt;ol>
&lt;li>&lt;a href="https://docs.microsoft.com/en-us/sql/database-engine/availability-groups/windows/automatic-seeding-secondary-replicas?view=sql-server-2017">Autoseed the databases&lt;/a> as you can with availability groups&lt;/li>
&lt;li>Use the old backup of the SE Network databases (the one from before we started the migration process), and restore it to the NY-SQL03 server, letting it get caught up with the AG syncing&lt;/li>
&lt;li>Take a new backup of the databases in the AG, restore it to NY-SQL03 again, and then join it to the AG to let things start syncing.&lt;/li>
&lt;/ol>
&lt;p>Considering the slow querying of the DMVs and the AG health, we went with number 3. I took a recent backup of the database from the primary server in the old cluster and restored it to the new server with &lt;code>NORECOVERY&lt;/code>. The thinking was that this would prevent overloading the server by automatically trying to seed 354 the databases all at once. Once all the databases were restored, we would execute the following on each one:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-sql" data-lang="sql">&lt;span class="k">ALTER&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">DATABASE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">database_name&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">SET&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">HADR&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Availability&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">Group&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">AG&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">SENetwork&lt;/span>&lt;span class="p">];&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;p>Problem was, this was really slow. Doing this for each database was taking a &lt;a href="https://twitter.com/tarynpivots/status/1119303103466000385">couple of minutes per database&lt;/a> and we have 354 of them. The waits we were hitting were downright unbelievable:&lt;/p>
&lt;p>&lt;img src="https://tarynpivots.com/image/2019/wait_stats.jpg" alt="Wait stats">&lt;/p>
&lt;p>The amount of time to execute the command against each database exponentially increased as we were adding more and more databases. It was going to take hours to get through them all. It was super late in the day, and Nick and I had been debugging and working in a hangout for many, many hours. Nick quickly wrote a script to loop through all of the databases in the &lt;code>SENetwork_AG&lt;/code> and execute the SQL above. This automated the process which allowed it to run overnight, and more importantly allowed us to step away from our desks after far too many hours. We hoped everything would be in the AGs in the morning, so we could debug some of the issues we were seeing and potentially move forward with the upgrades.&lt;/p>
&lt;h3 id="days-3-7">Days 3-7&lt;/h3>
&lt;p>Yes, that really says Days.&lt;/p>
&lt;p>The script eventually finished and we had all databases in the AG, however, we were still seeing tons of waits. I was chatting with some of the users on our &lt;a href="https://dba.stackexchange.com/">Database Administrators Stack Exchange&lt;/a> site, and there were suggestions to &lt;a href="https://www.sqlskills.com/help/waits/parallel_redo_flow_control/">disable parallel redo&lt;/a> on the server, as well as other things. We tried everything we could think of, and still didn&amp;rsquo;t know if things were syncing because we couldn&amp;rsquo;t get an accurate read from the DMVs or get passed the waits. Since nothing was working, it was time to open a ticket with Microsoft. Our ticket was opened, the server was creating plenty of mini-dumps that went sent along with memory dumps, and now it was time to wait for help.&lt;/p>
&lt;p>A quick aside: while waiting for things to get moving with Microsoft, we attempted to get CO-SQL03 back in a good state. We take local &lt;a href="https://docs.microsoft.com/en-us/sql/relational-databases/backup-restore/copy-only-backups-sql-server?view=sql-server-2017">copy-only&lt;/a> backups in Colorado, so we don&amp;rsquo;t have to deal with trying to move terabytes of datafiles from NY to CO in the event of an issue. All of that typically is fine, but due to the issues, we ended up being down for far longer than expected. This meant our backups in CO were very far behind the current state of things in production. In order to get CO-SQL03 in working order, we needed to copy all of the database backups from NY to CO, which was going to take a really, really long time. Good thing we weren&amp;rsquo;t in a hurry, huh? We kicked off a process to copy the current backups from NY and get them moved to CO. With &lt;a href="https://twitter.com/shanemadden">Shane Madden&lt;/a> and Nick Craver’s quick thinking, we copied all of the files in less than a day. Now that we had good backups, I restored all of the databases in &lt;code>NORECOVERY&lt;/code> so they would be ready whenever NY-SQL03 box was back up and running.&lt;/p>
&lt;p>Our Microsoft ticket was forwarded and we began working with one of our DBA.SE users who happens to work for Microsoft, &lt;a href="https://dba.stackexchange.com/users/41723/sean-gallardy">Sean Gallardy&lt;/a>. At this point, it was time to go back to the regularly scheduled painful upgrade process. Based on the memory dumps, one of the first things Sean suggested was to make sure that auto-seeding of the databases in the &lt;code>SENetwork_TAG&lt;/code> was disabled, so we executed:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-sql" data-lang="sql">&lt;span class="k">ALTER&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">AVAILABILITY&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">GROUP&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">SENetwork_TAG&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">MODIFY&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">REPLICA&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">ON&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;NY-SQL03&amp;#39;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">WITH&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">SEEDING_MODE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">MANUAL&lt;/span>&lt;span class="p">)&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;p>While this prevented the databases from automatically being seeded, it didn’t resolve our issue. We were still failing to sync databases on the new cluster, and we were dropping mini-dumps like crazy on NY-SQL03. It was back to the drawing board with ideas to fix all the things. Nick and I got onto a really long hangout with Sean (thanks Sean), to debug and work through various ways to fix the problem. Eventually, Sean suggested that we try the following:&lt;/p>
&lt;ol>
&lt;li>Destroy the temporary distributed availability group (TAG) for the &lt;code>SENetwork&lt;/code>, since that was our problem child&lt;/li>
&lt;li>Destroy the availability group on the new cluster&lt;/li>
&lt;li>Manually restore the databases to both NY-SQL03 and CO-SQL03 in a restoring state&lt;/li>
&lt;li>Recreate the temporary distributed availability group for the &lt;code>SENetwork&lt;/code>&lt;/li>
&lt;li>Add each database to the availability groups on both NY-SQL03 and CO-SQL03 very slowly so they could sync&lt;/li>
&lt;/ol>
&lt;p>That&amp;rsquo;s what we did. Another really slow process, and when I say slow I mean &lt;a href="https://twitter.com/tarynpivots/status/1119407499453452288">2-4 databases at a time&lt;/a> - and we had 354 to add, so it was painfully slow, but it worked.&lt;/p>
&lt;p>We were hitting a &lt;a href="https://support.microsoft.com/en-us/help/4501797/fix-data-movement-to-dag-forwarder-does-not-resume-automatically-after">bug with SQL Server&lt;/a>. The bug had to do with distributed availability groups that had more than 15 databases and, of course, we had that. Our &lt;a href="https://twitter.com/tarynpivots/status/1119576626272980997">distributed availability group contained 354 databases&lt;/a> and it couldn&amp;rsquo;t handle that many, resulting in it timing out. We got a workaround by following the steps above were able to move forward.&lt;/p>
&lt;p>By the end of the week we had &lt;a href="https://twitter.com/tarynpivots/status/1119430961010135041">two new clusters up and receiving data from the old 2012 clusters&lt;/a>. It was now time to finish moving pieces around so we could perform the failover of the temporary distributed availability groups. Even though we lost several days working through bugs, we were in a better, more stable state and could plan the date of the failover - if everything else went according to plan, it would be in just a &lt;a href="https://twitter.com/tarynpivots/status/1120152909331566595">few days&lt;/a>. To help visualize where we were at the end of this week, our servers were in this state:&lt;/p>
&lt;p>&lt;img src="https://tarynpivots.com/image/2019/EndofWeek1.jpg" alt="Week 1 State of Servers">&lt;/p>
&lt;p>After a pretty quiet weekend, only a few minor hiccups with data syncing, we were ready to pick up and go full steam ahead to wrap things up.&lt;/p>
&lt;h3 id="day-8">Day 8&lt;/h3>
&lt;p>Monday morning rolled around and there were &lt;a href="https://twitter.com/tarynpivots/status/1120335610307235841">3 servers left and one huge failover&lt;/a> to do. Surely, we had hit all of the bugs and it was going to be a clear path to the finish line, right?&lt;/p>
&lt;p>I started the day by evicting CO-SQL01 from its old cluster and kicking off the rebuild process. Ugh, and I hit some issues. The automated process, Foreman, didn&amp;rsquo;t want to work without some poking and prodding to get it moving, then the server didn&amp;rsquo;t want to find the Windows Updates it needed, but eventually the rebuild finished. I was able to install SQL Server, restore backups in a &lt;code>NORECOVERY&lt;/code> state, apply all the transaction logs to bring it up to current state, and just like that, we had syncing data.&lt;/p>
&lt;p>We finally had 4 servers in the new clusters, and they were reporting green everywhere.&lt;/p>
&lt;h3 id="day-9---failover-day">Day 9 - Failover Day&lt;/h3>
&lt;p>The big day was here. It was time to perform the failover of 5 temporary distributed availability groups, with hopefully very little downtime. I previously tested the whole process in the lab, but had never done this in production. And considering all the problems we hit up to now, I was feeling just a &lt;a href="https://twitter.com/tarynpivots/status/1120809488011780096">wee bit stressed out&lt;/a>.&lt;/p>
&lt;p>The morning of the failover, we spent time going over the steps that would be executed that night. We wanted to make sure that we had accounted for all of the application side changes that were needed as well. The complexity of this whole thing wasn&amp;rsquo;t limited to just failing over the SQL Servers. We also needed to rebuild and push out new versions of all of our applications and services due to changes to the connection strings.&lt;/p>
&lt;p>You might be wondering why we needed to do that? All of our application connection strings point to global listeners and since we had new clusters with new AGs/distributed AGs - they all needed to be created with new listeners (IP Addresses) and new names. This means that prior to the failover, the applications were pointing to the old listeners. In order for them to work when we failed over, the apps needed to be rebuilt to production to point to the new listeners.&lt;/p>
&lt;p>We also needed to be very deliberate about the order of things on the failover day.&lt;/p>
&lt;p>The first thing we considered were exceptions. We have a database called &lt;code>NY.Exceptions&lt;/code> which we use to capture errors that occur throughout the network. We needed to be sure it would still capture errors, in the event the failover failed. To do this, we removed the &lt;code>NY.Exceptions&lt;/code> database from both the old and new AGs and made sure the database was writable on both the new and the old clusters. We also added both databases to our monitoring in Opserver to catch lingering applications pointing to the wrong place. This would let us collect errors, even if things went wrong with a failover.&lt;/p>
&lt;p>Next, we had to think about how to push our application changes to production. We use &lt;a href="https://www.jetbrains.com/teamcity/">Team City&lt;/a> to deploy our applications, which has a database on SQL Server. We needed to be sure that Team City was available before we moved forward. As a result, the &lt;code>NYOnly_AG&lt;/code> was the very first one to failover. We pushed a change to the connection string from &lt;code>SQL-NYOnly_AG&lt;/code> to &lt;code>SQLAG-NYOnly&lt;/code> and built out the application. Once that was done, it was time to do the distributed availability group failover.&lt;/p>
&lt;p>Based on my testing and the &lt;a href="https://docs.microsoft.com/en-us/sql/database-engine/availability-groups/windows/configure-distributed-availability-groups?view=sql-server-2017#failover">Microsoft Docs&lt;/a> performing a failover of a distributed availability group is a manual process. The code needs to be executed on the current primary server, validated, then more code has to be executed, then validated, and repeat until it’s all done. In my huge planning document, I wrote all the code to perform the failover steps. Now it was time to execute them. Since we were going to be failing over the temporary distributed availability group called, &lt;code>NYOnly_TAG&lt;/code> we executed the following steps:&lt;/p>
&lt;ol>
&lt;li>
&lt;p>On the current global primary server in the DAG (NY-SQL04) change the DAG to &lt;code>SYNCHRONOUS_COMMIT&lt;/code>&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-sql" data-lang="sql">&lt;span class="w"> &lt;/span>&lt;span class="k">ALTER&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">AVAILABILITY&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">GROUP&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">NYOnly_TAG&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">MODIFY&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">AVAILABILITY&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">GROUP&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">ON&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;NYOnly_AG&amp;#39;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">WITH&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">AVAILABILITY_MODE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">SYNCHRONOUS_COMMIT&lt;/span>&lt;span class="p">),&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;AG-NYOnly&amp;#39;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">WITH&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">AVAILABILITY_MODE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">SYNCHRONOUS_COMMIT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;/li>
&lt;li>
&lt;p>Verify the commit state is &lt;code>SYNCHRONIZED&lt;/code> on the global primary (NY-SQL04):&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-sql" data-lang="sql">&lt;span class="w"> &lt;/span>&lt;span class="k">SELECT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ag&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">name&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">drs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">database_id&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">drs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">group_id&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">drs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">replica_id&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">drs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">synchronization_state_desc&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">drs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">end_of_log_lsn&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">FROM&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">sys&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">dm_hadr_database_replica_states&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">drs&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">INNER&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">JOIN&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">sys&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">availability_groups&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ag&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">ON&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">drs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">group_id&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ag&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">group_id&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;/li>
&lt;li>
&lt;p>Once we&amp;rsquo;re in a &lt;code>SYNCHRONIZED&lt;/code> state, set the global primary (NY-SQL04) to a &lt;code>SECONDARY&lt;/code> role&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-sql" data-lang="sql">&lt;span class="w"> &lt;/span>&lt;span class="k">ALTER&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">AVAILABILITY&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">GROUP&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">NYOnly_TAG&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">SET&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">ROLE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">SECONDARY&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;/li>
&lt;li>
&lt;p>Now the AG is not available for write traffic. Test that we&amp;rsquo;re ready to failover by executing the following on both the former global primary (NY-SQL04) and the soon to be new global primary (NY-SQL03). This query verifies that the &lt;a href="https://docs.microsoft.com/en-us/sql/relational-databases/backup-restore/recover-to-a-log-sequence-number-sql-server?view=sql-server-2017#LSNs">LSN&lt;/a> is the same on both servers.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-sql" data-lang="sql">&lt;span class="w"> &lt;/span>&lt;span class="k">SELECT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ag&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">name&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">drs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">database_id&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">drs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">group_id&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">drs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">replica_id&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">drs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">synchronization_state_desc&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">drs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">end_of_log_lsn&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">FROM&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">sys&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">dm_hadr_database_replica_states&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">drs&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">INNER&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">JOIN&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">sys&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">availability_groups&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ag&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">ON&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">drs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">group_id&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ag&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">group_id&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;/li>
&lt;li>
&lt;p>Perform the failover to the new global primary (NY-SQL03)&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-sql" data-lang="sql">&lt;span class="w"> &lt;/span>&lt;span class="k">ALTER&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">AVAILABILITY&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">GROUP&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">NYOnly_TAG&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">FORCE_FAILOVER_ALLOW_DATA_LOSS&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;/li>
&lt;li>
&lt;p>Change the state of the distributed availability group back to &lt;code>ASYNCHRONOUS_COMMIT&lt;/code> this is done on the new global primary (NY-SQL03)&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-sql" data-lang="sql">&lt;span class="w"> &lt;/span>&lt;span class="k">ALTER&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">AVAILABILITY&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">GROUP&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">NYOnly_TAG&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">MODIFY&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">AVAILABILITY&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">GROUP&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">ON&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;NYOnly_AG&amp;#39;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">WITH&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">AVAILABILITY_MODE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ASYNCHRONOUS_COMMIT&lt;/span>&lt;span class="p">),&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;AG-NYOnly&amp;#39;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">WITH&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">AVAILABILITY_MODE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ASYNCHRONOUS_COMMIT&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;/li>
&lt;/ol>
&lt;p>At this point, our first distributed availability group had failed over and things were writable again. Only 4 more to go!&lt;/p>
&lt;p>We could capture exceptions and with Team City up an running, it was time to tackle the next DAG, &lt;code>Chat_TAG&lt;/code>. We performed the same steps listed above, on that DAG. Chat was selected to be done next, because we use our chat platform internally, and having it accessible is very important. When I&amp;rsquo;m doing regular maintenance on the SQL Servers, the &lt;code>Chat_AG&lt;/code> is one of the first AGs we failover, and that the pattern held for the DAG failover. After executing all the steps above, we successfully &lt;a href="https://twitter.com/tarynpivots/status/1120838257200156672">failed over Chat&lt;/a>.&lt;/p>
&lt;p>2 down, 3 to go! Time for the more critical stuff to be tackled.&lt;/p>
&lt;p>To minimize impact for users, we did the failover during the latter part of the day. Next up in the failover process was the DAG that contained the &lt;code>Careers&lt;/code> database. By doing this, it would mean the end of syncing to the reporting cluster. We knew it was going to happen eventually, and held off as long as we could. Our stakeholders knew this was part of this failover process. The goal was to be back online as soon as possible once the entire failover process was complete.&lt;/p>
&lt;p>Before starting the failover, the old distributed availability group between the 2012 cluster and the reporting cluster was destroyed. This was done because we knew the syncing to the reporting cluster would stop when we failed over. There was no need to have this extra layer of complexity and strain on the NY-SQL04 server. After the &lt;code>HighAvailability_DAG&lt;/code> was destroyed, we set the Jobs area of the site to read-only, replaced all the connections strings in the applications, and then rebuilt them. It was then time failover the new DAG, &lt;code>Misc_TAG&lt;/code>. Again, everything failed over without any issues. The &lt;code>Careers&lt;/code> database was writable again, we moved Jobs out of read-only, and we were now more than half-way done with the failover.&lt;/p>
&lt;p>The final two failovers were for the &lt;code>StackOverflow_TAG&lt;/code> and &lt;code>SENetwork_TAG&lt;/code> distributed AGs. These meant that we were going to have a bit of downtime. We were also planning on doing them in rapid succession. These two distributed AGs contain all the databases that handle the entire network of sites, and we wanted to fail them over as fast as possible to reduce the downtime. We pushed all new connection strings, the apps were rebuilt, and we were ready. All sites were set to read-only and it was go time.&lt;/p>
&lt;p>First up, was the &lt;code>StackOverflow_TAG&lt;/code>, as this only had 5 databases, and we figured it&amp;rsquo;d be quicker than our problem DAG, &lt;code>SENetwork_TAG&lt;/code> - aka the one with 354 databases. I destroyed the old distributed availability group going to the reporting cluster, and we began the process to failover this DAG. Thankfully we hit no issues with it.&lt;/p>
&lt;p>We were in the home stretch for the failovers with only one left&amp;hellip;the big one. We hit so many issues with this distributed availability group when trying to get it set up, we were concerned about what would happen when we failed over. Unable to wait any longer, I kicked off the steps above to start the failover. It moved to &lt;code>SYNCHRONOUS_COMMIT&lt;/code> without any problems, the databases on NY-SQL04 all reported to be in a &lt;code>SYNCHRONIZED&lt;/code> state, and I moved the server to be a &lt;code>SECONDARY&lt;/code> role in the DAG.&lt;/p>
&lt;p>So far so good. Now it was time to verify the LSNs between the two servers using:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;span class="lnt">8
&lt;/span>&lt;span class="lnt">9
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-sql" data-lang="sql">&lt;span class="k">SELECT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ag&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">name&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">drs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">database_id&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">drs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">group_id&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">drs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">replica_id&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">drs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">synchronization_state_desc&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">drs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">end_of_log_lsn&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">FROM&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">sys&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">dm_hadr_database_replica_states&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">drs&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">INNER&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">JOIN&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">sys&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">availability_groups&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ag&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">ON&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">drs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">group_id&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ag&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">group_id&lt;/span>&lt;span class="p">;&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;p>Executing that query on both servers (NY-SQL04 and NY-SQL03) and trying to verify that the LSNs were the same on 354 databases was not what I’d call an enjoyable process. We would verify, then re-verify and something would change, so we&amp;rsquo;d verify again, and again. It took some time to figure out if we were in a good state to failover. Our goal was to not lose any data, but trying to make sure we had a match against all the databases was pretty tough. Once we were reasonably confident, we made the call that it was time to finish the failover of this DAG, so I executed the commands to failover the &lt;code>SENetwork_TAG&lt;/code>. Once the failover command was executed, the new primary (NY-SQL03) came up just fine, and was responsive for us to query it. We were done with all of the temporary distributed availability groups, and all of the sites went back to read/write. &lt;a href="https://twitter.com/tarynpivots/status/1120859302296637440">Failover done!&lt;/a>&lt;/p>
&lt;p>We thought we were in the clear, but the former primary (NY-SQL04) was very, very angry. After the failover from NY-SQL04, the server was completely unresponsive to anything. All the databases locked, CPU spiked, we were hitting HADR waits, and we didn’t get any sort of response for more than 25 minutes. Thankfully, we weren&amp;rsquo;t overly concerned because we had a new primary that was working, and this would be completely rebuilt to Windows Server 2016. We made the decision to just stop the SQL Server service until I got around to the rebuild.&lt;/p>
&lt;p>All of the failovers were complete and everything was running on the new Windows Server 2016 clusters. Our servers looked like this:&lt;/p>
&lt;p>&lt;img src="https://tarynpivots.com/image/2019/PostFailoverServerState.jpg" alt="Post Failover Night State of Servers">&lt;/p>
&lt;p>We still had two servers in the old 2012 clusters. NY-SQL04 wasn&amp;rsquo;t functioning as a SQL Server because we shutdown the SQL service post failover, and NY-SQL02 was a member of the temporary distributed availability group we just failed over. Those servers were going to be rebuilt the next day, so our focus turned to the reporting cluster. That cluster is used internally for applications, and we needed to get it back up as soon as possible for use.&lt;/p>
&lt;p>Initially, we thought that if we recreated the distributed availability groups with the reporting clusters as a member, things would just pick back up and sync. We executed the following to recreate the two distributed AGs with the new cluster and reporting cluster:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;span class="lnt">36
&lt;/span>&lt;span class="lnt">37
&lt;/span>&lt;span class="lnt">38
&lt;/span>&lt;span class="lnt">39
&lt;/span>&lt;span class="lnt">40
&lt;/span>&lt;span class="lnt">41
&lt;/span>&lt;span class="lnt">42
&lt;/span>&lt;span class="lnt">43
&lt;/span>&lt;span class="lnt">44
&lt;/span>&lt;span class="lnt">45
&lt;/span>&lt;span class="lnt">46
&lt;/span>&lt;span class="lnt">47
&lt;/span>&lt;span class="lnt">48
&lt;/span>&lt;span class="lnt">49
&lt;/span>&lt;span class="lnt">50
&lt;/span>&lt;span class="lnt">51
&lt;/span>&lt;span class="lnt">52
&lt;/span>&lt;span class="lnt">53
&lt;/span>&lt;span class="lnt">54
&lt;/span>&lt;span class="lnt">55
&lt;/span>&lt;span class="lnt">56
&lt;/span>&lt;span class="lnt">57
&lt;/span>&lt;span class="lnt">58
&lt;/span>&lt;span class="lnt">59
&lt;/span>&lt;span class="lnt">60
&lt;/span>&lt;span class="lnt">61
&lt;/span>&lt;span class="lnt">62
&lt;/span>&lt;span class="lnt">63
&lt;/span>&lt;span class="lnt">64
&lt;/span>&lt;span class="lnt">65
&lt;/span>&lt;span class="lnt">66
&lt;/span>&lt;span class="lnt">67
&lt;/span>&lt;span class="lnt">68
&lt;/span>&lt;span class="lnt">69
&lt;/span>&lt;span class="lnt">70
&lt;/span>&lt;span class="lnt">71
&lt;/span>&lt;span class="lnt">72
&lt;/span>&lt;span class="lnt">73
&lt;/span>&lt;span class="lnt">74
&lt;/span>&lt;span class="lnt">75
&lt;/span>&lt;span class="lnt">76
&lt;/span>&lt;span class="lnt">77
&lt;/span>&lt;span class="lnt">78
&lt;/span>&lt;span class="lnt">79
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-sql" data-lang="sql">&lt;span class="c1">-- run this on NY-SQL03
&lt;/span>&lt;span class="c1">&lt;/span>&lt;span class="k">CREATE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">AVAILABILITY&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">GROUP&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">Misc_DAG&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">WITH&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">DISTRIBUTED&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">AVAILABILITY&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">GROUP&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">ON&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;AG-Misc&amp;#39;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">WITH&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">LISTENER_URL&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;&amp;lt;listener&amp;gt;&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">AVAILABILITY_MODE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ASYNCHRONOUS_COMMIT&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">FAILOVER_MODE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">MANUAL&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">SEEDING_MODE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">AUTOMATIC&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">),&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;HighAvailability_RAG&amp;#39;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">WITH&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">LISTENER_URL&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;&amp;lt;listener&amp;gt;&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">AVAILABILITY_MODE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ASYNCHRONOUS_COMMIT&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">FAILOVER_MODE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">MANUAL&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">SEEDING_MODE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">AUTOMATIC&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">GO&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="c1">-- run this on NY-RPTSQL01
&lt;/span>&lt;span class="c1">&lt;/span>&lt;span class="k">ALTER&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">AVAILABILITY&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">GROUP&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">Misc_DAG&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">JOIN&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">AVAILABILITY&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">GROUP&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">ON&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;AG-Misc&amp;#39;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">WITH&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">LISTENER_URL&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;&amp;lt;listener&amp;gt;&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">AVAILABILITY_MODE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ASYNCHRONOUS_COMMIT&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">FAILOVER_MODE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">MANUAL&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">SEEDING_MODE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">AUTOMATIC&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">),&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;HighAvailability_RAG&amp;#39;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">WITH&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">LISTENER_URL&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;&amp;lt;listener&amp;gt;&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">AVAILABILITY_MODE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ASYNCHRONOUS_COMMIT&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">FAILOVER_MODE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">MANUAL&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">SEEDING_MODE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">AUTOMATIC&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">GO&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="c1">-- run this on NY-SQL01
&lt;/span>&lt;span class="c1">&lt;/span>&lt;span class="k">CREATE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">AVAILABILITY&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">GROUP&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">StackOverflow_DAG&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">WITH&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">DISTRIBUTED&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">AVAILABILITY&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">GROUP&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">ON&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;AG-StackOverflow&amp;#39;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">WITH&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">LISTENER_URL&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;&amp;lt;listener&amp;gt;&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">AVAILABILITY_MODE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ASYNCHRONOUS_COMMIT&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">FAILOVER_MODE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">MANUAL&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">SEEDING_MODE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">AUTOMATIC&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">),&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;StackOverflow_RAG&amp;#39;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">WITH&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">LISTENER_URL&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;&amp;lt;listener&amp;gt;&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">AVAILABILITY_MODE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ASYNCHRONOUS_COMMIT&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">FAILOVER_MODE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">MANUAL&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">SEEDING_MODE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">AUTOMATIC&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">GO&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="c1">-- run this on NY-RPTSQL01
&lt;/span>&lt;span class="c1">&lt;/span>&lt;span class="k">ALTER&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">AVAILABILITY&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">GROUP&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">StackOverflow_DAG&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">JOIN&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">AVAILABILITY&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">GROUP&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">ON&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;AG-StackOverflow&amp;#39;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">WITH&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">LISTENER_URL&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;&amp;lt;listener&amp;gt;&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">AVAILABILITY_MODE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ASYNCHRONOUS_COMMIT&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">FAILOVER_MODE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">MANUAL&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">SEEDING_MODE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">AUTOMATIC&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">),&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;StackOverflow_RAG&amp;#39;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">WITH&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">LISTENER_URL&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;&amp;lt;listener&amp;gt;&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">AVAILABILITY_MODE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ASYNCHRONOUS_COMMIT&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">FAILOVER_MODE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">MANUAL&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">SEEDING_MODE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">AUTOMATIC&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">GO&lt;/span>&lt;span class="w"> &lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;p>After executing this and recreating the distributed AGs, we realized very quickly we were wrong about them starting to sync again. The reporting cluster wouldn&amp;rsquo;t sync. We tried kicking the databases with various commands, and even tried failing the reporting cluster over to the secondary server in Colorado and then failing back. Nothing was working. After checking the logs on the reporting cluster primary, NY-RPTSQL01, we found lots of errors:&lt;/p>
&lt;blockquote>
&lt;p>Msg 3456, Level 21, State 1, Line 22&lt;/p>
&lt;/blockquote>
&lt;blockquote>
&lt;p>Could not redo log record (8278:53574:4), for transaction ID (0:8787310), on page (1:10082), allocation unit 72057594040811520, database &amp;lsquo;CareersAuth&amp;rsquo; (database ID 23). Page: LSN = (8277:108566:2), allocation unit = 72057594040811520, type = 1. Log: OpCode = 6, context 1, PrevPageLSN: (8278:53568:2). Restore from a backup of the database, or repair the database.&lt;/p>
&lt;/blockquote>
&lt;blockquote>
&lt;p>Msg 3313, Level 21, State 1, Line 22&lt;/p>
&lt;/blockquote>
&lt;blockquote>
&lt;p>During redoing of a logged operation in database &amp;lsquo;CareersAuth&amp;rsquo;, an error occurred at log record ID (8278:53574:4). Typically, the specific failure is previously logged as an error in the operating system error log. Restore the database from a full backup, or repair the database.&lt;/p>
&lt;/blockquote>
&lt;blockquote>
&lt;p>Msg 3414, Level 21, State 4, Line 22&lt;/p>
&lt;/blockquote>
&lt;blockquote>
&lt;p>An error occurred during recovery, preventing the database &amp;lsquo;CareersAuth&amp;rsquo; (23:0) from restarting. Diagnose the recovery errors and fix them, or restore from a known good backup. If errors are not corrected or expected, contact Technical Support.&lt;/p>
&lt;/blockquote>
&lt;p>Well, that stinks. It meant we&amp;rsquo;d have to rebuild the AGs on the Reporting Servers using a recent backup, and then try to get them to synchronize. We started restoring the databases that were impacted in a &lt;code>NORECOVERY&lt;/code> state with a local copy so the restore process didn&amp;rsquo;t too long. Once the databases were restored, we executed the following on each database to get them syncing again:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-sql" data-lang="sql">&lt;span class="k">Alter&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">Database&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">database_name&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">Set&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">HADR&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Availability&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">Group&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">distributed_AG_Name&lt;/span>&lt;span class="p">];&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;p>While this all worked great for the primary reporting server, NY-RPTSQL01, we were not as lucky with the secondary server in the reporting cluster, CO-RPTSQL01. We already suffer from throughput issues with CO-RPTSQL01 and all of the de-synchronizing put all of the databases in a state that couldn&amp;rsquo;t be recovered. Unfortunately, all the databases would also have to be rebuilt by restoring copies, then altering them to set them as a part of the availability group. The big problem was we didn&amp;rsquo;t have a local copy in Colorado to restore due to all the other server moves (we had turned off daily backups), so we were going to have to copy the files from NY to CO to then restore on CO-RPTSQL01. That might not be a big deal normally, but we have databases that are several TBs and the move was going to take a while. I decided to call Day 9 done, and pick that process up in the morning, along with other clean-up.&lt;/p>
&lt;h3 id="day-10---finishing-touches">Day 10 - Finishing Touches&lt;/h3>
&lt;p>After a &lt;a href="https://twitter.com/tarynpivots/status/1121052238829375496">couple of hours of sleep&lt;/a>, it was time to try to finish this up. The list of things left to do was small, but was still quite a bit of work. We had to:&lt;/p>
&lt;ul>
&lt;li>Restore databases to CO-RPTSQL01 and get the AGs/distributed AGs syncing throughout the reporting cluster&lt;/li>
&lt;li>Destroy the temporary distributed availability groups on the new 2016 clusters&lt;/li>
&lt;li>Rebuild NY-SQL04 and NY-SQL02 - the last two servers in the old 2012 clusters&lt;/li>
&lt;li>Insert the newly rebuilt servers into the new 2016 Windows Clusters, install SQL Server, and add them to the AGs&lt;/li>
&lt;li>Turn on all t-log backups and daily full backups&lt;/li>
&lt;li>Put read-only routing in place on all availability groups&lt;/li>
&lt;/ul>
&lt;p>The goal was to get through the entire list same day, but that would be too easy. We ran into an issue immediately. Remember that NY-RPTSQL01 we got synchronizing at the end of Day 9 &amp;ndash; it was lying to us. It said it was syncing, but it really wasn&amp;rsquo;t. If you queried the &lt;code>StackOverflow&lt;/code> database, it was several hours behind the current production database&amp;hellip;uh oh, something was broken. We tried to restart the SQL Service to see if that unblocked it, it didn&amp;rsquo;t. Unfortunately, the only way we were able to get it syncing again was to:&lt;/p>
&lt;ul>
&lt;li>drop, then restore the databases&lt;/li>
&lt;li>manually apply the t-logs to bring it up to date from the current global primary&lt;/li>
&lt;li>then after executing some SQL to kick the AGs, like magic, it would be synchronizing again&lt;/li>
&lt;/ul>
&lt;p>We did this for all the impacted databases on the reporting cluster primary, NY-RPTSQL01, and then still had to get CO-RPTSQL01 back online. Nick Craver jumped in and worked on the NY and CO reporting servers, while I started rebuilding the final two servers, NY-SQL04 and NY-SQL02. The rebuild of the final two servers went exactly as expected. Once it came time to put the servers into the availability groups, I took fresh backups from the primaries and restored those. I did this because we weren&amp;rsquo;t going to automatically seed an AG with 354 databases or a 3.5TB one (&lt;code>StackOverflow)&lt;/code>, and I wanted to manually apply as few T-logs to the databases as possible to bring it up to current time.&lt;/p>
&lt;p>It may have taken all day, but eventually we fixed the reporting cluster and had all six of our main SQL Servers moved into the new Windows Server 2016 clusters. By the end of the day, &lt;a href="https://twitter.com/tarynpivots/status/1121145354760507393">Opserver was reporting green everywhere&lt;/a>:&lt;/p>
&lt;p>&lt;img src="https://tarynpivots.com/image/2019/AllDone.jpg" alt="Final State - All Done">&lt;/p>
&lt;p>It was such a good feeling to have everything moved over. After months of planning, testing, and frustration from hitting issue after issue, plus a bug in SQL Server, it was incredible to see it all come to fruition.&lt;/p>
&lt;h2 id="final-thoughts">Final Thoughts&lt;/h2>
&lt;p>Besides the upgrade to &lt;a href="https://tarynpivots.com/post/how-we-upgraded-stackoverflow-to-sql-server-2017/">SQL Server 2017 from the year before&lt;/a>, I had never even attempted something with this level of complexity or planning. This project involved a lot of juggling of servers, AGs/distributed AGs, application changes, and considering we don&amp;rsquo;t have maintenance windows at Stack Overflow, it was difficult to imagine how to pull this off without being down for an extended period of time. To know this entire thing was executed with only about 10-15 minutes of downtime for the public facing sites is pretty darn amazing, in my opinion.&lt;/p>
&lt;p>I have some takeaways from this - most are common sense, but I need to repeat them for my own sanity:&lt;/p>
&lt;ol>
&lt;li>
&lt;p>Test, re-test, and then test some more. The initial plan was to jump from the lab to prod, the rebuild of our development environment was unexpected. I also didn&amp;rsquo;t think this upgrade would break our development environment, but by doing these intermediate steps we found several bugs in our deployment process which we might have hit in production.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Have a lab environment, if possible. Between creating new lab environments and using my existing lab environments, I was able to work through all the different scenarios I figured we would hit in production. Of course, I wasn&amp;rsquo;t able to test at our scale and find the SQL Server bug, but I felt confident the steps I planned would work for the failovers.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Rely on your team. While I&amp;rsquo;m the only official DBA at Stack Overflow, there were times when I was at a loss on how to fix some of our issues with Foreman, Puppet, and needed extra hands. I can&amp;rsquo;t thank my team enough for stepping in and jumping into hangouts with me when I was stuck or something broke.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Remember that no matter how much you plan, something is going to go sideways. We had a ~35 page planning document that I wrote and none of that mattered when we hit the bug with availability groups.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Try not to stress out over the things out of your control. I hit roadblock after roadblock during this project, and felt that I was letting my team down by not moving it forward faster. When you expect a project to be done in one month and it takes about four, it&amp;rsquo;s hard not to feel like you&amp;rsquo;re failing and be stressed out over it. Each issue (failed drives, bugs) moved us forward and we improved our process, even if it was incredibly slow. In the end, we were in a much better situation because we squashed bugs and moved to better software. Keeping perspective on the end-goal is something to remind yourself when in these types of situations.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Keep notes as you’re going through a deployment like this. Every day write yourself a recap of what transpired that day, so you remember what went well or what broke. Unfortunately, I didn’t do this during the production part and spent a lot of time going through chat transcripts trying to remember all the steps and roadblocks we hit to document in this post.&lt;/p>
&lt;/li>
&lt;/ol>
&lt;p>I&amp;rsquo;m hoping this is helpful to someone who needs to go through this type of upgrade. Now that this project is done, I guess it&amp;rsquo;s time I start thinking about upgrading to SQL Server 2019.&lt;/p></description></item><item><title>How we upgraded Stack Overflow to SQL Server 2017</title><link>https://tarynpivots.com/post/how-we-upgraded-stackoverflow-to-sql-server-2017/</link><pubDate>Wed, 07 Nov 2018 19:00:38 +0000</pubDate><guid>https://tarynpivots.com/post/how-we-upgraded-stackoverflow-to-sql-server-2017/</guid><description>&lt;p>This post has been rattling around in my head for months, and with &lt;a href="https://www.microsoft.com/en-us/sql-server/sql-server-2019">SQL Server 2019&lt;/a> on the horizon, I figured I&amp;rsquo;d finally put my thoughts down, especially since I know we might hit some of the same issues when we upgrade.&lt;/p>
&lt;p>A quick background, when I became the DBA at Stack Overflow, we had various versions of SQL Server in place — the majority were at some level of SQL Server 2016, each with a different CU or SP installed. We even had an instance of SQL Server 2012 still running. The bulk of our servers hadn’t been patched for 6-9 months, and we were just as behind in our Windows Updates&amp;hellip;eeek. In a perfect world, all servers run the same version and CU. However, since we don’t have regular maintenance windows, tackling this would mean downtime - which meant it had to be a project not a one-off type of task.&lt;/p>
&lt;p>When I kicked off this project, back in February 2018 (see I told you this has been rattling around in my head for months), we had ~30 servers, the breakdown of them was:&lt;/p>
&lt;ul>
&lt;li>3 servers in the Stack Overflow cluster (2 in NY, 1 in CO)&lt;/li>
&lt;li>3 servers in the Stack Exchange cluster (2 in NY, 1 in CO)&lt;/li>
&lt;li>3 servers in a cluster for Internal Stuff - these power our internal applications (2 in NY, 1 in CO)&lt;/li>
&lt;li>2 servers for Reporting Cluster - these power various reporting applications (1 in NY, 1 in CO)&lt;/li>
&lt;li>the remaining servers have miscellaneous functions like - lab/test environments, development servers, traffic logs, and many others used by various teams (located in both NY and CO)&lt;/li>
&lt;/ul>
&lt;p>We have various &lt;a href="https://docs.microsoft.com/en-us/sql/database-engine/availability-groups/windows/always-on-availability-groups-sql-server?view=sql-server-2017">availability groups&lt;/a> (AGs) running on the Stack Overflow, Stack Exchange, Internal Development, and Reporting servers. Adding another layer of complexity, we also have availability groups that are a part of &lt;a href="https://docs.microsoft.com/en-us/sql/database-engine/availability-groups/windows/distributed-availability-groups?view=sql-server-2017">Distributed Availability Groups&lt;/a> (DAGs). The main clusters (SO, SE, and Internal) have data that flows from a handful of databases downstream to the reporting servers. It looks a bit like this:&lt;/p>
&lt;p>&lt;img src="https://tarynpivots.com/image/2018/so-sql-server-layout.png" alt="A very basic overview of our SQL Server layout">&lt;/p>
&lt;h2 id="the-initial-plan">The Initial Plan&lt;/h2>
&lt;p>With so many servers to upgrade, I needed to figure out the best path to get them done in the least amount of time, with the least amount of downtime. I started out by basically creating a spreadsheet of metadata. This spreadsheet included all the server names, the current software version (including CU/SP), the date of the last Windows Update, the team to notify before starting the maintenance, and the date the upgrade was completed. I also added a rudimentary risk level from low to high, and chose the levels based on whether the server was public, internal, dev, etc. Using this risk level, I prioritized the order in which to upgrade each server. Since all the servers hadn&amp;rsquo;t been patched in months, the patching plan for the day of the upgrade was:&lt;/p>
&lt;ul>
&lt;li>upgrade to SQL Server 2017&lt;/li>
&lt;li>run Windows Updates&lt;/li>
&lt;li>reboot as needed&lt;/li>
&lt;li>apply SQL Server CU update&lt;/li>
&lt;li>reboot and repeat as needed - mainly Windows Updates until everything was up to date&lt;/li>
&lt;/ul>
&lt;p>My goal was to complete all servers over the course of a month. That may sound like a long timeframe, but with only me touching them, it gave me wiggle room in the event there were issues. I started on the servers with the least amount of risk, this meant the Lab, Dev, and some of the miscellaneous servers. These aren&amp;rsquo;t customer facing, and are used internally, so if something went wrong we wouldn&amp;rsquo;t have public downtime. After patching ~15 servers I felt pretty confident that the upgrade of the rest would go fine&amp;hellip;this was a mistake.&lt;/p>
&lt;h2 id="but-i-followed-the-plan">But I Followed The Plan&lt;/h2>
&lt;p>Moving to the servers in the AGs/DAGs, I knew I would follow this order for each cluster:&lt;/p>
&lt;ol>
&lt;li>Remote Secondary (CO)&lt;/li>
&lt;li>Local Secondary (NY)&lt;/li>
&lt;li>Local Primary (NY)&lt;/li>
&lt;/ol>
&lt;p>In between the NY Secondary and the NY Primary I&amp;rsquo;d perform a failover to let me hit the last server in the cluster. I also decided that I would do the remaining servers in this order:&lt;/p>
&lt;ol>
&lt;li>Reporting Cluster&lt;/li>
&lt;li>Internal Dev Cluster&lt;/li>
&lt;li>SO/SE - CO/NY secondaries&lt;/li>
&lt;li>SO/SE - NY primaries after a failover&lt;/li>
&lt;/ol>
&lt;p>This allowed me to get the majority of the upgrades done during the week, then we&amp;rsquo;d perform a quick failover on a weekend, during lower traffic, and I&amp;rsquo;d knock out upgrading the last two critical servers. Based on this, the CO reporting server was up first.&lt;/p>
&lt;p>All boxes on the patching plan were ticked for this server, and when it came back up after the final reboot, I checked our instance of &lt;a href="https://github.com/opserver/Opserver">OpServer&lt;/a> and everything was green and syncing; I thought &amp;lsquo;Woot, everything is great.&amp;rsquo; Since we use readable secondaries, I wanted to perform a touch test on the databases in the AGs and DAGs to make sure everything was working. I logged into SSMS, but when I attempted to query any database in a DAG they weren&amp;rsquo;t accessible. I couldn&amp;rsquo;t read from them. This was a giant &amp;lsquo;oh crap&amp;rsquo; moment.&lt;/p>
&lt;p>I frantically started checking the server to see if there were errors. There was nothing. Nothing in the logs, and no errors from the installation. From what I could tell, everything went as expected. Then why couldn&amp;rsquo;t I read from the databases? After doing a bit of research, I found it was because the secondary was a higher version from the primary. Initially I thought, &amp;lsquo;OK, I&amp;rsquo;ll upgrade the NY reporting server and everything would be fine&amp;rsquo;, but then I remembered these servers are downstream in the DAGs. Uh oh.&lt;/p>
&lt;p>If I upgraded both reporting servers, which receive data from 3 DAGs (SO/SE/Int Dev), and those are all a lower version of SQL Server, both reporting servers would be totally out of commission until the upstream servers were all upgraded. All this would result in major downtime for reporting and wasn&amp;rsquo;t going to work.&lt;/p>
&lt;p>I did research, pinged people on &lt;a href="https://dba.stackexchange.com/">Database Administrators Stack Exchange&lt;/a>, and poked my co-worker &lt;a href="https://twitter.com/Nick_Craver/status/963443600997736449">Nick Craver about it&lt;/a>. From everything I heard and read, the behavior was expected, but incredibly difficult to deal with if you&amp;rsquo;re not upgrading everything at the same time, or in a short amount of time. I wanted to spread the upgrades out over a month to make them manageable, but when they go out of commission because of a upgrade, that timeframe went out the window.&lt;/p>
&lt;p>All this left me in a state of confusion. How the heck am I going to upgrade these with basically zero downtime, and ensure they don&amp;rsquo;t become unreadable for days?&lt;/p>
&lt;p>My plan blew up — I couldn&amp;rsquo;t do the upgrade in the original order and timeframe that I wanted.&lt;/p>
&lt;h2 id="back-to-the-drawing-board">Back to the Drawing Board&lt;/h2>
&lt;p>Since we couldn&amp;rsquo;t be without both reporting servers for days, that meant I needed to come up with a new plan/timeframe to get this done. It would also need buy-in, because at some point we&amp;rsquo;d have more downtime than initially expected.&lt;/p>
&lt;p>Microsoft recommends &lt;a href="https://docs.microsoft.com/en-us/sql/database-engine/availability-groups/windows/upgrading-always-on-availability-group-replica-instances?view=sql-server-2017">upgrading servers&lt;/a> in this order:&lt;/p>
&lt;ul>
&lt;li>Upgrade remote secondaries&lt;/li>
&lt;li>Upgrade local secondaries&lt;/li>
&lt;li>Manual failover of the primary to the local secondary&lt;/li>
&lt;li>Upgrade previous primary&lt;/li>
&lt;li>Failback to previous primary&lt;/li>
&lt;/ul>
&lt;p>As you can see, that&amp;rsquo;s what I was following. The problem comes in when the server is in a Distributed Availability Group. I did the CO reporting server first, and ideally we&amp;rsquo;d do the NY version next, but since these are downstream in 3 DAGs, both NY and CO would be completely unreadable until the primaries upstream in the DAGs are upgraded. This means we automatically have reduced service with the servers, I can&amp;rsquo;t move to the CO secondary in the Internal servers because it&amp;rsquo;ll become unreadable like the reporting servers. As I move outward, the reduced service grows. So now what am I going to do?&lt;/p>
&lt;p>Thankfully, I have a lab environment to test with. If you don&amp;rsquo;t have one, make one. I have 4 VMs that I set up to mimic our AG/DAG environment. This allowed me to test the upgrade order of the servers, perform failovers, verify the AGs/DAGs came back online, etc. After multiple failed attempts at getting the order right, I finally figured out that I would follow the same order and pattern that I was using, just not at the cluster level - it would be based on location instead.&lt;/p>
&lt;p>The new plan would be:&lt;/p>
&lt;ol>
&lt;li>Upgrade all local NY secondaries in a DAG/AG - doing this makes them completely unreadable
&lt;ul>
&lt;li>the NY secondaries in the Internal, SE, SO clusters&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>Upgrade all remote CO secondaries in a DAG/AG - doing this makes them completely unreadable
&lt;ul>
&lt;li>the CO reporting server was done, this left the CO servers in the Internal, SE, SE clusters&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>Perform a failover in a specific order
&lt;ul>
&lt;li>Reporting Server - failover to CO - this makes the entire cluster unreadable until all upgrades are finished - failover non-DAG availability groups, then failover the DAGs - hope that when it shows it&amp;rsquo;s syncing it really is, since we won&amp;rsquo;t be able to query anything&lt;/li>
&lt;li>Internal Server - failover to NY secondary - failover DAGs first, verify it&amp;rsquo;s syncing to reporting, then failover AGs&lt;/li>
&lt;li>SE Cluster - failover to NY secondary - failover DAGs first, verify it&amp;rsquo;s syncing to reporting, then failover AGs&lt;/li>
&lt;li>SO Cluster - failover to NY secondary - failover DAG&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>Verify that everything is syncing or at least verify that everything appears to be syncing - I&amp;rsquo;m looking at you reporting servers&lt;/li>
&lt;li>Upgrade the former primaries in NY - reporting, internal SE, and SO&lt;/li>
&lt;li>Verify that all databases are syncing - most likely they aren&amp;rsquo;t, so running &lt;code>ALTER DATABASE [&amp;lt;name&amp;gt;] SET HADR RESUME;&lt;/code> should resolve any syncing issues - again looking at you reporting servers&lt;/li>
&lt;li>Perform any failovers to reinstate a primary. For us, this only applies to the reporting cluster. When we failover within NY, we just let the new primary stay primary until we need to failover again - this minimizes downtime for us.&lt;/li>
&lt;/ol>
&lt;p>You can see that this deviates from the recommended order by Microsoft because we did the local NY secondaries first. This was done in the event something catastrophic happened in NY, and we had to fail to CO. We knew CO was still up and running since we could query it. If we had upgraded CO and made it unreadable, we wouldn&amp;rsquo;t be able to query it and confirm things were syncing properly for an extended period of time. The thought of this made us uncomfortable.&lt;/p>
&lt;p>I tested and retested this plan in the lab environment to get it down before attempting this on any of the critical servers. The only real issue was the reporting servers would be out of commission until the rest upgraded. This meant everything would have to be upgraded fast, so we did not have reporting unavailable for too long. Based on this, it was decided we&amp;rsquo;d do the local NY secondaries on a Friday, then perform the rest of the upgrades and failover on a Saturday - this meant downtime, so we had to give &lt;a href="https://meta.stackexchange.com/q/307744/164200">plenty of notice to the community&lt;/a> before tackling it.&lt;/p>
&lt;p>In the few days before the upgrade, I wrote up the playbook for the weekend. To minimize downtime and to get the remaining 11 servers upgraded quickly, there were 3 of us who were going to be doing the last upgrades, so I wanted everything documented. The playbook had every step outlined, who was doing what to which server, SQL code for each step (if needed), what was to be installed on each server, the order of failover of AGs&amp;hellip;everything. This was my first major upgrade and I was a nervous wreck, I didn&amp;rsquo;t want anything to be missed or to go wrong, so I probably overplanned, but why not, right?&lt;/p>
&lt;p>The weekend came and went, and we had no issues with the upgrades. When we failover our main SO/SE clusters, we typically put the sites in read-only mode, just to be safe that nothing crazy happens when SQL is failing over - for this upgrade our downtime/read-only time was &amp;lt; 10 minutes which was far less than we expected. If you&amp;rsquo;re super curious, you can &lt;a href="https://www.youtube.com/watch?v=j5tOodr4ouc">watch a recording of the livestream&lt;/a>, though I&amp;rsquo;m not sure why you&amp;rsquo;d want to sit through ~3 hours of us talking about upgrades.&lt;/p>
&lt;p>In the end, I learned a bunch from this. I figured out how best to upgrade to new versions of SQL Server when Distributed Availability Groups are in play without making servers unreadable for too long. I also learned just how darn handy having a lab environment is, and that I super over-prepared/planned for the days of the actual work. I&amp;rsquo;m not sure if this will help anyone else in the future, but now I know to avoid these landmines when upgrading.&lt;/p>
&lt;p>I also know there are probably plenty of other landmines I can hit when we move to SQL Server 2019.&lt;/p></description></item><item><title>Pivoting yet again - New blog and domain</title><link>https://tarynpivots.com/post/pivoting-yet-again/</link><pubDate>Mon, 27 Aug 2018 15:00:00 +0000</pubDate><guid>https://tarynpivots.com/post/pivoting-yet-again/</guid><description>&lt;p>Almost a year ago, in November 2017, I moved back into a more technical role at &lt;a href="http://stackoverflow.com/">Stack Overflow&lt;/a> and became a Database Administrator. During this time, I have wanted to relaunch my blog, as it&amp;rsquo;s been over three years since I really wrote anything (yes, I&amp;rsquo;m terrible).&lt;/p>
&lt;p>Well, that time has finally come.&lt;/p>
&lt;p>After spending a few weeks, looking at various options to replace Wordpress, I finally decided on using &lt;a href="https://gohugo.io/">Hugo&lt;/a> and &lt;a href="https://app.netlify.com/">Netlify&lt;/a>. Hugo was incredibly easy to set up and Netlify deploys my changes automatically when I push to GitHub. Additionally, I moved away from onlybluefeet.com and am now using tarynpivots.com. This new URL seems fitting, as I love the &lt;a href="https://docs.microsoft.com/en-us/sql/t-sql/queries/from-using-pivot-and-unpivot?view=sql-server-2017">&lt;code>PIVOT&lt;/code>&lt;/a> function in SQL Server, and I seem to change things a lot. I have no plans on moving away from being a DBA any time soon, so my plan is it to write about the SQL Server issues we deal with at Stack Overflow, as well other SQL things.&lt;/p></description></item><item><title>Ch Ch Ch Changes are afoot</title><link>https://tarynpivots.com/post/ch-ch-ch-changes-are-afoot/</link><pubDate>Fri, 22 May 2015 22:49:01 +0000</pubDate><guid>https://tarynpivots.com/post/ch-ch-ch-changes-are-afoot/</guid><description>&lt;p>Well it&amp;rsquo;s been an interesting couple months and there are quite a few changes going on in my life, but before I get into what&amp;rsquo;s coming up, I feel that I need to give a bit of an overview of my journey thus far.&lt;/p>
&lt;p>I never thought about becoming a developer. When I was growing up, I loved school (yeah, I&amp;rsquo;m weird), I loved math and science (even weirder), I was going to be a doctor, work on genetics, or something similar. That was my plan even when I started college. Then there were some family things that happened and I was steered to law. I changed majors and got a degree in Justice Studies with the plan to go to law school. I eventually went to law school and after 1 semester realized I hated it. There was no way I was going to finish it, so I quit law school.&lt;/p>
&lt;p>Problem was, I had no backup plan. I had a no clue what I was going to do. I bounced around a bit, took a class here and there trying to figure it out, but nothing sparked my interest.&lt;/p>
&lt;p>I was working a customer service job, and there was an internal job posted for a Web Site Developer that required experience with HTML, CSS, and ColdFusion. I&amp;rsquo;d never done any of that before, but my feeling was &amp;ldquo;Heck I&amp;rsquo;m smart, I can learn it&amp;rdquo; so I applied it. The manager was nice enough to give me an interview and once he realized I had no experience, he said &amp;ldquo;I can see you want the job, but no. We need someone with some knowledge.&amp;rdquo; I completely understood, but I&amp;rsquo;m stubborn and I decided to prove him wrong. I went out that weekend, bought a few books, and started learning HTML, etc. Via the work grapevine, it got back to the manager of the Web Team that I did this, so he decided that my drive was enough to give me a chance and boom I got the job.&lt;/p>
&lt;p>In one day, I went from not knowing what I was going to do, to becoming a developer. Post Web Site Developer, many of my jobs were the same way, I didn&amp;rsquo;t know the technology required, but I&amp;rsquo;d learn it. When I left the web job, I needed to know VBA and MS Access, that turned into C#, SQL Server, and Winforms. Each of these I learned on the job or on my own. The only difference was I stopped buying books and started using sites like Stack Overflow to learn.&lt;/p>
&lt;p>As I said, I didn&amp;rsquo;t grow up wanting to be a developer. While I like being a developer, lately I&amp;rsquo;ve felt burned out with it. I&amp;rsquo;ve had this nagging feeling that I need to be doing something or working on something that I care about. That I feel passionate about. But I wasn&amp;rsquo;t sure what that was.&lt;/p>
&lt;p>I started using Stack Overflow almost 5 years ago, when I needed to write C#. I had a book but I wasn&amp;rsquo;t able to solve some of my problems, so I&amp;rsquo;d either ask a question or search for answers. I loved the site immediately. I loved that I could get a solution to my specific problem. Yes, I had growing pains on the site, like so many others. For example, I wouldn&amp;rsquo;t give enough details when posting a question, etc. but I learned how the site worked, and I learned a lot from it. I began to feel comfortable answering questions in a few tags. I loved being able to help others and hopefully users would learn something from my answers.&lt;/p>
&lt;p>So what does this have to do with changes?&lt;/p>
&lt;p>I&amp;rsquo;ve been both a user and a moderator on Stack Overflow. But now I&amp;rsquo;m going to be working in a different capacity, I&amp;rsquo;ve been hired to be a Community Manager by Stack Exchange. I can&amp;rsquo;t express how excited I am for this. I love the sites, I love the product that they put out there, I love that people get benefit from them. Of course it&amp;rsquo;s a bit scary because it&amp;rsquo;s a career change, but the fact that I love what they do is a huge driving force behind my decision. To be able to work on something you care about makes a big difference. I can&amp;rsquo;t wait to see where these changes take me.&lt;/p></description></item><item><title>Using powershell to fix server space issues</title><link>https://tarynpivots.com/post/using-powershell-to-fix-server-space-issues/</link><pubDate>Sun, 12 Apr 2015 18:30:08 +0000</pubDate><guid>https://tarynpivots.com/post/using-powershell-to-fix-server-space-issues/</guid><description>&lt;p>A few weeks ago, we were running into severe disk space and memory issues on our development servers at work. Our set-up is a bit odd, we have 3 servers - one for the transactions, one for the web interface, and the final one for reporting. Using &lt;a href="https://msdn.microsoft.com/en-us/library/ms151176(v=sql.110).aspx">transactional replication&lt;/a> we have databases that can exist on all 3 servers. Yes, it&amp;rsquo;s can be a real nightmare to maintain, but anyone who works with replication already knows this. Each development team gets its own version of production &amp;ldquo;in a box&amp;rdquo;. These copies are on VM slices with limited memory and disk size.&lt;/p>
&lt;p>Our issues were happening on the web database and reporting servers. We have replication and sql jobs running, development teams testing, and a variety of other things hitting the servers pretty hard and no memory to process it, so we were running out of disk space. There were several days in a row that I noticed we were down to 20MB of space on our web database server. This lack of resources was causing replication to fail throughout our environment, which resulted in delays to our current sprint.&lt;/p>
&lt;p>I&amp;rsquo;m the early bird on my team and every morning I was executing the following code to &amp;ldquo;clean-up&amp;rdquo; our environment via shrinking the log files. Eeeek, the &lt;a href="http://stackoverflow.com/q/56628/426671">dreaded shrinking&lt;/a> of &lt;a href="http://www.brentozar.com/archive/2009/08/stop-shrinking-your-database-files-seriously-now/">log files&lt;/a>. I know you don&amp;rsquo;t want to do this really ever, but it&amp;rsquo;s a development environment and it&amp;rsquo;s how we handle our limited memory issues.&lt;/p>
&lt;p>My first check to was see the size of the logs by running &lt;a href="https://msdn.microsoft.com/en-us/library/ms189768.aspx">&lt;code>DBCC SQLPERF (LOGSPACE)&lt;/code>&lt;/a>. This command gives the transaction log space for all of the databases on the server. Once I got the list I was able to target the databases that had excessively large log files in order to shrink them.&lt;/p>
&lt;p>Next, I&amp;rsquo;d get the name of the log file to shrink via:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-sql" data-lang="sql">&lt;span class="k">SELECT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">name&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">FROM&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">sys&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">master_files&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="k">WHERE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">database_id&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">db_id&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">AND&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">type&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">1&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;p>Lastly, using the name of the log above I&amp;rsquo;d run &lt;a href="https://msdn.microsoft.com/en-us/library/ms189493(v=sql.110).aspx">&lt;code>DBCC SHRINKFILE&lt;/code>&lt;/a> to drop the size of the logs and restore a bit of memory on our box.&lt;/p>
&lt;p>Doing this first thing in the morning for multiple databases on multiple servers for multiple days in a row was terrible. So, I decided I needed to automate this. Considering I learned a bit about &lt;a href="https://www.tarynpivots.com/post/sql-saturday-adventures/">Powershell from Mike Fal at SQL Saturday Phoenix&lt;/a>, I thought &amp;ldquo;hey, let me take a stab at writing my first Powershell script to do this for me!!&amp;rdquo;&lt;/p>
&lt;p>To be honest, I wasn&amp;rsquo;t exactly sure where to start so I asked Mike for a bit of help on looping through all databases on a server. He was nice enough to give me a &lt;a href="http://pastebin.com/xNRZMGDm">starting script&lt;/a>. Using this, I attempted to incorporate my code above into it with the purpose of shrinking all log files on the server. I&amp;rsquo;m sure there are much better ways to do this but here is my first Powershell script.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-powershell" data-lang="powershell">&lt;span class="nb">Import-Module&lt;/span> &lt;span class="n">sqlps&lt;/span> &lt;span class="n">-DisableNameChecking&lt;/span>
&lt;span class="nv">$dbs&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="nb">Invoke-SqlCmd&lt;/span> &lt;span class="n">-ServerInstance&lt;/span> &lt;span class="s1">&amp;#39;servername&amp;#39;&lt;/span> &lt;span class="n">-Query&lt;/span> &lt;span class="s2">&amp;#34;select name from sys.databases where database_id &amp;gt; 4&amp;#34;&lt;/span>
&lt;span class="k">foreach&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nv">$db&lt;/span> &lt;span class="k">in&lt;/span> &lt;span class="nv">$dbs&lt;/span>&lt;span class="p">){&lt;/span>
&lt;span class="nv">$Log&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="nb">Invoke-Sqlcmd&lt;/span> &lt;span class="n">-ServerInstance&lt;/span> &lt;span class="s1">&amp;#39;servername&amp;#39;&lt;/span> &lt;span class="n">-Database&lt;/span> &lt;span class="nv">$db&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">name&lt;/span> &lt;span class="n">-Query&lt;/span> &lt;span class="s2">&amp;#34;select name from sys.master\_files where database\_id = db_id() and type = 1&amp;#34;&lt;/span>
&lt;span class="nv">$LogName&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="nv">$Log&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">name&lt;/span>
&lt;span class="nv">$query&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;DBCC SHRINKFile($LogName, 1)&amp;#34;&lt;/span>
&lt;span class="nb">Invoke-Sqlcmd&lt;/span> &lt;span class="n">-ServerInstance&lt;/span> &lt;span class="s1">&amp;#39;servername&amp;#39;&lt;/span> &lt;span class="n">-Database&lt;/span> &lt;span class="nv">$db&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">name&lt;/span> &lt;span class="n">-Query&lt;/span> &lt;span class="nv">$query&lt;/span>
&lt;span class="p">}&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>
&lt;p>This does exactly what I need it to do. I can set it up on our environments to run nightly to shrink the logs so I no longer need to manually &amp;ldquo;fix&amp;rdquo; stuff every day. I&amp;rsquo;ve passed it along to our engineering services group to set this up in all of the development environments when they create them with new production copies.&lt;/p>
&lt;p>The script is available on &lt;a href="https://github.com/tarynpratt/posh_stuff/tree/master/Shrinking%20DBs">GitHub&lt;/a> if anyone is interested in using it, or tweaking it, or criticizing it, etc.&lt;/p></description></item><item><title>SQL Saturday Adventures</title><link>https://tarynpivots.com/post/sql-saturday-adventures/</link><pubDate>Sun, 12 Apr 2015 16:50:33 +0000</pubDate><guid>https://tarynpivots.com/post/sql-saturday-adventures/</guid><description>&lt;p>A few weeks bit over a month ago, I took the plunge and attended my very first SQL Saturday, &lt;a href="https://www.sqlsaturday.com/370/eventhome.aspx">SQL Saturday #370 in Phoenix&lt;/a>. I&amp;rsquo;ve never had a chance to go to any of the local user group events or another SQL Saturday due to scheduling issues, etc. but this year I reshuffled things so I could take part. I&amp;rsquo;m really glad that I did, I truly learned a lot of stuff and I also realized I know more than I probably give myself credit for. Here&amp;rsquo;s a few of the highlights from me day:&lt;/p>
&lt;p>My first session was Understanding Parameter Sniffing by Benjamin Nevarez. We deal with parameter sniffing a lot at work, so it was something I had experience with. I also gave a Lunch &amp;amp; Learn presentation to co-workers on the topic, which made me curious to see what else I should have covered during my session. To my surprise, I knew most of what Benjamin covered - which was great. His session was listed as &amp;ldquo;Beginner&amp;rdquo; but it was nice to know almost everything he discussed.&lt;/p>
&lt;p>During lunch, I attended the WIT Panel. For those who don&amp;rsquo;t know, WIT is for Women in Technology. I&amp;rsquo;ve rarely worked with female developers, so I went to this session to hear stories from the women in the same field. It was interesting to hear similar stories from the women on the panel, as well as from the audience. I found myself saying &lt;em>yeah, I&amp;rsquo;ve experienced that&lt;/em> repeatedly during the session. Hearing about &amp;ldquo;Mr. Know-It-All&amp;rdquo; from multiple panelists made me laugh because we&amp;rsquo;ve all dealt with that guy. There are so many things I could say about the WIT Panel and being a woman in technology, I&amp;rsquo;ll have to make that a separate post. My takeaway from this was that my experiences are similar to many other women in tech, which I already knew, but it was nice to hear. I&amp;rsquo;d recommend this session to anyone attending a future SQL Saturday.&lt;/p>
&lt;p>The last session I went to Powershell Tips and Tricks for SQL Server Administration by &lt;a href="https://twitter.com/Mike_Fal">Mike Fal&lt;/a>. I had never used Powershell and had limited exposure to it prior to this session, but I know Mike from &lt;a href="http://dba.stackexchange.com/users/7611/mike-fal">DBA.SE&lt;/a> and Stack Overflow so I wanted to attend his session and I&amp;rsquo;m glad that I did. I saw a few things that I would be able to use almost immediately at work with Powershell (I&amp;rsquo;ll be posting my lame attempts in another post).&lt;/p>
&lt;p>I was truly impressed by out SQL Saturday that we had here in Phoenix. I&amp;rsquo;m glad I forced myself to go and I&amp;rsquo;ll definitely go to more events in the future.&lt;/p></description></item></channel></rss>