This is a goodΒ day for me.Β I’ve finally tried theΒ improved migrations feature in the latest version of RedGate SQL Source ControlΒ and Iβve tested the feature against all my typical cases so that I can share my findings with you.
What is the migrations feature?
When a change set includes an edit whichΒ leads to data loss (or a deploy schema error due to constraints definitions), we need to create a script that avoids any regression/block during the deployment phase. This is true also when working as a distributed team inΒ a continuous integration based environment. We need to avoid members of our team being blocked any time changes are shared and minimize the risk of regressions.
Migrations in SQL Source Control means “migration SQLΒ scripts”. We are speaking about custom scripts, related to the changed objects, that let us avoid regressions. They are created whenever there is a likelihood of a potential problem or data loss occurring on an object and inserted when getting the latest version of our database from our source control system. But the most important thing is that they are applied when deploying our databases to the test/staging/production environments.
Migrations feature history
A migrations feature has been implemented more than one time into Redgate SQL Source Control unfortunately with some trouble, especially when trying to merge the migration scripts between different branches and to integrateΒ the most recent source control systems. (You can read a brief history of migrations in SQL Source Control in this blog post). However, the latest version of migrations in SQL Source Control v5 is a great implementation, which supports also aΒ genericΒ Working Folder (no matter what version control system is installed, we are just usingΒ a simple folder). So, you may hear aboutΒ the previous versions, Migrations V1,Β V2,Β and you will find some capability in SQL CompareΒ projects, but, using the new migrations feature in SQL Source Control v5 will resolve everything.
AΒ real scenario
The environment I’m working on is a multi-branch scenario, implemented inΒ Visual Studio Team Service, likeΒ the following one:
- A VSTS local workspace folder mapped to the VSTS team project
- A local folder for each branch (devΒ |Β mainΒ |Β release), mapped to the VSTS branches
- TheΒ folder ofΒ the database in each branch
The purpose of this articleΒ is to demonstrate what are the capabilities when managing two typical cases. The firstΒ is aboutΒ aΒ breaking-change, aΒ NOT NULL column addition to a table. The second is a data-only migration script, which can be insertedΒ during the deployment phase.
Migration for NOT NULL column addition
Adding a NOT NULL column to a tableΒ can lead to a potentialΒ delivery problem. Indeed, in a dev environment, whenΒ a developer tries to get the change and the targetΒ tableΒ has rows, the NOT NULL constraint will be violated. The same couldΒ happen in any environment when deploying.
WhenΒ this kind of change occurred, I used to create a “Pre-release” script for adding the column with NULL, then writing data inside it and finally, adding the NOT NULL constraint. This was manually managed – until now – and it took me someΒ time, typically the day before the deployment (it’s not usually just a matter of a single table). I had to create sql scripts, folders, naming conventions and IΒ had to rememberΒ to execute them. When weβre talking about automation, this is a step backwards and it opens us up to the risk of human error. Last but not least, thisΒ is a more complex solution to setup onΒ the deployment software we are using,Β Octopus Deploy.
In the following scenarios we will try to understand what will happen when:
- replacingΒ a schema change withΒ aΒ migration script
- sharing change set with other developers
- merging the branches
- using data-only migration script
Replace a schema change with a migration script
When we need to replace the schema change with our script, a schema-data-schema migration script is the best choice.
Suppose we have two developers,Β Dev1Β andΒ Dev2, who are working with aΒ dedicated database modelΒ on SQL Source Control on theΒ devΒ branch. TheyΒ have the same table in their databases (StoreDb), calledΒ Inventory.Items (ItemId int PK, Name varchar(30), CategoryId smallint).Β Dev1Β table is empty, while theΒ Dev2Β one has tenΒ rows.Β Dev1Β executes the following command:
Use StoreDb;
GO
ALTER TABLE Inventory.Items ADD InsertTimestamp datetime NOT NULL;
GO
Since the table is empty forΒ Dev1, the command is executed successfully. ButΒ Dev1Β ignored the potential problems about data loss/constraint violation. Fortunately, SQL Source Control warns him when he tries to commit the changes:

Dev1Β can add a migration script, related to the object that is changed:

