【问题标题】:VSTS build - Incremental database deployment in distributed environmentVSTS build - 分布式环境中的增量数据库部署
【发布时间】:2026-02-13 06:00:01
【问题描述】:

我有一个使用 .net 2015 mvc 5 应用程序的 sql server 数据库。我的数据库代码是使用 SSDT 项目进行源代码控制的。我正在使用 SqlPackage.exe 使用由 SSDT 项目构建过程创建的 .Decpac 文件将数据库部署到暂存环境。这是使用 VSTS build 的 powershell 任务完成的。 通过这种方式,我可以以源代码控制的方式更改数据库架构。现在的问题是关于数据库的主数据插入。

我使用一个 sql 脚本文件,该文件具有作为部署后脚本执行的数据插入脚本。该文件也是源代码控制的。

问题在于,最初我们已经准备好插入脚本以针对第一个版本的 sprint(以 sprint n 为基础)。但是在下一个冲刺中,如果更新一些主数据,那么应该如何更新主数据插入:

  1. 在脚本文件的最后添加新的更新/插入查询?但在这种情况下,部署后脚本将由 CI 执行,它会尝试在后续构建中一次又一次地插入数据,如果我们在该数据库的主表中进行了一些架构更改,最终将失败。
  2. 更新数据插入脚本中的现有插入查询。在这种情况下,我们也会遇到麻烦,因为在构建后事件中,整个数据将被重新插入。
  3. 为每个脚本维护单独的数据插入脚本,并将脚本引用更新为 SSDT 构建后事件的新文件。这种方法需要手动操作并且容易出错,因为开发人员必须记住这个过程。这种方法的另一个问题是,如果我们需要在分布式服务器场中再设置 1 个数据库服务器。多个数据插入脚本会抛出错误,因为 SSDT 具有最新的架构,并且它将创建一个具有相同架构的数据库。但较旧的数据脚本具有先前模式的数据插入(在以后的 sprint 中更改的 sprint wise db 模式)

因此,任何人都可以建议人工操作较少但可以涵盖所有上述情况的最佳方法。

谢谢 鲁彭德拉

【问题讨论】:

  • 我们使用了多个脚本来检查这些值是否已经在表中。如果有重大更改,您可能需要对脚本进行版本控制以适用于较新版本的架构。要么,要么你对你的 dacpacs/项目进行版本控制,然后逐步发布。
  • 你得到了帮助你解决问题的答案吗?如果是,您可以将其标记为答案。它会帮助其他有类似问题的人。
  • 嗨@Marina-MSFT 我还没有最终确定。最初,我的观点是使用 Sprint 明智的 SSDT 文件和每个 sprint 的主数据文件作为后期构建脚本。如果需要完整部署,我将以增量方式运行所有 SSDT。但我需要测试一下。
  • 好问题:)

标签: sql-server continuous-integration sql-server-data-tools azure-pipelines database-deployment


【解决方案1】:

确保您的部署前和部署后脚本始终是幂等的。但是,您要实现这取决于您。脚本应该能够运行任意次数并始终产生正确的结果。 因此,如果您的架构更改会影响部署脚本,那么更新脚本是更改的依赖项,并伴随它在源代码控制中。 数据库的版本控制已经是 SSDT 的内置功能。在项目文件本身中,有一个版本节点。 VSTS 中有大量版本控制构建任务,您也可以免费使用它来对其进行版本控制。当 SqlPackage.exe 在已设置数据库版本的情况下发布您的项目时,会更新 msdb.dbo.sysdac_instances 中的一条记录。这比尝试管理、更新等您自己的本地版本解决方案要容易得多。而且您不会用与应用程序本身无关的表和其他对象来弄乱应用程序的数据库。 我同意将 sprint 信息排除在外。 在我们的项目中,我用构建号标记成功构建的源代码,这当然会在源代码中创建一个与特定构建链接的时间点标记。

【讨论】:

    【解决方案2】:

    我建议使用 MERGE 语句而不是插入语句。这样,您就可以在 sprint 范围内避免重复插入。

    接下来是如何区分不同冲刺的不同插入。我建议实施版本编号以将数据库与 sprint 同步。所以创建一个表 DbVersion(version int)。 然后在部署后脚本中执行以下操作:

    SET @version = SELECT ISNULL(MAX(version), 0) FROM DbVersion 
    IF @version < 1
     --inserts/merge for sprint 1
    IF @version < 2
     --inserts/merge for sprint 2
    ...
    INSERT INTO DbVersion(@currentVersion)
    

    【讨论】:

    • 我正在尝试一些解决方案和工具。一旦我尝试了你的解决方案,如果它运作良好。我会标记它或恢复。谢谢
    • 您可以在这里找到更多详细信息:enterprisecraftsmanship.com/2015/08/18/…
    【解决方案3】:

    我在大多数项目中所做的是创建MERGE 脚本,每个表一个,填充“主”或“静态”数据。 https://github.com/readyroll/generate-sql-merge 等工具可用于帮助生成这些脚本。

    这些是从部署后脚本调用的,而不是在构建后操作中调用的。我通常会为项目创建一个(你只允许一个!)部署后脚本,然后使用:r 语法包含所有单独的静态数据脚本。部署后脚本只是一个带有“部署后”构建操作的.sql 文件,可以“手动”创建,也可以使用 SSDT 中的“添加新对象”对话框并选择脚本 -> 部署后脚本。

    这些文件(包括部署后脚本)可以与其他源文件一起进行版本控制;如果您对表定义进行了更改,需要在填充数据的 merge 语句中进行更改,则可以一起提交这些更改。

    当您构建 dacpac 时,所有主数据都将包含在内,并且由于您使用的是 merge 而不是插入,因此您可以保证在部署结束时 contents表将匹配源代码管理的 contents,就像 SSDT/sqlpackage 保证表的 结构 匹配其定义的 结构源代码管理。

    我不清楚“冲刺”的概念是如何出现的,除非“冲刺”意味着“发布”;在这种情况下,在 sprint 结束时构建和发布的 dacpac 将包含所有更改,包括在 sprint 期间添加的结构和“主数据”。我认为将“冲刺”的概念远离源代码控制可能是明智之举!

    【讨论】:

    • 我正在尝试一些解决方案和工具。一旦我尝试了你的解决方案,如果它运作良好。我会标记它或恢复。谢谢