Tsql Transact-SQL迁移中出现错误时Flyway失败

Tsql Transact-SQL迁移中出现错误时Flyway失败,tsql,flyway,Tsql,Flyway,将Flyway与Microsoft SQL Server结合使用时,我们注意到了上所述的问题 基本上,这样的迁移脚本不会在另一部分失败时回滚成功的GO分隔的批: 开始交易 --创建一个包含两个可空列的表 创建表[dbo]。[t1]( [id][nvarchar](36)空, [姓名][nvarchar](36)空 ) --添加具有一个空列的行 插入[dbo].[t1]值(NEWID(),NULL) --将一列设置为不可为空 --由于上一次插入,此操作失败 ALTER TABLE[dbo].[t1

将Flyway与Microsoft SQL Server结合使用时,我们注意到了上所述的问题

基本上,这样的迁移脚本不会在另一部分失败时回滚成功的
GO
分隔的批:

开始交易
--创建一个包含两个可空列的表
创建表[dbo]。[t1](
[id][nvarchar](36)空,
[姓名][nvarchar](36)空
)
--添加具有一个空列的行
插入[dbo].[t1]值(NEWID(),NULL)
--将一列设置为不可为空
--由于上一次插入,此操作失败
ALTER TABLE[dbo].[t1]ALTER COLUMN[name][nvarchar](36)不为空
去
--创建一个表作为下一个操作,以便测试回滚是否正确发生
创建表[dbo]。[t2](
[id][nvarchar](36)不为空
)
去
提交事务
在上面的示例中,即使前面的
altertable
语句失败,仍将创建表
t2

关于相关问题,提出了以下方法(飞行路线范围外):

  • 多批处理脚本应该有一个单一的错误处理程序作用域,该作用域在出现错误时回滚事务,并在最后提交。在TSQL中,可以使用动态sql实现这一点

    • 动态SQL使得脚本很难阅读,这将非常不方便
  • 使用SQLCMD,您可以使用
    -b
    选项在出现错误时中止脚本

    • 这在flyway中可用吗?
  • 或者推出自己的脚本运行程序

    • flyway可能就是这样吗?是否有特定于飞道的配置来启用正确的故障排除错误?

  • 编辑:备选示例

    给定:简单数据库
    开始交易
    创建表[a](
    [a_id][nvarchar](36)不为空,
    [a_name][nvarchar](100)不为空
    );
    创建表[b](
    [b_id][nvarchar](36)不为空,
    [a_name][nvarchar](100)不为空
    );
    插入[a]值(NEWID(),'name-1');
    插入[b]值(NEWID(),'name-1'),(NEWID(),'name-2');
    提交事务
    
    迁移脚本1(失败,未执行)
    开始交易
    ALTER TABLE[b]添加[a_id][nvarchar](36)NULL;
    更新[b]从[a]设置[a\U id]=[a].[a\U id],其中[a].[a\U名称]=[b].[a\U名称];
    ALTER TABLE[b]ALTER COLUMN[a_id][nvarchar](36)不为空;
    ALTER TABLE[b]删除列[a_name];
    提交事务
    
    这将导致出现错误消息
    更新语句的列名“a\u id”无效。

    可能的解决方案:在语句之间引入
    GO

    迁移脚本2(使用GO:working for“happy case”,但在出现错误时仅进行部分回滚)
    开始交易
    将XACT_中止设置为ON
    去
    ALTER TABLE[b]添加[a_id][nvarchar](36)NULL;
    去
    更新[b]从[a]设置[a\U id]=[a].[a\U id],其中[a].[a\U名称]=[b].[a\U名称];
    去
    ALTER TABLE[b]ALTER COLUMN[a_id][nvarchar](36)不为空;
    去
    ALTER TABLE[b]删除列[a_name];
    去
    提交事务
    
    • 只要表
      [b]
      中的所有值在表
      [a]
      中都有匹配的条目,就可以执行所需的迁移
    • 在给定的示例中,情况并非如此。即,我们得到两个错误:
      • 应为:
        无法将值NULL插入到“test.dbo.b”表的“a_id”列中;列不允许空值。更新失败。
      • 意外:
        提交事务请求没有相应的开始事务。
      • 可怕的是:最后一个
        ALTER TABLE[b]DROP COLUMN[a_name]
        语句实际上已执行、提交且未回滚。
        也就是说,由于链接列丢失,以后无法修复此问题
    这种行为实际上独立于flyway,可以通过SSMS直接复制。

    编辑20201102——了解了更多关于这一点,并在很大程度上重写了它!到目前为止,我们已经在SSMS中进行了测试,也计划在Flyway中进行测试,并撰写了一篇博客文章。为了简化迁移,我相信如果愿意,您可以将@trancount检查/错误处理放在存储过程中,这也在我要测试的列表中

    修复中的成分 对于SQL Server中的错误处理和事务管理,有三件事可能非常有用:

    • 将XACT_ABORT设置为ON(默认情况下为off)。此设置“指定当Transact-SQL语句引发运行时错误时,SQL Server是否自动回滚当前事务”
    • 在您发送的每个批定界符后检查@@TRANCOUNT状态,并使用它“退出”RAISERROR/RETURN(如果需要)
    • Try/catch/throw——在这些示例中,我使用的是RAISERROR,如果您可以使用throw,Microsoft建议您使用throw(我认为可以使用SQL Server 2016+了)——
    处理原始示例代码 两个变化:

    • 将XACT_ABORT设置为ON
    • 在发送每个批定界符后对@TRANCOUNT执行检查,以查看是否应运行下一批。这里的关键是,如果发生错误,@@TRANCOUNT将为0。如果未发生错误,则为1。(注意:如果显式打开多个“嵌套”事务,则需要调整trancount检查,因为它可能高于1)
    在这种情况下,即使XACT_ABORT处于关闭状态,@@TRANCOUNT check子句也可以工作,但我相信您希望在其他情况下启用它。(需要阅读更多关于这方面的内容,但我还没有发现它的缺点。)

    开始交易;
    将XACT_ABORT设置为ON;
    去
    --创建一个包含两个可空列的表
    创建表[dbo]。[t1](
    [id][nvarchar](36)空,
    [姓名][nvarchar](36)空
    )
    --添加具有一个空列的行
    插入[dbo].[t1]值(NEWID(),NULL)
    --将一列设置为不可为空
    --由于上一次插入,此操作失败
    ALTER TABLE[dbo].[t1]ALTER COLUMN[name][nvarchar](36)不为空
    去
    如果@@TRANC