Pressing “Generate script” of the selected object will show the proposed t-sql migration script:

As you can see, the proposed script is not really “completed”, because it’s up toΒ Dev1Β to add the desired behavior for the new NOT NULL column. The highlighted part is the migration addition. However, the change that shouldΒ be added is simple and quick.
Dev1Β can “Save and close” and the commit tab becomes as described in the picture below:

The generated migration script replaces the suggested schema change. This allows us to avoid any constraint violation.
Sharing changes to other developers
WhatΒ is going to happen onΒ the other developersβ workstations? WhenΒ Dev1Β execute a check in of the column addition change, Dev2Β can get the latest version of the database. Keep in mind that Dev2 hasΒ already the database withΒ the Inventory.Items table, with ten rows, a version without theΒ Dev1 change.

They are using Working Folder, so they need to get the files from the source control (VSTS, using team explorer, for example) and then apply changes to the databases.Β Dev2Β can see the following:

Without the migration he receives errors (NOT NULL constraint violated), while the migration let him go ahead. Indeed, the get latest works and the tenΒ rows have been updated with the logic of the migration script.

Merging branches
What ifΒ Dev1Β andΒ Dev2Β merge the branch theyΒ are working on with another line? Suppose thatΒ Dev2Β starts a merge process betweenΒ devΒ andΒ mainΒ branches. Getting the latest version from theΒ mainΒ branch will behave in the same way. This means that the migration script is replicated also switching the repository. This didn’t work in the past, and I can definitely say that is working well now. This is a great point, especially when frequentΒ merges occurs.
When using data-only migration scripts?
When the change has been already committed and weΒ need to updateΒ the data on the changedΒ object, the data-only (blank) migration script is the right choice. The “split column” or “merge columns” refactors are good examples.

In theΒ split columnΒ scenario we have:
- create the new columns, then commit
- addΒ a migration script for updating values from the composite column, then commit
- drop the composite column, then commit

In the merge columns scenario we have:
- create the new column, then commit
- add a migration script forΒ aggregating values from the other columns, then commit
- drop the other columns, then commit
In both cases, sharing or deploying will deliver the changes respecting the commit order. You can read the migrations samples here.
What SQL Source Control does under the hood?
A deployment script that involves migrations consists of compare blocks and migration blocks:

RedGate SQL Source ControlΒ creates a setΒ of itemsΒ inside a “Custom Scripts” folder, which is inside the folder of the database itself:
- <datetime> ue auto: configurations and settings about the comparison (also theΒ RedGateDatabaseInfo.xml)
- <datetime> uf user: migration script (sql script and json files for transformations)
- DeploymentOrder.jsonΒ file, which is the order of migration deployment
Additionally, aΒ RedGateLocal.DeploymentMetadataΒ is added into the database. This table contains the list of executed migration scripts on the database and it allows us to avoid any duplication when the scripts are applied.Β More details here.
What about the deployment phase?
SQL Source ControlΒ is not used for delivering changes to test/staging/production environments.Β The other tools in the Redgate SQLToolbeltΒ able to doΒ that areΒ SQL CompareΒ andΒ SQL Data Compare,Β whichΒ compare structures and data, or DLM Automation which plugs into your release management tool.
As the get/apply processes, the comparison oneΒ will check for the set of items into the “Custom Scripts” folder and willΒ look upΒ the entries on theΒ RedGateLocal.DeploymentMetadataΒ table, respecting the SQL Source ControlΒ commit order. If the migration has been already delivered, it will be skipped, in order to avoid any double execution. This means that weΒ will find the table also in our test/staging/production environments.
Conclusions
The “Migrations” featureΒ in SQL Source Control v5 comesΒ finally with a great implementation. The suggested scripts are good, everything is clear and simple to understand. Additionally the user interface changes are welcome, also speaking about style. The great point is, in my opinion, the advantage that we can get from automation. All manual operations, whichΒ wereΒ necessary before this release, suddenly disappear. We can simply insert the folder into Octopus (or TeamCity, or another deployment tool) and execute the comparison against our environments avoiding regressions and data loss.
That’s the way we like it!