【问题标题】:How can I conditionally include large scripts in my ssdt post deployment script?如何有条件地在我的 ssdt 部署后脚本中包含大型脚本?
【发布时间】:2016-09-05 17:36:02
【问题描述】:

在我们的 SSDT 项目中,我们有一个巨大的脚本,其中包含许多用于从旧系统导入数据的 INSERT 语句。使用 sqlcmd 变量,我希望能够有条件地将文件包含到部署后脚本中。

我们目前正在使用 :r 语法,其中包括内联脚本:

IF '$(ImportData)' = 'true'
BEGIN
  :r .\Import\OldSystem.sql
END

这是一个问题,因为无论$(ImportData) 是真还是假,脚本都被包含在内联中,而且文件太大以至于将构建速度减慢了大约 15 分钟。 p>

是否有另一种方法可以有条件地包含此脚本文件,以免减慢构建速度?

【问题讨论】:

  • :R 命令以及所有其他 SQLCMD 命令在条件块中不起作用 - 它们始终执行。解决方法是使用 OldSystem.sql 文件用 IF '$(ImportData)' = 'true' 包装旧系统代码
  • 为了性能,您是否考虑过批量插入而不是 INSERTing?

标签: sql sql-server sql-server-2014 sql-server-data-tools


【解决方案1】:

而不是将我之前的答案与另一个混淆。有一个非常简单的选项的特殊情况。

为每个执行可能性创建单独的 SQLCMD 输入文件。 这里的关键是使用控制变量的值来命名执行输入文件。

因此,例如,您的发布脚本定义了变量“Config”,该变量可能具有以下值之一:“Dev”、“QA”或“Prod”。

创建 3 个名为“DevPostDeploy.sql”、“QAPostDeploy.sql”和“ProdPostDeploy.sql”的部署后脚本。

像这样编写您的实际部署后文件:

:r ."\"$(Config)PostDeploy.sql

这非常类似于构建事件机制,您可以用适当的脚本覆盖脚本,但您不需要构建事件。但是您依赖于非常具体地命名您的脚本。

【讨论】:

  • 我的数据库部署了两次,具有不同的 SQLCMD 属性,以这种方式使用。只有通过第二次部署传入的值用于两个部署。这几乎就像第一个的值被覆盖了。只有它们是按顺序完成的……第一次部署的目标连接是正确的。
  • 公平警告 - 如果您仍然坚持使用 VS2013,则通过此方法包含的文件不一定与“发布”对话框匹配。似乎它将始终包含基于 SQLCMD 变量选项卡上的 Project 属性中指定的变量值的文件。我没有使用更新版本的 Visual Studio 进行测试。
  • 非常感谢 Nathan 的评论,我遇到了同样的行为,无法理解为什么无论我输入什么值都会包含相同的部署文件。这似乎是一个不错的解决方案,但并非在所有情况下都有效。
  • 这似乎不再起作用,我尝试使用在项目设置中设置的 sqlcmd 变量,但它在生成的脚本中显示为空。例如 $(SqlCmdVariable1),当在路径中用作 :r 时。"\"$(SqlCmdVariable1)$(InlineSetVar1).. 它采用的路径是除了 SqlCmdVariable 之外的所有内容。我在 postdeploy 文件中设置的 InlineSetVar1 工作正常如 :setvar InlineSetVar1 "_Something.Sql"
  • 谢谢@JARRRRG。提及您注意到它不再有效的版本可能会很有用。
【解决方案2】:

始终包含使用 :r 引用的脚本。您有几个选择,但我会首先验证,如果您将脚本取出,它会将性能提高到您希望它到达的位置。

最简单的方法是将其保留在整个构建过程之外并更改您的部署过程,使其成为一个两步的事情(部署 DAC 然后部署脚本)。这样做的好处是您可以在 ssdt 进程之外做一些事情,但坏处是您无法在部署中自动禁用对表更改的约束。

