将版本控制添加到现有SQL Server数据库

时间:2012-11-07 16:58:41

标签: sql sql-server sql-server-2008 version-control

我是一个开发团队的成员,目前正在使用一个没有任何源代码控制的数据库。我们使用SQL Server 2008 R2并始终使用SSMS直接管理数据库。它现在有~340个表和~1600个存储过程,加上一些触发器和视图,因此它不是一个小型数据库。

我的目标是让数据库处于版本控制之下,所以我一直在阅读文章,例如Scott Allen's series和许多旧的SO相关问题。但我仍然无法决定如何继续。

我在想的是将数据库模式编写在一个文件中,然后在每个文件中编写过程,触发器和视图。然后将所有版本都保存在Mercurial下。但是,当然,团队的每个成员都可以访问SSMS并直接更改模式和过程,可能我们任何人都忘记在版本化文件中复制这些更改。

有哪些更好的选择?而且,我是否忘记了任何值得拥有源代码控制权的元素?我最关心的是,我发现的大多数文献都解释了在创建新数据库时如何进行版本控制,但是当它已经陈旧且相对较大时就不行了。

5 个答案:

答案 0 :(得分:14)

一般流程

我们为特定版本创建基线(例如,v1.0)。基线包括一个完整的模式创建脚本,以及来自允许的先前版本的升级脚本(如果有的话)(稍后会详细介绍)。因此对于v1.0,我们只有一个脚本:

baseline-v1.0.sql

从该基线开始,我们在前一个基线工作时创建增量更改脚本。这些脚本以可重入的方式创建,以便它们可以安全地多次运行(第一次只执行任何实际工作;请参阅下一段有关建议的方法)。我们只是为每个更改脚本创建一个文件,其中包含基线名称和时间戳(我们称之为版本)。例如,假设我们在基线之后创建了两个更改脚本。我们有以下文件:

baseline-v1.0.sql (for creating new installations)
baseline-v1.0-201211071220.sql (created on Nov. 7, 2012 at 12:20 PM UTC)
baseline-v1.0-201211122019.sql (created on Nov. 12, 2012 at 8:00 PM UTC)

我们创建了一个schema_version表,其中包含两列:baselineversionbaseline是一些标签(例如上面提到的v1.0),而version只是创建更改脚本的时间戳(我们选择这样做是因为创建任意版本号创建令人讨厌管理开销,时间戳易于使用)。因此,在运行更改脚本之前,我们会检查是否已应用更改脚本,方法是baselineversion查询更改脚本。如果它已经存在,只需退出脚本或其他任何内容。否则,应用更改并插入schema_version表以标记更改脚本已完成。

示例更改脚本:

-- Created by <developer> on Nov. 7, 2012 at 12:20 PM UTC
declare @schema_baseline varchar(10), @schema_version varchar(12)

set @schema_baseline = 'v1.0'
set @schema_version = '201211071210'

if exists (select 1 from schema_version where baseline = @schema_baseline and version = @schema_version = @schema_version) return 0

-- begin change script

-- place your schema changes here

-- end change script

insert into schema_version(@schema_baseline, @schema_version)

现在,当我们实际安装软件时,我们运行相关的baseline脚本。当我们升级该版本时,我们只是按顺序应用更改脚本。

当我们在产品开发阶段达到一个重要的里程碑时,我们会创建一个新的基线。因此,我们创建了一个新的基线脚本(同样,这是数据库作为基线的快照),以及上一个基线的升级脚本。所以我们假设我们有一个新的基线v2.0,我们有以下文件:

baseline-v2.0.sql (for creating new installations)
baseline-v2.0-upgrade-v1.0.sql (for upgrading from v1.0)

然后这个过程继续。

我们如何应用更改

脚本都保存在源代码管理中。我们有一个工具可以打包这些文件并自动升级我们的支持和安装团队使用的数据库。该工具计算出目标数据库的当前基线,并询问用户是否希望升级到包中的基线。如果这样做,并且当前版本存在有效的升级路径,则它将应用升级脚本,并更新schema_version.baseline,并从先前的基准中删除更改脚本的所有条目。如果数据库是新的,则它应用常规基线脚本。无论哪种方式,在实现基线之后,它会在事务中按顺序应用程序包中存在的所有更改脚本。如果特定的更改脚本失败,它将回滚最后一组更改和错误。我们查看日志,修复任何问题,然后重新运行包。此时,它应该只是在最后一个成功的更改脚本中获取,从而节省时间。

