重新运行数据库开发脚本

时间:2020-03-05 18:49:19  来源:igfitidea点击:

在当前的数据库开发环境中,我们已自动进行构建过程以从svn create database脚本中检查所有sql代码,并将其应用于各种development / qa数据库。

这一切都很好,并且比我们过去做的要好得多,但是重新运行脚本存在问题。显然,对于某些脚本(例如更改过程)而言,这不是问题,因为我们可以反复运行它们而不会不利地影响系统。现在添加元数据和运行语句(如创建/更改表语句),我们添加代码以检查对象是否存在,如果存在,则不要运行它们。

我们的问题是,我们实际上只获得了运行脚本的机会,因为一旦脚本运行完毕,对象就在环境中,系统将不再运行脚本。如果部署后需要更改某些内容,我们将很难运行更新脚本,使更新脚本无法运行,并希望一切都以正确的顺序排列,并且所有PK都在环境之间排成一行(数据库应该是我们所说的) , "特别的")。

除了删除数据库并从头开始流程(最新的最新版本)之外,没有人对此有更完善的解决方案吗?

解决方案

回答

我不确定在特定环境中如何最好地解决该问题,但是我建议我们阅读Rail的迁移功能,以获取有关入门的启发。

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

回答

我们是否将现有数据保留在数据库中?如果没有,我们可能想看看类似于Matt为.NET提到的称为RikMigrations的内容。

http://www.rikware.com/RikMigrations.html

我在项目中使用它来动态更新数据库,同时跟踪修订。同样,这使得将数据库架构移动到其他服务器等非常简单。

回答

如果要在脚本中具有可重运行性,则不能将它们作为定义...我的意思是,我们需要专注于更改脚本,而不是这里的Table脚本。

假设我们有一张桌子客户:

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

然后我们要添加一个状态列。不要修改我们原来的表脚本,该脚本已经运行(并且可以使用if(!存在)语法来防止它在再次运行时引起错误)。

而是使用一个名为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(!exist)块包装它以允许重新运行,但是这里我们利用了这是一个更改脚本的概念,并相应地调整了数据库。如果客户表中已经有数据,那么我们还是可以的,因为我们添加了列,并为它添加了数据,然后添加了非null约束。

上面提到的两个迁移框架都很好,我在MigratorDotNet方面也有出色的经验。

回答

Scott提出了其他两个用于解决变更管理问题的SQL工具。但是我仍然在自己动手。

我想提出第二个问题,并且令我感到困惑的是,仍然没有针对该问题的免费的,基于社区的工具。显然,脚本不是维护数据库模式的令人满意的方法。实例也不是。那么,为什么我们不将元数据保存为单独的格式(在平台上还是中立的)?

那就是我现在正在做的。我的主数据库模式是一个版本控制的XML文件,最初是从一个简单的Web服务创建的。一个简单的javascript程序将实例与之进行比较,而一个简单的XSL转换将产生CREATE或者ALTER语句。它有限制,例如RikMigrations;例如,它并不总是正确地对相互依赖的对象进行排序。 (但是,请猜猜Microsoft的SQL Server数据库发布工具没有什么。)确实,这太简单了。我只是没有包含未使用的对象(角色,用户等)。

因此,我的观点是,这个问题确实没有得到充分解决,我们迟早必须聚在一起解决棘手的细节。

回答

我们解决了这个问题,或者至少解决了类似的问题,如下所示:

  • 模式具有版本号-由一个表表示,每个表具有一行,该表以及版本号携带无聊的内容,例如该版本何时存在的日期/时间戳。
  • 通过将模式创建/修改DDL封装在为我们执行更改的代码中。

在上面的上下文中,将在构建过程中构建模式更改代码,然后运行它,并且它将仅应用尚未应用的模式更改。

根据我们的经验(一定不能代表),在大多数情况下,模式更改足够小/快速,可以在事务中安全地运行它们,这意味着,如果失败,我们将进行回滚并且db是"安全的",尽管在可行的情况下,始终建议在应用架构更新之前进行备份。

我是从讨厌的痛苦经历中演变而来的。它不是一个完美的系统(或者原始想法),但是通过这种方式工作,我们具有高度的信心:如果我们数据库之一的两个实例具有相同的版本,则这两个数据库的模式将在几乎所有方面都相同,并且我们可以安全地将任何数据库升级到该应用程序的当前模式,而不会带来不良影响。 (不幸的是,最后一个并不是100%正确,总是有一个例外,但离事实相差不远!)

回答

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

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

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