第二种方法是在构建时不将脚本包含在部署中,而是创建一个 AfterBuild msbuild 任务,将脚本作为部署后脚本添加到 dacpac 中。 dacpac 是一个 zip 文件,因此您可以使用 .net 打包 Api 添加一个名为 postdeploy.sql 的部分,然后将其包含在部署过程中。

这两种方式都意味着您会丢失验证,因此您可能希望将其保存在一个单独的 ssdt 项目中,该项目对您的主项目具有“相同的数据库”引用,当它发生变化时会减慢构建速度,但应该快速其余时间。

【讨论】:

    【解决方案3】:

    这是我必须这样做的方式。

    1) 创建一个虚拟的部署后脚本。

    2) 在您的项目中为每个部署场景创建构建配置。

    3) 使用预构建事件来确定要使用的部署后配置。 您可以为每个配置创建单独的脚本,也可以在预构建事件中动态构建部署后脚本。无论哪种方式,您所做的工作都基于始终存在于构建事件中的 $(configuration) 的值。

    如果您使用单独的静态脚本,您的构建事件只需要复制适当的静态文件,使用在该部署场景中有用的任何脚本覆盖虚拟的部署后。

    在我的例子中,我不得不使用动态生成,因为决定要包含哪些脚本需要了解正在部署到的数据库的当前状态。所以我使用配置变量告诉我正在部署哪个环境,然后使用 SQLCMD 脚本并将 :OUT 设置为我的部署后脚本位置。因此,我的预构建脚本将动态编写部署后脚本。

    无论哪种方式,一旦构建完成并开始正常的部署过程,部署后脚本就包含了我想要的 :r 命令。

    这是我在预构建中调用的 SQLCMD 脚本的示例。

    :OUT .\Script.DynamicPostDeployment.sql
    
    PRINT ' /*';
    PRINT '     DO NOT MANUALLY MODIFY THIS SCRIPT.                                               ';
    PRINT '                                                                                       ';
    PRINT '     It is overwritten during build.                                                   ';
    PRINT '     Content IS based on the Configuration variable (Debug, Dev, Sit, UAT, Release...) ';
    PRINT '                                                                                       ';
    PRINT '     Modify Script.PostDeployment.sql to effect changes in executable content.         ';
    PRINT ' */';
    PRINT 'PRINT ''PostDeployment script starting at''+CAST(GETDATE() AS nvarchar)+'' with Configuration = $(Configuration)'';';
    PRINT 'GO';
    IF '$(Configuration)' IN ('Debug','Dev','Sit')
    BEGIN
        IF (SELECT IsNeeded FROM rESxStage.StageRebuildNeeded)=1
        BEGIN
            -- These get a GO statement after every file because most are really HUGE
            PRINT 'PRINT ''ETL data was needed and started at''+CAST(GETDATE() AS nvarchar);';
            PRINT '                                                  ';
            PRINT 'EXEC iESxETL.DeleteAllSchemaData ''pExternalETL'';';
            PRINT 'GO';
            PRINT ':r .\PopulateExternalData.sql         ';
    ....
    

    【讨论】:

      【解决方案4】:

      我最终混合使用了我们的构建工具 (Jenkins) 和 SSDT 来完成此任务。这就是我所做的:

      1. 为每个写入文本文件的特定于环境的 Jenkins 作业添加了一个构建步骤。我要么编写一个包含导入文件的 SQLCMD 命令,要么根据用户选择的构建参数将其留空。
      2. 通过:r 在部署后脚本中包含新文本文件。

      就是这样!我还使用相同的方法根据应用程序版本选择要包含在项目中的部署前和部署后脚本,除了我从代码中获取版本号并使用 VS 中的预构建事件将其写入文件在构建工具中。 (我还将文本文件名添加到.gitignore,因此它不会被提交)

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2015-05-04
        • 2020-03-30
        • 1970-01-01
        • 1970-01-01
        • 2013-09-24
        • 1970-01-01
        • 2015-07-22
        相关资源
        最近更新 更多