自动化和差异工具

我们不允许diff工具直接升级生产数据库。这太危险了。当然,我们使用diff工具来帮助创建我们的升级和更改脚本,但是一旦我们拥有它们,我们梳理它们,按摩它们,测试它们等,然后根据上面的规范创建升级或更改脚本。我们使用工具/ shell脚本来创建更改脚本文件并将锅炉板schema_version检查。

<强>注意事项

它实际上非常简单,效果很好。唯一真正变得棘手的是分支机构。在大多数情况下,分支机构处理得很好。如果我们需要一个特定分支工作的更改脚本,一旦我们将分支合并回来,它将很好地折叠到主线。没问题。它变得棘手的地方是两个分支尝试做类似的事情,或者一个分支依赖另一个分支。但这主要是一个过程和计划问题。如果我们陷入这种情况,我们只需创建一个新基线(比如v2.1),然后相应地更新分支。

要记住的另一件事是,如果安装想要从一个基线升级到另一个基线,则必须在升级到新基线之前应用当前基线的所有未完成的更改。换句话说,我们不会让安装从它们的任何位置跳到下一个基线(当然,除非它们已经是当前基线的最新版本)。

答案 1 :(得分:11)

我建议使用SQL Server Data Tools和/或Visual Studio SQL数据库项目。它会将您现有的数据库反向工程为可以进行版本控制的代码(sql)文件,并提供许多其他细节(发布,比较等)

答案 2 :(得分:5)

我们专门开发了SQL Source Control来解决您描述的问题。它扩展了SSMS,以提供SQL Server架构对象(和静态数据)与现有源代码控制系统之间的链接。

http://www.red-gate.com/products/sql-development/sql-source-control/

如果您需要更多信息,我们非常乐意提供帮助(联系support@red-gate.com)

答案 3 :(得分:3)

在许多开发者论坛上有很多关于这个主题的讨论。

我所做的并被发现是最简单,最干净的方法是:

  1. 将每个数据库对象的DDL解压缩到自己的文件中,索引和PK可以与它们所属的表位于同一个文件中。 FK,过程,视图,触发器,可以跨多个表的任何内容都放在他们自己的文件中。

  2. 按对象类型(例如表格,程序,触发器,视图等)以dirs组织DDL文件。

  3. 对于包含静态参考数据(例如邮政编码或州)的表,请使用包含一堆插入语句的单独文件

  4. 将此目录结构检查到您正在使用的任何版本控制

  5. 编写一个脚本,该脚本将遍历对您的数据库进行映像的目录结构,将其与您指向的实际数据库进行区分(从系统表中提取模式)并使用ALTER TABLE语句应用差异

  6. 如果您在版本之间进行数据转换,例如在v1中你有一个字段FirstAndLastName而在v2中你决定将它拆分为FirstName和LastName,你将有一些批量数据迁移/处理语句。

  7. 我使用多个不同的RDBMS成功管理了多个作业中的数据库更改。我通常使用Perl作为区分数据库模式和图像中的DDL文件的脚本。这个方法有一些假设,其中之一就是你永远不会直接在DB中对DB进行任何更改,而是在DDL文件中,然后运行脚本来应用它。如果以其他方式执行此操作,则在运行脚本时将撤消它们。所以它需要一些团队协议和纪律。你的milage可能会有所不同。

    现在,如果有一个FOSS工具会为你做这件事,一定要使用它而不是设计你自己的。我已经用这种方式做了超过10年的事情

答案 4 :(得分:-1)

我们的Sql Historian源代码控制系统可以帮助解决这个问题的人,特别是在你提到的队友和#34;忘记&#34;在他们更新服务器后签入代码。

它位于后台,并将对数据库对象所做的所有更改记录到源代码控制中,无需用户检查任何内容。可以将其视为飞机黑盒记录器,直到需要它为止。