2

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

这一切都很好,与我们过去所做的相比有了很大的改进,但我们在重新运行脚本时遇到了问题。显然,对于某些脚本(例如更改过程)来说,这不是问题,因为您可以一遍又一遍地运行它们而不会对系统造成不利影响。现在要添加元数据并运行诸如创建/更改表语句之类的语句,我们添加代码来检查对象是否存在,如果存在,则不要运行它们。

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

没有删除数据库并从头开始流程(最新的最新版本),有没有人对此有更优雅的解决方案?

4

6 回答 6

2

我不确定如何最好地在您的特定环境中解决问题,但我建议阅读 Rail 的迁移功能以获得有关如何开始的一些灵感。

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

于 2008-09-08T12:38:15.147 回答
2

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

  1. 模式有一个版本号 - 这由一个表表示,每个版本有一行,以及版本号,带有无聊的东西,比如该版本何时存在的日期/时间戳。
  2. 通过将模式创建/修改 DDL 包装在为我们执行更改的代码中。

在上面的上下文中,将构建架构更改代码作为构建过程的一部分,然后运行它,它只会应用尚未应用的架构更改。

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

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

于 2008-09-08T15:56:42.223 回答
1

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

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

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

于 2008-09-08T12:45:23.947 回答
0

如果你想在你的脚本中重新运行,那么你不能将它们作为定义......我的意思是你需要专注于更改脚本,而不是我的 Table 脚本。

假设您有一张表客户:

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

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

于 2008-09-08T14:45:36.230 回答
0

Scott列举了几个解决变更管理问题的其他SQL 工具。但我仍然在自己动手。

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

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

所以,我的观点是,这个问题确实没有得到充分的解决,迟早我们会聚在一起,解决这些可怕的细节。

于 2008-09-08T15:23:05.330 回答
0

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

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

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

于 2009-07-08T18:56:35.610 回答