SQL Server ALTER字段NOT NULL将永远使用

SQL Server ALTER字段NOT NULL将永远使用,sql,sql-server,nullable,blocking,ddl,Sql,Sql Server,Nullable,Blocking,Ddl,我想从一个有大约400万条记录的表中修改一个字段。我确保所有这些字段值都不为NULL,并希望将此字段更改为不为NULL ALTER TABLE dbo.MyTable ALTER COLUMN myColumn int NOT NULL 。。。似乎要花很长时间才能完成此更新。有什么方法可以加快速度,还是我只能在休息时间通宵做 这也会导致表锁定吗?您可以在不检查字段的情况下更改字段并使其不为空。如果您真的担心在非工作时间不这样做,您可以向字段添加一个约束,该约束将检查以确保它不是空的。这将允许您

我想从一个有大约400万条记录的表中修改一个字段。我确保所有这些字段值都不为NULL,并希望将此字段更改为不为NULL

ALTER TABLE dbo.MyTable
ALTER COLUMN myColumn int NOT NULL
。。。似乎要花很长时间才能完成此更新。有什么方法可以加快速度,还是我只能在休息时间通宵做


这也会导致表锁定吗?

您可以在不检查字段的情况下更改字段并使其不为空。如果您真的担心在非工作时间不这样做,您可以向字段添加一个约束,该约束将检查以确保它不是空的。这将允许您使用with no check选项,而不是让它检查400万行中的每一行以查看它是否更新

CREATE TABLE Test
(
    T0 INT Not NULL,
    T1 INT NUll 
)

INSERT INTO Test VALUES(1, NULL) -- Works!

ALTER TABLE Test
    WITH NOCHECK
        ADD CONSTRAINT N_null_test CHECK (T1 IS NOT NULL)

    ALTER COLUMN T1 int NOT NULL 

INSERT INTO Test VALUES(1, NULL) -- Doesn't work now!
实际上,您有两个选项(添加了第三个选项,请参见编辑):

  • 使用该约束将防止更新任何新行,并保持原始行不变
  • 将空的行更新为其他行,然后应用NOTNULL alter选项。这确实应该在非工作时间运行,除非您不介意将进程锁定在表之外
  • 根据您的具体情况,任何一种选择都可能更适合您。我不会选择这个选项,因为你必须在非工作时间运行它。从长远来看,你在午夜进行更新的时间会比你可能要面对的头痛要花上一段时间来节省几个小时。 总而言之,如果你打算选择第二种方法,你可以尽量减少你在非工作时间的工作量。由于必须确保在更改列之前将行更新为NOTNULL,因此可以缓慢地将光标写入(相对于一次完成所有操作)

  • 检查每一行
  • 检查它是否为空
  • 适当地更新它。 这需要一段时间,但不会锁定整个表,也不会阻止其他程序访问它。(不要忘记表格提示!)
  • 编辑:我刚刚想到了第三个选项: 可以使用适当的列创建新表,然后将数据从原始表导出到新表。完成后,您可以删除原始表并将新表的名称更改为旧表。要做到这一点,您必须禁用对原始的依赖项,并在完成后将它们重新设置到新的依赖项上,但此过程将大大减少您在非工作时间必须完成的工作量。这与SQLServer在通过ManagementStudio对表进行列排序更改时使用的方法相同。对于这种方法,我将进行分块插入,以确保不会对系统造成撤消压力并阻止其他人访问它。然后在非工作时间,您可以删除原始的,重命名第二个,并应用依赖项等。您仍然会有一些非工作时间的工作,但与其他方法相比,这将是微不足道的


    链接到使用。

    很抱歉让您失望,但是:

    • 任何加速的方法:不,如果您想更改表结构本身,就不要
    • 还是我在休息时间整晚都在做?是的,正如@HLGEM所指出的,这可能是最好的
    • 这也会导致表锁定吗?对
    与您没有直接关系(因为它是关于从非空变为空的),但阅读本主题很有趣:

    最后,一些古代历史——在2005年的一个论坛上,有人提出了与上面@Kevin相同的建议——使用约束,而不是使专栏本身不可为空:

    我所知道的唯一“快速”(*)实现这一点的方法是

    • 创建具有所需布局的“阴影”表
    • 向源表添加触发器,以便将任何插入/更新/删除操作复制到阴影表(注意捕获可能弹出的任何空值!)
    • 将所有数据从源复制到卷影表,可能是以较小的块(确保可以通过触发器处理已复制的数据,确保数据适合新结构(ISNULL(?)!)
    • 用脚本写出其他表之间的所有依赖项
    • 完成所有操作后,在显式事务中执行以下操作:
      • 获取源表上的独占表锁和shadowtable上的独占表锁
      • 运行脚本将依赖项删除到源表
      • 将源表重命名为其他名称(例如后缀_old)
      • 将阴影表重命名为源表的原始名称
      • 再次运行脚本以创建所有依赖项
    您可能希望在事务之外执行最后一步,因为根据引用此表的表的数量和大小,这可能需要相当多的时间,第一步根本不会花费太多时间

    和往常一样,最好先在测试服务器上进行测试运行=)

    PS:请不要尝试用NOCHECK重新创建FK,这会使它们无效,因为优化器在构建查询计划时不信任它们,也不考虑它们。


    (*:where quick归结为:以尽可能少的停机时间)

    就我个人而言,除了非高峰时间,我不会在任何时候更改现有大型表中的表结构。即使它相当快,它也会导致用户在您进行更改时在执行操作的过程中出现问题。任何重大更改最好也在单用户模式下完成。当用户什么都做不了时(当然是在非高峰时间提前宣布),最好有一个停电的计划维护期,而不是那些在做错误的人中间有不愉快的用户。你在FK约束中所涉及的列是变化的吗?从Prror中的快速测试中,它得到了一个<代码> SC-M < /Cord>锁定在表上。然后,它必须读取每一页,以确定所有行都有效