SQL Server ALTER 字段 NOT NULL 需要很长时间

发布于 2024-07-27 00:21:40 字数 222 浏览 10 评论 0原文

我想更改大约有 400 万条记录的表中的字段。 我确保所有这些字段值都不为空,并且想要将此字段更改为非空

ALTER TABLE dbo.MyTable
ALTER COLUMN myColumn int NOT NULL

......似乎需要永远进行此更新。 有什么方法可以加快速度,还是我只能在下班时间过夜?

这也可能导致表锁吗?

I want to alter a field from a table which has about 4 million records. I ensured that all of these fields values are NOT NULL and want to ALTER this field to NOT NULL

ALTER TABLE dbo.MyTable
ALTER COLUMN myColumn int NOT NULL

... seems to take forever to do this update. Any ways to speed it up or am I stuck just doing it overnight during off-hours?

Also could this cause a table lock?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

何其悲哀 2024-08-03 00:21:40

您可以更改字段并使其不为空,而无需检查字段。 如果您真的担心不在下班时间执行此操作,您可以向该字段添加一个约束,以检查以确保它不为空。 这将允许您使用不检查选项,而不是让它检查 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!

实际上,您有两个选项(添加了第三个选项,请参阅编辑):

  1. 使用约束将阻止更新任何新行并保持原始行不变。
  2. 将 null 行更新为其他内容,然后应用 not null alter 选项。 这确实应该在非工作时间运行,除非您不介意进程被锁定在表之外。

根据您的具体情况,任一选项可能更适合您。 我不会选择这个选项,因为你必须在下班时间运行它。 从长远来看,与你为了节省几个小时而走捷径可能面临的麻烦相比,你在半夜更新的时间是值得的。

话虽如此,如果您打算选择第二种,您可以最大限度地减少在下班时间所做的工作量。 由于您必须确保在更改列之前将行更新为不为空,因此您可以编写一个游标来慢慢地(相对于一次完成所有操作)

  1. 遍历每一行,
  2. 检查它是否为空,并
  3. 适当地更新它。
    这将需要一段时间,但它不会锁定整个表并阻止其他程序访问它。 (不要忘记 with(rowlock) 表提示!)

编辑:我只是想到了第三种选择:
您可以创建一个包含适当列的新表,然后将原始表中的数据导出到新表。 完成此操作后,您可以删除原始表并将新表的名称更改为旧表。 为此,您必须禁用对原始依赖项的依赖项,并在完成后将它们重新设置到新依赖项上,但此过程将大大减少您在非工作时间必须完成的工作量。 这与您通过 Management Studio 对表进行列排序更改时 sql Server 使用的方法相同。 对于这种方法,我会分块进行插入,以确保不会对系统造成撤消压力并阻止其他人访问它。 然后在下班时间,您可以删除原来的,重命名第二个,并应用依赖项等。您仍然会有一些下班时间的工作,但与其他方法相比,它会微不足道。

链接到使用 sp_rename

You can alter a field and make it not null without it checking the fields. If you are really concerned about not doing it off hours you can add a constraint to the field which checks to make sure it isn't null instead. This will allow you to use the with no check option, and not have it check each of the 4 million rows to see if it updates.

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!

Really you have two options (added a third one see edit):

  1. Use the constraint which will prevent any new rows from being updated and leave the original ones unaltered.
  2. Update the rows which are null to something else and then apply the not null alter option. This really should be run in off hours, unless you don't mind processes being locked out of the table.

Depending on your specific scenario, either option might be better for you. I wouldn't pick the option because you have to run it in off hours though. In the long run, the time you spend updating in the middle of the night will be well spent compared the headaches you'll possibly face by taking a short cut to save a couple of hours.

This all being said, if you are going to go with option two you can minimize the amount of work you do in off hours. Since you have to make sure you update the rows to not null before altering the column, you can write a cursor to slowly (relative to doing it all at once)

  1. Go through each row
  2. Check to see if it is null
  3. Update it appropriately.
    This will take a good while, but it won't lock the whole table block other programs from accessing it. (Don't forget the with(rowlock) table hint!)

