我在这个问题上挣扎了好几年,直到最近采用了一种似乎效果很好的策略。我的生活要点:
- 数据库不需要独立于应用程序进行版本控制
- 所有数据库更新脚本都应该是幂等的
因此,我不再创建任何类型的版本表。我只是将更改添加到 .sql 文件的编号序列中,这些更改可以在任何给定时间应用,而不会损坏数据库。如果这能让事情变得更容易,我将为该应用程序编写一个简单的安装程序屏幕,以允许管理员随时运行这些脚本。
当然,这种方法确实对数据库设计提出了一些要求:
- 所有模式更改都是通过脚本完成的 - 无需 GUI 工作。
- 必须格外小心,确保所有键、约束等均已命名,以便以后的更新脚本(如有必要)可以引用它们。
- 所有更新脚本都应检查现有条件。
最近项目的示例:
001.sql:
if object_id(N'dbo.Registrations') is null
begin
create table dbo.Registrations
(
[Id] uniqueidentifier not null,
[SourceA] nvarchar(50) null,
[SourceB] nvarchar(50) null,
[Title] nvarchar(50) not null,
[Occupation] nvarchar(50) not null,
[EmailAddress] nvarchar(100) not null,
[FirstName] nvarchar(50) not null,
[LastName] nvarchar(50) not null,
[ClinicName] nvarchar(200) not null,
[ClinicAddress] nvarchar(50) not null,
[ClinicCity] nvarchar(50) not null,
[ClinicState] nchar(2) not null,
[ClinicPostal] nvarchar(10) not null,
[ClinicPhoneNumber] nvarchar(10) not null,
[ClinicPhoneExtension] nvarchar(10) not null,
[ClinicFaxNumber] nvarchar(10) not null,
[NumberOfVets] int not null,
[IpAddress] nvarchar(20) not null,
[MailOptIn] bit not null,
[EmailOptIn] bit not null,
[Created] datetime not null,
[Modified] datetime not null,
[Deleted] datetime null
);
end
if not exists(select 1 from information_schema.table_constraints where constraint_name = 'pk_registrations')
alter table dbo.Registrations add
constraint pk_registrations primary key nonclustered (Id);
if not exists (select 1 from sysindexes where [name] = 'ix_registrations_created')
create clustered index ix_registrations_created
on dbo.Registrations(Created);
if not exists (select 1 from sysindexes where [name] = 'ix_registrations_email')
create index ix_registrations_email
on dbo.Registrations(EmailAddress);
if not exists (select 1 from sysindexes where [name] = 'ix_registrations_email')
create index ix_registrations_name_and_clinic
on dbo.Registrations (FirstName,
LastName,
ClinicName);
002.sql
/**********************************************************************
The original schema allowed null for these columns, but we don't want
that, so update existing nulls and change the columns to disallow
null values
*********************************************************************/
update dbo.Registrations set SourceA = '' where SourceA is null;
update dbo.Registrations set SourceB = '' where SourceB is null;
alter table dbo.Registrations alter column SourceA nvarchar(50) not null;
alter table dbo.Registrations alter column SourceB nvarchar(50) not null;
/**********************************************************************
The client wanted to modify the signup form to include a fax opt-in
*********************************************************************/
if not exists
(
select 1
from information_schema.columns
where table_schema = 'dbo'
and table_name = 'Registrations'
and column_name = 'FaxOptIn'
)
alter table dbo.Registrations
add FaxOptIn bit null
constraint df_registrations_faxoptin default 0;
003.sql、004.sql 等...
在任何给定时间,我都可以在任何状态下针对数据库运行整个系列的脚本,并且知道事情将立即与应用程序的当前版本保持同步。因为一切都是脚本化的,所以构建一个简单的安装程序来执行此操作要容易得多,并且将架构更改添加到源代码管理完全没有问题。