14

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

我的目标是让数据库处于版本控制之下,所以我一直在阅读文章,比如Scott Allen 的系列和许多与 SO 相关的老问题。但我仍然无法决定如何进行。

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

有什么更好的选择?而且,我是否忘记了任何值得进行源代码控制的元素?我最担心的是,我发现的大多数文献都解释了在创建新数据库时如何进行版本控制,但在它已经很旧且相对较大的情况下却没有。

4

5 回答 5

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只是创建更改脚本时的时间戳(我们选择这样做是因为创建任意版本号会产生烦人的管理开销,而时间戳很容易使用)。因此,在运行更改脚本之前,我们通过baselineand查询来检查更改脚本是否已被应用version。如果它已经存在,只需退出脚本或其他任何内容。否则,应用更改并插入到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 工具来帮助创建我们的升级和更改脚本,但是一旦有了它们,我们就会梳理它们、按摩它们、测试它们等,然后根据上面的规范创建升级或更改脚本. 我们确实使用工具/shell 脚本来创建更改脚本文件并进行样板schema_version检查。

注意事项

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

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

于 2012-11-07T17:54:23.750 回答
11

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

于 2012-11-07T17:07:27.007 回答
5

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

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

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

于 2012-11-30T08:56:31.513 回答
3

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

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

  1. 将每个 DB 对象的 DDL 提取到自己的文件中,索引和 PK 可以与它​​们所属的表放在同一个文件中。FK、过程、视图、触发器,任何可以跨越多个表的东西都放在它们自己的文件中。

  2. 根据对象类型(例如表、过程、触发器、视图等)在目录中组织 DDL 文件

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

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

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

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

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

现在,如果有一个 FOSS 工具可以为您执行此操作,请务必使用该工具,而不是自己设计。我这样做已经超过 10 年了

于 2012-11-07T17:32:03.470 回答
-1

我们的Sql Historian源代码控制系统可以帮助人们解决这个问题,尤其是在您提到队友在更新服务器后“忘记”签入代码的情况下。

它位于后台,并将对您的数据库对象所做的所有更改记录到源代码控制中,而用户无需签入任何内容。可以将其想象为飞机黑匣子记录器,在您需要它之前一直保持原状。

于 2015-02-04T16:58:36.097 回答