【问题标题】:Conditionally insert into table if column exists如果列存在,则有条件地插入表中
【发布时间】:2018-01-16 15:54:56
【问题描述】:

我正在尝试编写一个幂等数据库迁移脚本,除其他外,它需要对一些数据进行洗牌。稍后在脚本中,我从中选择的一列被删除(迁移的目的是将数据从该列移动到新位置),所以我有这样的东西(由 EF Core 生成):

IF NOT EXISTS (SELECT * FROM [__EFMigrationsHistory] WHERE [MigrationId] = N'AName')
BEGIN
     INSERT INTO Foos (A, B)
     SELECT OldA, OldB FROM Bars
END

-- a little later in the script:

IF NOT EXISTS (SELECT * FROM [__EFMigrationsHistory] WHERE [MigrationId] = N'AnotherName')
BEGIN
      ALTER TABLE [Bars] DROP COLUMN [OldB];
END

但是,这并不像我希望的那样是幂等的;我第二次运行脚本时,它在第一个 INSERT 语句中失败并出现错误,因为 Bars 上不再存在 OldB 列。

然而,如果OldB 已被删除,上述保护子句总是为假,因为在删除OldB 的同时,我们还会将该行插入到迁移历史记录中(并且是的,我现在也检查过这也是真的;该行存在)。所以INSERT 应该永远在它关心的所有列都存在的情况下运行。

我怎样才能编写一个像上面那样的幂等INSERT,它在实际运行之前不会验证所有列的存在?

【问题讨论】:

  • 我打算建议使用动态 SQL。

标签: sql-server tsql idempotent


【解决方案1】:

您可以检查所有列是否存在:

IF NOT EXISTS (SELECT * FROM [__EFMigrationsHistory] 
               WHERE [MigrationId] = N'AName')
BEGIN
     IF (SELECT COUNT(*) 
        FROM sys.columns 
        WHERE [object_id] = OBJECT_ID('Bars')
          AND name IN ('OldA', 'OldB')) = 2
     BEGIN
          EXEC('INSERT INTO Foos (A, B)
          SELECT OldA, OldB FROM Bars');
     END
END

-- a little later in the script:

IF NOT EXISTS (SELECT * FROM [__EFMigrationsHistory] 
               WHERE [MigrationId] = N'AnotherName')
BEGIN
      EXEC('ALTER TABLE [Bars] DROP COLUMN [OldB]');
END

【讨论】:

  • 这仍然会产生相同的错误;似乎我在 INSERT 周围有一个保护条款并不重要,这将确保它永远不会被执行,它仍然被认为是无效的。例如,用IF 0 = 1 包装INSERT 仍然会产生错误。
  • @TomasLycken 请检查我的编辑。用动态 SQL 包装它可以解决名称解析问题,并且检查将在运行时完成。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-11-23
  • 1970-01-01
  • 2018-05-19
  • 2018-09-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多