“如果可能发生数据丢失,阻止增量部署”的粒度

Posted

技术标签:

【中文标题】“如果可能发生数据丢失,阻止增量部署”的粒度【英文标题】:Granularity of "Block incremental deployment if data loss might occur" 【发布时间】:2013-08-12 09:29:10 【问题描述】:

在 SQL Server 数据工具中,您有部署选项“如果可能发生数据丢失,则阻止增量部署”,我敢打赌这是保持检查的最佳做法。

假设我们有一个表 foo 和一个现在是多余的列 bar - 没有依赖项、外键等,我们已经在数据层和存储过程中删除了对该列的引用,因为它根本没有使用.换句话说,我们对删除此列不会产生不利影响感到满意。

美中不足:

    该列中有数据 数据库发布到 数百个分布式客户端,并且可能需要几个月的时间 更改为波及所有客户

当该列被填充时,发布将失败,除非我们更改“如果可能发生数据丢失,阻止增量部署”选项。这个选项是在数据库级别,而不是表级别,因此由于客户端的分布式特性,我们必须在所有数据库更新之前关闭“数据丢失”选项几个月,然后重新打开一旦所有客户端都更新了(我们的数据库具有由我们的构建设置的版本号)。

您可能认为我们可以使用预部署脚本解决此问题,例如

if exists (select * from information_schema.columns where table_name = 'foo' and column_name = 'bar') BEGIN
  alter table foo drop constraint DF_foo_bar
  alter table foo drop column bar 
END

但这又会失败,除非我们关闭“可能发生数据丢失”选项。

我只是对其他人在这种情况下做了什么感兴趣,因为我希望获得目前看来不可能的粒度。

【问题讨论】:

我刚刚将该选项设置为 False 并意识到它已经完成并且知道后果。我不知道你是如何推动发布的。如果您正在逐步推动(应用程序包 1,然后应用程序包 2,等等),您可以将其关闭以便一个版本删除列,然后重新打开它。如果只是“根据需要”,我会考虑将其关闭并仔细观察数据库的变化。 谢谢@PeterSchott。我们的构建创建了在客户端下载和应用的 dacpacs,但由于修复程序和客户端的性质(例如,某些客户端可能在冬天不使用他们的机器,并在他们打开时下载最新版本)的 windows 切换特定版本的启动和启动有点困难。如果共识是设置数据丢失设置,那么这就是我必须考虑的事情。我一直认为它应该打开。 我普遍认为打开它会很有用,但尤其是在您的情况下,您可能希望关闭它。如果您了解一般风险,这没什么大不了的。如果您担心其他更改,您可以随时选择“生成脚本”选项来查看更改。您还可以使用选项来查看更改报告命令行或部分脚本选项,以查看可能受到的影响。 【参考方案1】:

所以我一直通过以下步骤完成这项任务:

1) 由于我们要创建 #Foo 表,如果该表存在,请确保在继续前删除该表。 2)在预部署脚本中:如果该列存在,则创建一个临时表#Foo并将Foo中的所有行选择到#Foo中。 3) 从#Foo 中删除该列 4) 删除 Foo 中的所有行(现在不会因为不存在数据而丢失数据) 5) 在部署后脚本中:如果 #Foo 存在,则从 #Foo 中选择所有行到 Foo 6) 删除表#Foo

及代码:

预部署脚本

if(Object_ID('TempDB..#Foo') is not null)
begin
    drop table #Foo
end
if exists (
    select *
    from sys.columns
    where Name = 'Bar'
        and Object_ID = Object_ID('Foo')
)
begin    
    select * into #Foo
    from Foo

    alter table #Foo drop column Bar

    -- Now that we've made a complete backup of Foo, we can delete all its data
    delete Foo
end

部署后脚本

if(Object_ID('TempDB..#Foo') is not null)
begin
    insert into Foo
    select * from #Foo

    drop table #Foo
end

警告:根据您的环境,在条件句中依赖版本而不是列和临时表可能更明智

【讨论】:

感谢您的回复。我一直在尝试这个,现在已经按照你描述的方式工作了。这里的关键点是表中必须有零行才能修改表。最后我没有检查版本,而是使用表结构上的信息模式视图来确定是否应该更改任何内容。不错的一个:) 只需阅读下面的评论“这显然意味着复制数据并可能删除和重新创建外键并检查约束等,具体取决于您的结构”这是真的 - 尽管该细节可能会偏离您的原始问题,对于任何未来的读者来说都很高兴。 这样做,您将无法回滚更改【参考方案2】:

由于 SSDT 的操作顺序,PreDeployment 脚本无法按照您希望的方式工作:

    架构比较 为架构差异生成脚本 执行预部署 执行生成的脚本 执行部署后。

当然,架构差异被识别为 #2 的一部分,并生成适当的 SQL 以删除列(包括检查以阻止数据丢失),然后您的手动预部署脚本才能“摆脱它” '。

如果您查看幕后生成的脚本以检测(并因此阻止)可能的数据丢失,它会通过运行以下内容来检查是否有任何行:

IF EXISTS (select top 1 1 from [dbo].[Table]) RAISERROR ('Rows were detected. The schema update is terminating because data loss might occur.', 16, 127)

这意味着行的简单存在将停止删除列。除了使用基于版本号的条件部署步骤,在 SSDT 部署之外(和之前)手动处理问题外,我们没有找到任何解决方法。

您提到分布式客户端,这意味着您拥有某种自动发布/更新机制。您还提到版本号作为数据库的一部分 - 您能否在部署中包含手动 SQL 脚本(在我假设您正在运行的 sqlpackage.exe 命令之前)?这类似于我们所做的(我们在 Powershell 中,但您明白了要点):

IF VersionNumber < 2.8 
BEGIN
    ALTER TABLE X DROP COLUMN Y
END

免责声明:这绝不是有效的 SQL,它只是暗示一个想法的伪代码!

【讨论】:

感谢hanzworld 的回复。您的评论“除了在 SSDT 部署之外(和之前)手动处理问题外,我们没有找到解决此问题的任何方法”几乎总结了我在这方面的进展,所以尽管我还没有真正实现这一点,就目前而言我认为这是唯一的选择。很遗憾,因为我真的喜欢 ssdt,但我想几乎没有银弹 :) 只要再读一遍,我应该因为使用“银弹”一词而被否决。 Lingo 宾果游戏! 你有上面所说的“操作顺序”的链接吗? @Fetchezlavache 我最初是根据经验写的,但这里有一个很好的幕后介绍,并比我总结的更好地回答了“为什么预部署脚本不影响部署脚本” - @987654321 @. 我现在可以根据 Phills 的回答来解决这个问题。关键是在前一步中清除表中的所有数据。这显然意味着根据您的结构复制数据并可能删除和重新创建外键以及检查约束等,但毕竟这是可行的。在此过程中,您在填补空白方面提供了帮助。

以上是关于“如果可能发生数据丢失,阻止增量部署”的粒度的主要内容,如果未能解决你的问题,请参考以下文章

地推解决HoL阻塞

数据仓库之粒度

同一张表如何计算不同粒度的数据

Mysql丢数据及主从数据不一致的场景

java中for多次嵌套循环丢数据,丢数据,丢数据,逻辑没问题!

如何检测网络数据丢包的现象?(网络行家进)