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

时间:2013-08-12 09:29:11

标签: sql-server database-project sql-server-data-tools

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

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

美国药膏中有几种苍蝇:

  1. 该列中包含数据
  2. 数据库已发布到 数以百计的分布式客户端,可能需要几个月的时间 改变为所有客户涟漪
  3. 在填充列时,除非我们更改“可能发生数据丢失时阻止增量部署”选项,否则发布将失败。这个选项是在数据库级别,而不是表级,因此由于客户端的分布式特性,我们必须在所有数据库更新之前关闭“数据丢失”选项数月,并将其重新打开一旦所有客户都更新了(我们的数据库有我们构建的版本号)。

    您可能认为我们可以使用预部署脚本(例如

    )解决此问题
    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
    

    但除非我们关闭“数据丢失可能发生”选项,否则这种情况再次失败。

    我只是对其他人在这种情况下所做的事情感兴趣,因为我想要具有目前看来不可能的粒度。

2 个答案:

答案 0 :(得分:5)

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

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

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

答案 1 :(得分:3)

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

  1. 架构比较
  2. 用于架构差异的脚本生成
  3. 执行PreDeployment
  4. 超出生成的脚本
  5. 执行PostDeployment。
  6. 当然,架构差异被识别为#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部署之外(和之前)的问题,我们还没有找到解决方法。

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

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

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