Deploying schema changes to multiple databases using the SQL Compare command line
How do you push out the schema changes to ten copies of the database on ten different servers, as part of a deployment?
Sometimes, you need to synchronize multiple target databases with the same source database, or with the source code for that database, in version control. You may need to push out the schema changes to ten copies of the database on ten different servers, as part of a deployment, for example. Or there may be cases where, for test purposes, you need to synchronize multiple copies of the same database, each with different names, on the same server. You might even want to synchronize a whole list of databases, maybe differently-named, on several different instances.
Whereas the SQL Compare GUI allows us to compare and synchronize one source and one destination at a time, the SQL Compare Pro command line makes it possible to automate the process of synchronizing a source with a destination. By calling SQL Compare from a batch script, we can then make it work for whole lists of destinations, one after another.
Command line licensing
Any automations using the SQL Compare command line that require installing it on more than one machine need Redgate Deploy or SQL Toolbelt licenses. For full details, see the Changes to distribution of command line page of the documentation.
Synchronizing databases from the command line: general syntax
The basic command line syntax for performing database comparison and synchronization looks as follows:
1 2 3 |
SQLCompare.exe /s1:SourceServer\SourceInstance /database1:SourceDatabase /s2:TargetServer\TargetInstance /database2:TargetDatabase /include:Identical /sync |
I described this syntax in my previous article, How to automate database synchronization using the SQL Compare command line. Briefly, it compares the source and target databases, and the use of the /sync
parameter means that SQL Compare will generate a script to apply any schema changes to the target database, necessary to synchronize it with the source.
The parameter /include:Identical
also allows us to avoid raising an error in cases where the source and target databases are identical.
Deploying to multiple databases on the same target server
In this example, I have two target databases, AdventureWorks2014_1
and AdventureWorks2014_2
, on the same target instance (.\DW
), both of which I need to synchronize with the source database, AdventureWorks2014
, on my local instance.
The simplest way to do this is to write a script that deploys the source to each target, one after the other, from the command line, as shown in Listing 1. The command timeout /t -1
simply keeps open the command window until we press a key (otherwise, it closes immediately, regardless of success of failure).
1 2 3 4 5 6 7 |
"C:\Program Files (x86)\Red Gate\SQL Compare 12\sqlcompare.exe" /s1 . /s2 .\DW /db1 AdventureWorks2014 /db2 AdventureWorks2014_1 /include:Identical /sync "C:\Program Files (x86)\Red Gate\SQL Compare 12\sqlcompare.exe" /s1 . /s2 .\DW /db1 AdventureWorks2014 /db2 AdventureWorks2014_2 /include:Identical /sync timeout /t -1 |
Listing 1
This will be fine if you are typing using the terminal, but there are problems if the script will be run several times unattended, such as by running it within a batch (.bat) file. As I described in my previous article, you’ll then need logging and error-handling (I’ll add that in Listing 4).
Also, this brute force ‘one line per database comparison’ approach gets more cumbersome and harder to manage and debug, the more target databases you have. So you will also need to find a way of executing SQL Compare in just one place in the script and turn the parameters you pass in into variables.
A simple approach is to loop through a list of target databases, as shown in Listing 2.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
@echo off for %%x in ( AdventureWorks2014_1 AdventureWorks2014_2 ) do ( echo "%%x" is being compared... echo. "C:\Program Files (x86)\Red Gate\SQL Compare 12\sqlcompare.exe" /s1 . /s2 .\DW /db1 AdventureWorks2014 /db2 %%x /include:Identical /sync echo. echo "%%x" is ready! echo. ) |
Listing 2
Save the script as a .bat file (in my case called simply compare.bat!) and run it from the command line. The output should look as shown in Figure 1.
This works reasonably well for multiple databases, since the list of the databases is easy to administer.
Deploying to multiple target databases on different servers
If we have a database with the same name on several different target instances, we simply modify Listing 2, above, to loop through server names instead of databases.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
@echo off for %%x in ( Server_1 Server_2 ) do ( echo The database on server "%%x" is being compared... echo. "C:\Program Files (x86)\Red Gate\SQL Compare 12\sqlcompare.exe" /s1 . /s2 %%x /db1 AdventureWorks2014 /db2 AdventureWorks2014 /include:Identical /sync echo. echo The database on server "%%x" is ready! echo. ) |
Listing 3
Of course, it gets a bit more complex if we have the destination databases on different instances and with different names. To deal with this, and to allow us to specify a different source database easily, we’ll use batch variables as well as loops.
While we’re about it, we’ll write in a log details of whether the synchronization went well, or we hit an error, as shown in Listing 4.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 |
# this batch file should be saved into a file e.g. Synchdatabases.bat and # then executed by: # cmd.exe /c Synchdatabases.bat # Put in the path to the right version of SQL Compare Set Compare="%ProgramFiles(x86)%\Red Gate\SQL Compare 10\sqlcompare.exe" # and define where you want your log of what happened Set Logfile=%localappdata%\CompareLog.Log # the instance where the source database is Set SourceServer=MySourceInstance # the name of the database Set SourceDatabase=AdventureWorks # create a task file with a line for each destination , comma-delimited # each one representing the database you want to sync with # this is simply a text file with a destination database on each line # with the name of the server separated from the name of the database by a comma Echo MyInstance1, AdventureWorks_1 >MyDatabases.txt Echo MyInstance1, AdventureWorks_2 >>MyDatabases.txt Echo MyInstance2, AdventureWorks_2 >>MyDatabases.txt # check that you have installed SQL Compare to the right path IF EXIST %compare% ( echo %compare% exists ) ELSE ( echo %compare% needs to be installed exit ) # Now read each line of the file and pick out the name of the instance # and the database # '/f' means use a file, 'delims=,' means use a comma, # tokens=1,2 specifies the columns to use # Column1 is the instance, column2 is the database etc. FOR /F "eol=; tokens=1,2* delims=, " %%g in (MyDatabases.txt) do ( # Now execute SQL Compare to do the synch. # %%g will be the first parameter # %%h will be the second parameter etc.. taken from the current line # Note that we suppress the 'database identical' error # Capture any errors to a temp file %compare% /s1 %SourceServer% /s2 %%g /db1 %SourceDatabase% /db2 %%h /include:Identical /sync 2>Temp.log # Error handling IF ERRORLEVEL 1 ( # then it is an error. echo error from SQL Compare >>%Logfile% # report what operation was being done to the log type Temp.log >>%Logfile% # ...and to the screen if necessary type Temp.log rem Exit 1 ) ELSE ( # this is executed if no error was detected. echo successfully synched %SourceServer%\%SourceDatabase% with %%g\%%h >>%Logfile% ) ) |
Listing 4
A few points of interest about this code, to help you navigate it:
- We would normally just read the destination instances and database names from a CSV file, but here we create a task file (called MyDatabases.txt) ‘on the fly’, in the same directory as the script file, and then read from it
- The chevron, ‘>‘, means ‘create and write into this file’
- The double chevron ‘>>‘ means ‘append to this file’
%%g
and%%h
are the script variables, the first identifying the instance, the second the database, and so on. It is standard practice to start script variables at ‘g’ (a-to-e are reserved for drive letters).IF ERRORLEVEL 1
actually means ‘If error level is 1 or higher’- If you don’t suppress ‘database identical’ error, using
/include:Identical
, then you will need to handle error level 63 in the error handling
Conclusion
In this article, we saw a way to compare and deploy changes from a single source to multiple databases. This can be quite useful for large environments, where keeping the schemas in sync rapidly becomes a tedious task. By using a fairly simple batch script, we can compare a single source to multiple destinations effortlessly.
If you’re not already using SQL Compare, download a fully-functional 14-day trial and discover how it lets you save time comparing and deploying SQL Server database schemas.