Consider the two migration scripts below. The scripts are run by a migration tool that executes the script and on success it inserts into a [dbo].[MigrationHistory] to track migration history.
(1) Check whether the table exists
-- File: 0050-CreatePersonsTable.sql
-- Only check if the table exists; create if not.
IF NOT EXISTS (
SELECT TOP 1 *
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_SCHEMA = 'dbo'
AND TABLE_NAME = 'Persons'
)
BEGIN
CREATE TABLE [dbo].[Persons] (
PersonID int,
LastName varchar(255),
FirstName varchar(255),
Address varchar(255),
City varchar(255)
)
END
(2) Check the migration history table
-- File: 0050-CreatePersonsTable.sql
-- Check migration history table to see if '0050-CreatePersonsTable.sql'
-- was already applied
IF NOT EXISTS (
SELECT *
FROM [dbo].[MigrationHistory]
WHERE ScriptName = '0050-CreatePersonsTable.sql'
)
BEGIN
CREATE TABLE [dbo].[Persons] (
PersonID int,
LastName varchar(255),
FirstName varchar(255),
Address varchar(255),
City varchar(255)
)
END
-- On success the migration tool inserts '0050-CreatePersonsTable.sql'
-- into the [dbo].[MigrationHistory] table.
Question:
I think the first migration script is a misleading way to write a migration.
The IF NOT EXISTS (...) predicate is misleading. What if a an earlier executed migration already created the table? Then this migration will be skipped and the database will be in an unexpected state. Now to remedy this I could add an additional check to see if the table exists and then execute a different set of instructions. This is okay but then what if the table exists and the expected columns exists but the data types are wrong (e.g., LastName is an nvarchar). Now I could write another check to see whether the data types are correct and if not then fix them.
These predicates can quickly get out of control.
Why not avoid all of this and just check the [dbo].[MigrationHistory] table? The migration tool in this case executes the migrations sequentially. That is 0001 to 0049 migrations are run before 0050-CreatePersonsTable.sql. I expect the database to be in a specific state when this migration is applied. If the [dbo].[Persons] table already exists (for whatever reason) then I want the migration to fail because the database is in an unexpected state.
Claim: The second migration is the better and safer migration.
Is there something that I am overlooking?