【问题标题】:Updating 4 million records in SQL server using list of record-ids as input使用记录 ID 列表作为输入更新 SQL Server 中的 400 万条记录
【发布时间】:2013-02-09 17:11:18
【问题描述】:

在迁移项目期间,我面临着 SQL Server 中 400 万条记录的更新。

更新非常简单;需要将布尔字段设置为 true/1,并且我的输入是必须填写该字段的所有 id 的列表。(每行一个 id)

当涉及到这种大小的 sql 任务时,我并不完全是专家,所以我开始尝试 1 个包含“WHERE xxx IN ( {list of ids, separated by comma} )”的 UPDATE 语句。首先,我用一百万条记录进行了尝试。在测试服务器上的一个小数据集上,这就像一个魅力,但在生产环境中,这给出了一个错误。因此,我将 id 列表的长度缩短了几次,但无济于事。

接下来我尝试将列表中的每个 id 转换为 UPDATE 语句(“UPDATE yyy SET booleanfield = 1 WHERE id = '{id}'”)。某处,我读到每隔 x 行有一个 GO 很好,所以我每 100 行插入一个 GO(使用从 unix 移植的优秀的 'sed' 工具)。

因此,我将 400 万条更新语句列表分成 250.000 条,将它们保存为 sql 文件,然后开始将第一个语句加载并运行到 SQL Server Management Studio (2008)。请注意,我也尝试过 SQLCMD.exe,但令我惊讶的是,它的运行速度比 SQL Studio 慢了大约 10-20 倍。

大约需要 1.5 小时才能完成并导致“查询完成但出现错误”。但是,消息列表包含一个很好的列表,其中包含“1 行受影响”和“0 行受影响”,后者用于未找到 id 时。

接下来,我使用 COUNT(*) 检查了表中更新记录的数量,发现更新语句的数量和更新的记录数量之间存在几千条记录的差异。

然后我认为这可能是由于不存在的记录,但是当我在输出中减去“0行受影响”的数量时,出现了 895 条记录的神秘差距。

我的问题:

  1. 有什么方法可以在“Query completed with errors”中找到错误的描述和原因。

  2. 895条记录的神秘差距如何解释?

  3. 有什么更好或最好的方法来进行此更新? (因为我开始认为我正在做的事情可能非常低效和/或容易出错)

【问题讨论】:

  • Gap 似乎与您有重复 id 的事实相关(这肯定有 400 万条记录的可能性)或该 id 不存在于表中(另一种可能性)。跨度>
  • 您是如何将这 4 百万条记录转换为 csv 字符串的?
  • 表一共有多少条记录?我在想,如果刚刚超过 400 万,最好删除该列,将其添加为默认值 1,然后将其他行更新为 0 或 null。
  • @Chris...重复的 ids,我之前没想到,谢谢!会检查这个
  • @Kaf,实际上是 4 个文件,每个文件有 100 万条记录(尽管是 1 列),由从我们的“旧”系统中提取它们的自定义工具生成

标签: sql sql-server sql-server-2008 tsql data-migration


【解决方案1】:

解决这个问题的最佳方法是将 400 万条记录插入到一​​个表中。事实上,您可以通过“批量插入”到视图中,将它们放入带有标识列的表中。

create table TheIds (rownum int identity(1,1), id int);

create view v_TheIds (select id from TheIds);

bulk insert into v_TheIds . . .

有了数据库中的所有数据,您现在有更多选择。尝试更新:

update t
    set booleanfield = 1
    where exists (select 1 from TheIds where TheIds.id = t.id)

您还应该在TheIds(id) 上创建一个索引。

这是一个大型更新,所有更新都作为一个事务执行。这可能会影响性能并开始填满日志。您可以使用rownum 列将其分解为更小的事务:

update t
    set booleanfield = 1
    where exists (select 1 from TheIds where TheIds.id = t.id and TheIds.rownum < 1000)

这里的exists子句相当于left outer join。主要区别在于这种相关子查询语法应该适用于其他数据库,其中更新连接是特定于数据库的。

使用rownum 列,您可以选择任意多的行进行更新。因此,如果整体更新太大,您可以将更新置于循环中:

where rownum < 100000
where rownum between 100000 and 199999
where rownum between 200000 and 299999

等等。您不必这样做,但如果您出于某种原因想要批量更新,您可以这样做。

关键思想是将id的列表放入数据库中的一个表中,这样你就可以利用数据库的力量进行后续的操作。

【讨论】:

  • 这听起来很棒,看起来也很简单,这在大多数情况下是最好的 :) 非常感谢 Gordon,将尝试一下并将结果发布在这里。
  • sql server 将 400 万个 id 插入到表中的速度令人难以置信……我很惊讶。到目前为止,一切都很好。我不确定“从 TheIds 中选择 1,其中 TheIds.id = t.id 和 TheIds.rownum
  • 我使用行号“技巧”将 400 万条记录分成 8 个部分,最终在不到一个小时的时间内更新了所有 400 万条记录。令人惊叹的。谢谢戈登!
【解决方案2】:

警告:我无法对其进行测试,而且我没有可以容纳这么多数据的“游乐场数据库”。

我不确定 1. 和 2. 但是对于 3. 你最好将更新的限制留给数据库:

UPDATE TOP(100000) yyy
SET booleanfield = 1
WHERE booleanfield = 0
GO

虽然文档说要“随机选择”一些具有 TOP 限制的条目 - 我希望它只从具有 booleanfield = 0 的条目中这样做。重复运行该查询,直到不再报告更新。

如果上述方法不起作用,另一种选择是直接从数据库中选择受影响的 id ... 这看起来很奇怪,我也没有测试过,但我希望它有效:

UPDATE yyy
SET booleanfield = 1
FROM (SELECT TOP 100000 id FROM yyy WHERE booleanfield = 0 ORDER BY id ASC) AS xxxx
WHERE yyy.id = xxxx.id;
GO

(我假设您在此处的表中有一个唯一键 id)。 多次(大约 40 次)运行此查询,直到不再报告更新。

【讨论】:

  • Clemens,我不知道 TOP() 技巧。很高兴知道。谢谢
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-04-12
  • 2012-05-30
  • 1970-01-01
  • 2013-12-06
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多