{"id":2475,"date":"2015-07-25T15:43:33","date_gmt":"2015-07-25T15:43:33","guid":{"rendered":"http:\/\/www.garysieling.com\/blog\/?p=2475"},"modified":"2015-07-25T15:43:33","modified_gmt":"2015-07-25T15:43:33","slug":"comparing-two-sql-server-databases-with-liquibase","status":"publish","type":"post","link":"https:\/\/www.garysieling.com\/blog\/comparing-two-sql-server-databases-with-liquibase\/","title":{"rendered":"Comparing two SQL Server databases with Liquibase"},"content":{"rendered":"<p>Liquibase is a tool for managing database schemas (e.g. diffing schemas and writing migration scripts), and supports most major commercial and open-source relational databases. I&#8217;ll show how to use it to compare a migrated database to the original database, to find and fix discrepancies.<\/p>\n<p>To use it with SQL Server, you&#8217;ll first need to <a href=\"http:\/\/www.liquibase.org\/\">download Liquibase<\/a> and the <a href=\"http:\/\/www.microsoft.com\/en-us\/download\/confirmation.aspx?id=11774\">Sql Server JDBC Driver.<\/a><\/p>\n<p>Once you&#8217;ve done this, you can make a file called &#8220;liquibase.properties&#8221; where you&#8217;re running it &#8211; this avoids long command line arguments, and it&#8217;s really convenient when you switch from diffing to applying updates, since you don&#8217;t need to make any changes to the file later in the process.<\/p>\n<p>Here&#8217;s my example properties file &#8211; obviously you&#8217;ll need to change the paths to fit your environment:<\/p>\n<pre>\nclasspath=D:\\\\Software\\\\SQL Server JDBC\\\\sqljdbc_4.0\\\\enu\\\\sqljdbc4.jar\ndriver=com.microsoft.sqlserver.jdbc.SQLServerDriver\nurl=jdbc:sqlserver:\/\/new host:1433;databaseName=new database name\nusername=new server user\npassword=new server password\nchangeLogFile=ChangeLog.xml\n\nreferenceUrl=jdbc:sqlserver:\/\/old server host;databaseName=old database name\nreferenceUsername=old database username\nreferencePassword=old database password\n<\/pre>\n<p>Once you do this, you can run:<\/p>\n<pre lang=\"bash\">\nliquibase diff\n<\/pre>\n<p>This prints out a nice report, that tells you what problems it found. One of the risks of using such a tool is that it may not detect some class of difference that is material to your application, but this appears to be fairly thorough.<\/p>\n<p>The nice thing about running a report is that it gives you an early read on how long this is going to take in the future when you run the migration for real (minutes, hours, days).<\/p>\n<pre>\nUnexpected Index(s): NONE\nChanged Index(s): NONE\nMissing Primary Key(s):\n     PK_ABExperiments_1 on ABExperiments(UniqueId, Experiment)\n     PK_AccountCredits on AccountCredits(AccountId)\nUnexpected Primary Key(s): NONE\nChanged Primary Key(s): NONE\nMissing Schema(s): NONE\nUnexpected Schema(s): NONE\nChanged Schema(s): NONE\nMissing Sequence(s): NONE\nUnexpected Sequence(s): NONE\nChanged Sequence(s): NONE\nMissing Stored Procedure(s): NONE\nUnexpected Stored Procedure(s): NONE\nChanged Stored Procedure(s): NONE\nMissing Table(s): NONE\nUnexpected Table(s): NONE\nChanged Table(s): NONE\nMissing Unique Constraint(s): NONE\nUnexpected Unique Constraint(s): NONE\nChanged Unique Constraint(s): NONE\n<\/pre>\n<p>The previous step was optional. To get the real diff, the following command will make an XML document containing every difference:<\/p>\n<pre lang=\"bash\">\nliquibase diffChangeLog\n<\/pre>\n<p>Once you have this, you can simply run the update. However, before running the update, I removed the reference database from the properties file as a &#8220;belts and suspenders&#8221; tactic, although I&#8217;m sure it&#8217;s safe anyway.<\/p>\n<pre>\nliquibase update\n<\/pre>\n<p>Then you will get a series of failures (and I never promised this would be easy). Fortunately, it rolls back each time. You have to decide how to fix these in your case &#8211; while some of them may be defects in Liquibase, you&#8217;re more likely to hit problems with the database itself.<\/p>\n<p>For instance, for me the migration I ran to build the new database didn&#8217;t bring across auto autoincrement fields, so I get these patches, which fail:<\/p>\n<pre>\n&lt;addAutoIncrement columnDataType=\"int(10)\" columnName=\"Id\" tableName=\"Accounts\"\/&gt;\n<\/pre>\n<p>The problem here is two-fold; Liquibase would need to know how to restart the auto-increment field, and even if that was solved, SQL Server doesn&#8217;t let you change something to an auto-increment. Dropping the column and re-adding it as an auto-increment is a poor option as well, because the table will have the columns out of order, potentially breaking code that relies on &#8220;SELECT *&#8221;.  This alone indicates that I&#8217;ll need to re-migrate this database.<\/p>\n<p>Fortunately each update is a single line, so you can (and should) delete the problem updates from the patch XML and re-run it- most likely you&#8217;ll hit several problems, but liquibase stops after hitting one category of problem, so it&#8217;s helpful to know how many failures you&#8217;re going to get.<\/p>\n<p>I&#8217;m not sure why this happens, but Liquibase tries to set default values on my boolean columns, which fails: <\/p>\n<pre>\nDefault value of 0 does not match defined type of boolean, D:\/Software\n\/liquibase-3.4.0-bin\/ChangeLog.xml::1437835822256-139::gary (generated)\n<\/pre>\n<p>Liquibase also tried to re-create all my primary keys. This may be the difference between an &#8220;identity autoincrement&#8221; column, or just that the original migration changed the names of the primary keys. Fortunately this I can just remove entirely.<\/p>\n<pre>\nUnexpected error running Liquibase: The object 'PK_Accounts' is dependent on col\numn 'Id'. [Failed SQL: ALTER TABLE [dbo].[Accounts] ALTER COLUMN [Id] [int]]\n<\/pre>\n<p>Liquibase also attempts to changing column types on many columns, but this fails because keys were already migrated:<\/p>\n<pre>\n&lt;changeSet author=\"gary (generated)\" id=\"1437835822256-137\"&gt;\n  &gt;modifyDataType columnName=\"Id\" newDataType=\"bigint(19)\" tableName=\"lcsk_Messages\"\/&gt;\n&lt;\/changeSet&gt;\n<\/pre>\n<p>This may be an indication that Liquibase would be better to start the migration, then use a separate tool to migrate data.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Liquibase is a tool for managing database schemas (e.g. diffing schemas and writing migration scripts), and supports most major commercial and open-source relational databases. I&#8217;ll show how to use it to compare a migrated database to the original database, to find and fix discrepancies. To use it with SQL Server, you&#8217;ll first need to download &hellip; <\/p>\n<p class=\"link-more\"><a href=\"https:\/\/www.garysieling.com\/blog\/comparing-two-sql-server-databases-with-liquibase\/\" class=\"more-link\">Continue reading<span class=\"screen-reader-text\"> &#8220;Comparing two SQL Server databases with Liquibase&#8221;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"om_disable_all_campaigns":false,"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"footnotes":""},"categories":[4,6,7,24],"tags":[160,340,524],"aioseo_notices":[],"amp_enabled":true,"_links":{"self":[{"href":"https:\/\/www.garysieling.com\/blog\/wp-json\/wp\/v2\/posts\/2475"}],"collection":[{"href":"https:\/\/www.garysieling.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.garysieling.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.garysieling.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.garysieling.com\/blog\/wp-json\/wp\/v2\/comments?post=2475"}],"version-history":[{"count":0,"href":"https:\/\/www.garysieling.com\/blog\/wp-json\/wp\/v2\/posts\/2475\/revisions"}],"wp:attachment":[{"href":"https:\/\/www.garysieling.com\/blog\/wp-json\/wp\/v2\/media?parent=2475"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.garysieling.com\/blog\/wp-json\/wp\/v2\/categories?post=2475"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.garysieling.com\/blog\/wp-json\/wp\/v2\/tags?post=2475"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}