代码之家  ›  专栏  ›  技术社区  ›  kemiller2002

重新运行数据库开发脚本

  •  2
  • kemiller2002  · 技术社区  · 16 年前

    在我们当前的数据库开发环境中,我们有自动构建过程,检查SVN创建数据库脚本中的所有SQL代码,并将它们应用到各种开发/QA数据库中。

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

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

    除了删除数据库并从头开始这个过程(最后一个最新的版本)之外,还有人对此有更优雅的解决方案吗?

    6 回复  |  直到 15 年前
        1
  •  2
  •   Matt Sheppard    16 年前

    我不知道在您特定的环境中如何最好地解决这个问题,但是我建议您阅读一下Rail的迁移特性,以获得一些关于如何开始的启发。

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

        2
  •  2
  •   Murph    16 年前

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

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

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

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

    我是从痛苦的经历中发展出来的。它不是一个完美的系统(或一个最初的想法),但通过这样的工作,我们有很高的信心,如果我们的一个数据库中有两个实例的版本相同,那么这两个数据库的模式在几乎所有方面都是相同的,并且我们可以安全地将任何数据库带到当前的模式中,用于应用时不会产生不良影响。(不幸的是,最后一个不是100%正确的——总是有例外——但这与事实并不遥远!)

        3
  •  1
  •   Ryan Lanciaux    16 年前

    是否将现有数据保留在数据库中?如果没有,您可能想看看类似Matt为.NET所提到的 RikMigrations

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

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

        4
  •  0
  •   Ben Scheirman    16 年前

    如果您希望在脚本中具有可重新运行性,那么就不能将它们作为定义来使用……我的意思是,您需要关注更改脚本,而不是这里的表脚本。

    假设您有一个桌子客户:

    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 \u customer\u 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 .

        5
  •  0
  •   Community rcollyer    7 年前

    Scott 叫了另外两个 SQL tools 这解决了变更管理的问题。但我仍在继续我自己的生活。

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

    这就是我现在要做的。我的主数据库模式是一个版本控制的XML文件,最初是从一个简单的Web服务创建的。一个简单的javascript程序将实例与之进行比较,一个简单的XSL转换生成create或alter语句。它有局限性,比如 RikMigrations ;例如,它并不总是正确地对跨部门对象排序。(但你猜怎么着-也不知道 Microsoft's SQL Server Database Publication tool 真的,太简单了。我只是不包括我没有使用的对象(角色、用户等)。

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

        6
  •  0
  •   Kelly S. French    15 年前

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

    跑步花了很长时间吗?对。起初,我们使用了这个设置方法,这意味着每个测试都会删除/创建表,这花费了太长的时间。然后我们创建了一个测试套件,它可以在类的所有测试之前运行一次,然后在所有类测试完成后进行清理。这仍然意味着当我们运行“all tests”类时,DB安装程序会运行很多次,其中包括我们所有包中的所有测试。我解决这个问题的方法是向oracletestsuite代码中添加一个信号量,所以当第一个测试请求设置数据库时,它会这样做,但随后的任何调用都只会增加一个计数器。当调用每个teardown()方法时,计数器将递减计数器,直到它达到0,并且OracleTestSuite代码将删除所有内容。剩下的一个问题是测试是否假定数据库是空的。让数据库测试知道它们的运行顺序是很方便的,这样它们就可以利用数据库的状态,因为这样可以减少数据库设置的重复。

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