重新运行数据库开发脚本

时间:2008-09-08 12:34:48

标签: sql-server database version-control sdlc

在我们当前的数据库开发环境中,我们自动构建procceses检查svn创建数据库脚本中的所有sql代码,并将它们应用于各种开发/ qa数据库。

这一切都很好,并且比我们过去的做法有了很大的改进,但我们遇到了重新运行脚本的问题。显然,对于某些脚本(如更改过程)而言,这不是问题,因为您可以反复运行它们而不会对系统产生不利影响。现在要添加元数据和运行语句(如create / alter table语句),我们添加代码来检查并查看对象是否存在,如果存在,则不要运行它们。

我们的问题是我们实际上只有一次运行脚本,因为一旦脚本运行,对象就在环境中,系统将不再运行脚本。如果在部署后需要更改某些内容,我们就会遇到一个难以运行的更新脚本的过程,并且希望所有内容都按照正确的顺序排列,并且所有PK都在环境之间排列(数据库是,我们应该说,“特别”)。

没有放弃数据库并从头开始(最新的最新版本),有没有人有更优雅的解决方案呢?

6 个答案:

答案 0 :(得分:2)

我不确定在特定环境中如何最好地解决问题,但我建议您阅读Rail的迁移功能,了解如何开始使用。

http://wiki.rubyonrails.org/rails/pages/UnderstandingMigrations

答案 1 :(得分:2)

我们解决这个问题 - 或者至少是类似的问题 - 如下:

  1. 架构有一个版本号 - 这是一个表,每个版本有一行,除版本号外,还带有无聊的东西,比如该版本何时出现的日期/时间戳。
  2. 通过让架构创建/修改DDL包装在代码中,为我们执行更改。
  3. 在上面的上下文中,将构建架构更改代码作为构建过程的一部分然后运行它,它将仅应用尚未应用的架构更改。

    根据我们的经验(一定不具代表性),在大多数情况下,架构更改足够小/快,以至于它们可以安全地在事务中运行,这意味着如果它失败,我们会得到回滚,db是“安全“ - 尽管人们总是建议在应用架构更新之前进行备份,如果可行的话。

    我从令人讨厌的痛苦经历中演变出来。它不是一个完美的系统(或一个原创的想法),但由于这种方式的工作,我们高度自信,如果我们的一个数据库有两个具有相同版本的实例,那么这两个数据库的模式将在几乎所有方面都是相同的,我们可以安全地将任何数据库带到该应用程序的当前架构,而不会产生不良影响。 (不幸的是,最后一次并非100%真实 - 总有一个例外 - 但它离真相并不太远!)

答案 2 :(得分:1)

您是否将现有数据保存在数据库中?如果没有,您可能希望查看类似于Matt提到的名为RikMigrations

的内容
http://www.rikware.com/RikMigrations.html

我在我的项目中使用它来动态更新我的数据库,同时跟踪修订。此外,它使得将数据库模式移动到不同的服务器等非常简单。

答案 3 :(得分:0)

如果你想在你的脚本中重新运行,那么就不能把它们作为定义......我的意思是你需要关注改变脚本而不是我的表脚本。< / p>

假设您有一个表客户:

create table Customers (
   id int identity(1,1) primary key,
   first_name varchar(255) not null,
   last_name varchar(255) not null
)

以后要添加状态列。不要修改已经运行的原始表脚本(并且可以使用if(!exists)语法来防止它再次运行时导致错误。)

相反,有一个名为add_customer_status.sql的新脚本

在这个脚本中你会有类似的东西:

alter table Customers
add column status varchar(50) null

update Customers set status = 'Silver' where status is null

alter table Customers
alter column status varchar(50) not null

再次,您可以使用if(!exists)块来包装它以允许重新运行,但是在这里我们利用了这是一个更改脚本的概念,并且我们相应地调整了数据库。如果customers表中已有数据,那么我们仍然可以,因为我们添加了列,用数据播种,然后添加非空约束。

上面提到的两个迁移框架都很好,我也有MigratorDotNet的优秀经验。

答案 4 :(得分:0)

Scott命名了其他几个SQL tools来解决变更管理问题。但是我还在继续自己。

我想回答这个问题,并加上我的困惑,即仍然没有免费的,基于社区的工具来解决这个问题。显然,脚本不是维护数据库模式的令人满意的方法;也不是实例。那么,为什么我们不将元数据保存在一个单独的(当我们处于平台中立的格式)格式时呢?

这就是我现在正在做的事情。我的主数据库模式是一个版本控制的XML文件,最初是从一个简单的Web服务创建的。一个简单的javascript程序将实例与它进行比较,一个简单的XSL转换产生CREATE或ALTER语句。它有限制,如RikMigrations;例如,它并不总是正确地对inter-depdendent对象进行排序。 (但猜猜是什么 - Microsoft's SQL Server Database Publication tool都没有。)真的,这太简单了。我根本没有包含我没有使用的对象(角色,用户等)。

所以,我认为这个问题确实没有得到充分解决,迟早我们必须聚在一起解决恶魔的细节。

答案 5 :(得分:0)

我们去了'drop并重新创建架构'路线。我们在JUnit测试包中有一些类,它们对脚本进行参数化,以便为执行代码的开发人员创建模式中的所有对象。这允许所有开发人员共享一个测试数据库,并且每个人都可以同时创建/测试/删除他们的测试表而不会发生冲突。

运行需要很长时间吗?是。起初我们使用了这个设置方法,这意味着每次测试都会删除/创建表格,这种方法花费的时间太长了。然后我们创建了一个TestSuite,它可以在类的所有测试之前运行一次,然后在所有类测试完成后清理。这仍然意味着当我们运行'AllTests'类时,db设置运行了很多次,其中包括我们所有软件包中的所有测试。我如何解决它是在OracleTestSuite代码中添加一个信号量,所以当第一个测试请求设置数据库时,它会这样做,但任何后续调用只会增加一个计数器。当调用每个tearDown()方法时,计数器将递减计数器,直到它达到0并且OracleTestSuite代码将丢弃所有内容。这留下的一个问题是测试是否假定数据库为空。让数据库测试知道它们运行的​​顺序可以很方便,这样它们就可以利用数据库的状态,因为它可以减少数据库设置的重复。

我们使用ObjectMothers的概念来解决类似的问题,即为测试目的创建复杂的域对象。模拟对象可能是一个更好的答案,但我们当时没有听说过它们。在这段时间之后,我建议创建可以为典型场景创建标准化数据集的测试助手方法。此外,这将有助于从数据角度记录重要的边缘情况。