【问题标题】:Alter Column to Not Null where System Versioned column was nullable将列更改为非空,其中系统版本化列可以为空
【发布时间】:2018-07-31 12:01:58
【问题描述】:

我正在使用 SQL Server 和系统版本化(临时)表。在我的主表中,我有一个 INT 列,该列当前允许 NULL。我想将其更新为不允许空值,但表的系统/历史副本允许空值。

我运行这个语句:

ALTER TABLE dbo.MyTable 
    ALTER COLUMN MyInt INT NOT NULL;

我得到这个错误:

无法将值 NULL 插入到列“MyInt”、表“mydb.dbo.MyTable_History”中;列不允许空值。更新失败。

我已经使用这个脚本创建了系统版本表:

ALTER TABLE dbo.MyTable
    ADD 
        ValidFrom DATETIME2 (2) GENERATED ALWAYS AS ROW START HIDDEN CONSTRAINT DFMyTable_ValidFrom DEFAULT DATEADD(SECOND, -1, SYSUTCDATETIME()),
        ValidTo DATETIME2 (2) GENERATED ALWAYS AS ROW END HIDDEN CONSTRAINT DFMyTable_ValidTo DEFAULT '9999.12.31 23:59:59.99',
        PERIOD FOR SYSTEM_TIME (ValidFrom, ValidTo);

ALTER TABLE dbo.MyTable 
    SET (SYSTEM_VERSIONING = ON (HISTORY_TABLE = dbo.MyTable_History));
GO

在这种情况下,是否有其他方法可以使主表的列不可为空?我想我可以(也许)使用任意垃圾值手动更新现有的系统版本化空值,但似乎这种情况应该支持临时表。

【问题讨论】:

  • 您可以尝试ALTER TABLE ... WITH NOCHECK ADD CONSTRAINT CK_MyInt_NotNull CHECK (MyInt IS NOT NULL) 在事后为新行添加约束,而无需检查现有行。 (免责声明:未经测试。)这有点不确定,因为它实际上与标记列 NOT NULL 不同,它会在重新导入数据时中断,但这也是您的方案失败的原因:版本化表版本数据,但是不是结构。任何成功的结构更改都会传播到历史记录表,即使它有效,也不会完全忠实于历史记录。
  • @JeroenMostert 在我的临时审计系统中,我将审计表保留为可为空,并将其留给主表以强制执行约束。希望这里可能有一种方法可以做同样的事情,因为似乎所有其他解决方法都不理想。感谢您的额外想法,我自己没想到。
  • 临时表真的很酷,只要你的用例完全匹配它们的实现,如果它们略有不同,真的很令人沮丧。这是希望未来的版本(不是双关语)给我们历史查询善良,而不与引擎的实现非常密切的联系。
  • 当你已经有空值时你不能这样做。更新它们或删除表。

标签: sql-server temporal-tables


【解决方案1】:

我也看过这个,看来您必须将系统版本列中的 NULL 值更新为某个值。

ALTER TABLE dbo.MyTable
    SET (SYSTEM_VERSIONING = OFF)
GO
UPDATE dbo.MyTable_History
    SET MyInt = 0 WHERE MyInt IS NULL --Update to default value
UPDATE dbo.MyTable
    SET MyInt = 0 WHERE MyInt IS NULL --Update to default value
ALTER TABLE dbo.MyTable
    ALTER COLUMN MyInt INT NOT NULL
ALTER TABLE dbo.MyTable_History
    ALTER COLUMN MyInt INT NOT NULL
GO
ALTER TABLE dbo.MyTable 
    SET (SYSTEM_VERSIONING = ON (HISTORY_TABLE = dbo.MyTable_History));
GO

【讨论】:

  • 我现在远远超出了要求,但感谢您的回答。当问题出现在我的办公桌上时,我没有想到这一点。
【解决方案2】:

我在尝试添加新的非空列时遇到了这个问题。我最初试图将列创建为可为空,更新所有值,然后将其设置为不可为空:

ALTER TABLE dbo.MyTable 
    ADD COLUMN MyInt INT NULL;

GO

UPDATE dbo.MyTable
    SET MyInt = 0;

GO

ALTER TABLE dbo.MyTable 
    ALTER COLUMN MyInt INT NOT NULL;

但我设法通过使用临时默认约束来解决它:

ALTER TABLE dbo.MyTable 
    ADD COLUMN MyInt INT NOT NULL CONSTRAINT DF_MyTable_MyInt DEFAULT 0;

ALTER TABLE dbo.MyTable
    DROP CONSTRAINT DF_MyTable_MyInt;

【讨论】:

    【解决方案3】:

    您不能更新系统版本控制表上的行或更改列。

    您可以在关闭系统版本控制之前对这两个表进行更改,然后再重新打开它,或者创建一个新的系统版本控制表并跨数据复制。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-04-03
      • 2023-04-03
      • 1970-01-01
      • 2017-12-22
      • 2014-08-16
      • 2013-09-10
      • 2011-08-23
      • 1970-01-01
      相关资源
      最近更新 更多