EDIT: I just thought of a third option:
You can create a new table with the appropriate columns, and then export the data from the original table to the new one. When this is done, you can then drop the original table and change the name of the new one to be the old one. To do this you'll have to disable the dependencies on the original and set them back up on the new one when you are done, but this process will greatly reduce the amount of work you have to do in the off hours. This is the same approach that sql server uses when you make column ordering changes to tables through the management studio. For this approach, I would do the insert in chunks to make sure that you don't cause undo stress on the system and stop others from accessing it. Then on the off hours, you can drop the original, rename the second, and apply dependencies etc. You'll still have some off hours work, but it will be minuscule compared to the other approach.

Link to using sp_rename.

鸠魁 2024-08-03 00:21:40

据我所知,“快速”(*)执行此操作的唯一方法是

  • 创建一个“影子”表,该表具有所需的布局
  • ,向源表添加触发器,以便任何插入/更新/删除操作都复制到影子-table(注意捕获可能弹出的任何 NULL!)
  • 将所有数据从源复制到影子表,可能以较小的块形式(确保您可以通过触发器处理已复制的数据,确保数据将适合新结构(ISNULL(?)!)
  • 用脚本输出来自/到其他表的所有依赖项
  • 当所有操作完成后, ,在显式事务中执行以下操作:
    • 在源表上获取独占表锁,在影子表上获取独占表锁
    • 运行脚本以删除对源表的依赖项
    • 将源表重命名为其他名称(例如后缀 _old)
    • 将影子表重命名为源表的原始名称
    • 运行脚本以再次创建所有依赖项

您可能希望在事务之外执行最后一步,因为这可能需要相当多的时间,具体取决于引用此表的表的数量和大小,第一步获胜根本不需要太多时间

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

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

(*:快速归结为:尽可能减少停机时间)

The only way to do this "quickly" (*) that I know of is by

  • creating a 'shadow' table which has the required layout
  • adding a trigger to the source-table so any insert/update/delete operations are copied to the shadow-table (mind to catch any NULL's that might popup!)
  • copy all the data from the source to the shadow-table, potentially in smallish chunks (make sure you can handle the already copied data by the trigger(s), make sure the data will fit in the new structure (ISNULL(?) !)
  • script out all dependencies from / to other tables
  • when all is done, do the following inside an explicit transaction :
    • get an exclusive table lock on the source-table and one on the shadowtable
    • run the scripts to drop dependencies to the source-table
    • rename the source-table to something else (eg suffix _old)
    • rename the shadow table to the source-table's original name
    • run the scripts to create all the dependencies again

You might want to do the last step outside of the transaction as it might take quite a bit of time depending on the amount and size of tables referencing this table, the first steps won't take much time at all

As always, it's probably best to do a test run on a test-server first =)

PS: please do not be tempted to recreate the FK's with NOCHECK, it renders them futile as the optimizer will not trust them nor consider them when building a query plan.

(*: where quickly comes down to : with the least possible downtime)

面如桃花 2024-08-03 00:21:40

抱歉让您灰心,但是:

  • 有什么方法可以加快速度:不,如果您想更改表结构本身
  • ,或者我是否只能在下班时间过夜做这件事,那就不行? 是的,这可能是最好的,正如 @HLGEM 指出的那样,
  • 这也可能导致表锁定吗? 是的,

与您没有直接关系(因为它是关于从 NOT NULL 到 NULL),但有关此主题的有趣阅读:http://beyondrelational.com/blogs/sankarreddy/archive/2011/04/05/ is-alter-table-alter-column-not-null-to-null-always-expense.aspx

最后是一些古老的历史 - 在 2005 年论坛上的一个类似问题上,@ 提出了相同的建议Kevin 在上面提供了 - 使用约束而不是使列本身不可为空: http:// /www.sqlteam.com/Forums/topic.asp?TOPIC_ID=50671

Sorry for the discouragement, but:

  • Any ways to speed it up: No, not if you want to change the table structure itself
  • or am I stuck just doing it overnight during off-hours? Yes, and that's probably for the best, as @HLGEM pointed out
  • Also could this cause a table lock? Yes

Not directly relevant to you (because it's about going from NOT NULL to NULL), but interesting read on this topic: http://beyondrelational.com/blogs/sankarreddy/archive/2011/04/05/is-alter-table-alter-column-not-null-to-null-always-expensive.aspx

And finally some ancient history - on an equivalent question in a forum in 2005, the same suggestion was made as @Kevin offered above - using a constraint insteadof making the column itself non-nullable: http://www.sqlteam.com/Forums/topic.asp?TOPIC_ID=50671

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文