7

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

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

美中不足的是:

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

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

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

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

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

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

4

2 回答 2

6

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

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

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

于 2014-10-22T00:51:24.680 回答
4

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

  1. 架构比较
  2. 模式差异的脚本生成
  3. 执行预部署
  4. 执行生成的脚本
  5. 执行部署后。

因此,当然,模式差异被识别为#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,它只是暗示一个想法的伪代码!

于 2014-10-11T02:24:46.007 回答