![](/img/trans.png)
[英]Adding an autonumber to a SQLcolumn which has more than 15 million records
[英]How to update 2 new columns created in a table which has more than 250 million rows
我必须向具有超过2.5亿行的表中添加2个新列col1 char(1) NULL
和col2 char(1) NULL
。 而且我有一个值来更新两列1
为现有的2.5亿行。
然后我的SSIS包将每天以增量顺序更新表。 SSIS包将使用来自源表的任何内容填充这两列。
如何实现这一点,以便快速完成,因为我必须更新250M行?
谢啦
您没有说您正在使用的SQL Server版本。 从SQL Server 2012开始,在大多数情况下,添加带有默认值的新NOT NULL
列是瞬时的 :仅更改表元数据,并且不更新任何行。 感谢Martin Smith提供此信息。 所以在这个版本中,你最好放弃并重新创建列。
在以前的版本中,您可以尝试这样的事情:
WHILE 1 = 1 BEGIN
WITH T AS (
SELECT TOP (10000) *
FROM dbo.YourTable
WHERE
T.Col1 IS NULL
AND T.COl2 IS NULL
)
UPDATE T
SET
T.Col1 = '1',
T.Col2 = '1'
;
IF @@RowCount < 10000 BREAK; -- a trick to save one iteration most times
END;
这可能需要很长时间才能运行,但是它的好处是它将不会长时间在表上保持锁。 索引和通常行大小的确切组合也会影响它的执行效果。 要更新的行数的最佳位置永远不会是恒定的。 它可能是50,000或2,000。 我曾经在这样的分块操作中尝试过不同的计数,发现5,000或10,000通常非常接近最佳尺寸。
根据SQL Server的版本(2008及更高版本),上面的查询还可以从筛选索引中受益:
CREATE UNIQUE NONCLUSTERED INDEX IX_YourTable ON dbo.YourTable (ClusteredColumns)
WHERE Col1 IS NULL AND COl2 IS NULL;
完成后,删除索引。
请注意,如果您用默认值和NOT NULL
指定了两个新列,则它们将在创建列时添加值-然后可以删除默认值:
ALTER TABLE dbo.YourTable ADD Col1 char(1)
NOT NULL CONSTRAINT DF_YourTable_Col1 DEFAULT ('1');
与向末尾添加NULL
列不同,这可能会进行舔分,这可能需要花费大量时间,因此在您的250M行表上这可能不是一个选项。
更新要解决布莱恩的评论:
小批量进行10,000次的理由是,可以大大缓解更新的“开销”带来的负面影响。 是的,确实,这将是一项很多活动 - 但它不会长时间阻止,这就是这样一项活动的第一个影响性能的因素:阻塞很长一段时间。
我们对该查询的锁定潜力有很多了解:UPDATE EXCLUSIVE锁定,并且先前的点应将由此产生的任何有害影响降至最低。 如果还有其他我想念的锁定问题,请分享。
筛选后的索引会有所帮助,因为它将只允许读取索引的几页,然后再查找巨型表。 由于更新,为真,因此必须维护过滤的索引以删除更新的行,因为它们不再符合条件,这确实增加了更新的写入部分的成本。 这听起来很糟糕,直到你意识到上面批量UPDATE
的最大部分,没有某种索引, 每次都会进行表扫描 。 给定250M行,这需要与整个表的12,500次完整扫描相同的资源! 因此,我的建议是使用索引DOES,但它是手动遍历聚集索引的一种不错而又简便的捷径。
它们对具有大量写操作的表不利的“索引基本定律”在这里不成立。 您正在考虑正常的OLTP访问模式,其中可以使用搜索找到正在更新的行,然后对于写入,表上的每个附加索引确实会创建之前不存在的开销。 将此与我上一点的解释进行比较。 即使过滤后的索引使UPDATE
部分占用每行I / O的5倍(可疑),这仍然会使I / O减少超过2500次! 。
评估更新对性能的影响很重要,尤其是在表非常繁忙且不断被使用的情况下。 如您所建议的那样,如果需要,将其安排在下班时间(如果有的话)是基本的意义。
我建议的一个潜在弱点是,在SQL 2008及更低版本中,添加过滤后的索引可能需要很长时间 - 尽管可能不是,因为它是一个非常窄的索引并且将以群集顺序编写(可能只需一次扫描) !)。 因此,如果创建时间过长,则有另一种选择:手动遍历聚集索引。 这可能看起来像这样:
DECLARE @ClusteredID int = 0; --assume clustered index is a single int column
DECLARE @Updated TABLE (
ClusteredID int NOT NULL
);
WHILE 1 = 1 BEGIN
WITH T AS (
SELECT TOP (10000) *
FROM dbo.YourTable
WHERE ClusteredID > @ClusteredID -- the "walking" part
ORDER BY ClusteredID -- also crucial for "walking"
)
UPDATE T
SET
T.Col1 = '1',
T.Col2 = '1'
OUTPUT Inserted.ClusteredID INTO @Updated
;
IF @@RowCount = 0 BREAK;
SELECT @ClusteredID = Max(ClusteredID)
FROM @Updated
;
DELETE @Updated;
END;
你去:没有索引,一直寻找,只有一个有效的整个表扫描(处理表变量的一点点开销)。 如果ClusteredID
列密集,您甚至可以省去表变量,并在每个循环结束时手动添加10,000。
您提供了一个更新,您的聚簇索引中有5列。 这是一个更新的脚本,显示了您可能如何适应它:
DECLARE -- Five random data types seeded with guaranteed low values
@Clustered1 int = 0,
@Clustered2 int = 0,
@Clustered3 varchar(10) = '',
@Clustered4 datetime = '19000101',
@Clustered5 int = 0
;
DECLARE @Updated TABLE (
Clustered1 int,
Clustered2 int,
Clustered3 varchar(10),
Clustered4 datetime,
Clustered5 int
);
WHILE 1 = 1 BEGIN
WITH T AS (
SELECT TOP (10000) *
FROM dbo.YourTable
WHERE
Clustered1 > @Clustered1
OR (
Clustered1 = @Clustered1
AND (
Clustered2 > @Clustered2
OR (
Clustered2 = @Clustered2
AND (
Clustered3 > @Clustered3
OR (
Clustered3 = @Clustered3
AND (
Clustered4 > @Clustered4
OR (
Clustered4 = @Clustered4
AND Clustered5 > @Clustered5
)
)
)
)
)
)
)
ORDER BY
Clustered1, -- also crucial for "walking"
Clustered2,
Clustered3,
Clustered4,
Clustered5
)
UPDATE T
SET
T.Col1 = '1',
T.Col2 = '1'
OUTPUT
Inserted.Clustered1,
Inserted.Clustered2,
Inserted.Clustered3,
Inserted.Clustered4,
Inserted.Clustered5
INTO @Updated
;
IF @@RowCount < 10000 BREAK;
SELECT TOP (1)
@Clustered1 = Clustered1
@Clustered2 = Clustered2,
@Clustered3 = Clustered3,
@Clustered4 = Clustered4,
@Clustered5 = Clustered5
FROM @Updated
ORDER BY
Clustered1,
Clustered2,
Clustered3,
Clustered4,
Clustered5
;
DELETE @Updated;
END;
如果您发现一种特定的方式行不通,请尝试另一种方式。 在更深层次上理解数据库系统将带来更好的想法和卓越的解决方案。 我所知道的深度嵌套的WHERE
条件是一个谎言。 你可以尝试对规模以下以及-这工作完全一样,但更难理解,所以我真的不能推荐它,即使加上额外的列是很容易的。
WITH T AS (
SELECT TOP (10000) *
FROM
dbo.YourTable T
WHERE
122 <=
CASE WHEN Clustered1 > @Clustered1 THEN 172 WHEN Clustered1 = @Clustered1 THEN 81 ELSE 0 END
+ CASE WHEN Clustered2 > @Clustered2 THEN 54 WHEN Clustered1 = @Clustered2 THEN 27 ELSE 0 END
+ CASE WHEN Clustered3 > @Clustered3 THEN 18 WHEN Clustered3 = @Clustered3 THEN 9 ELSE 0 END
+ CASE WHEN Clustered4 > @Clustered4 THEN 6 WHEN Clustered4 = @Clustered4 THEN 3 ELSE 0 END
+ CASE WHEN Clustered5 > @Clustered5 THEN 2 WHEN Clustered5 = @Clustered5 THEN 1 ELSE 0 END
ORDER BY
Clustered1, -- also crucial for "walking"
Clustered2,
Clustered3,
Clustered4,
Clustered5
)
UPDATE T
SET
T.Col1 = '1',
T.Col2 = '1'
OUTPUT
Inserted.Clustered1,
Inserted.Clustered2,
Inserted.Clustered3,
Inserted.Clustered4,
Inserted.Clustered5
INTO @Updated
;
我有很多次使用这种精确的“小批量步行聚集索引”策略对巨型表进行更新,对生产数据库没有任何不良影响。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.