6

在迁移项目期间,我面临着 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. 有什么方法可以在“查询完成但出现错误”中找出错误的描述和原因。

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

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

4

2 回答 2

6

解决这个问题的最好方法是将 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列表拿到数据库中的一个表中,这样就可以利用数据库的力量进行后续的操作。

于 2013-02-09T19:42:34.387 回答
4

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

我不确定 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 次)运行此查询,直到不再报告更新。

于 2013-02-09T19:10:00.743 回答