0

我有一个MyTable包含这样数据的表:

[Production],[Region Country],[CustID]
computer,US,123
phone,CA,321
shirt,NZ,111

我想要做的是将 2 个字母的国家代码 (ISO) 更改为该表中所有记录(300 万条记录)的完整国家名称。

我有一张表Countries,其中包含所有 2 个字母的国家/地区代码及其“全名”。所以我可以查看此表进行更新。

CREATE TABLE [dbo].[Countries](
[NumericalCode] [char](3) NOT NULL,
[CountryNameLowerCase] [nvarchar](50) NOT NULL,
[CountryNameUpperCase] [nvarchar](50) NOT NULL,
[ISO_ALPHA3] [char](3) NOT NULL,
[ISO_ALPHA2] [char](2) NOT NULL
) ON [PRIMARY]

因为US它会有这些数据:

840,United States of America,UNITED STATES,USA,US

最终结果将是一个查看 MyTable 记录并Region Country根据其在Countries表中的全名(列CountryNameLowerCase)进行更新的过程。

最好的方法是什么(性能方面)?

4

1 回答 1

3

UPDATE简单的方法是使用带有 a的单个蛮力更新JOIN

UPDATE m
  SET [Region Country] = c.CountryNameLowerCase 
  FROM dbo.MyTable AS m
  INNER JOIN dbo.Countries AS c
  ON m.[Region Country] = c.ISO_ALPHA2;

现在,这样的更新可能会生成大量的日志,以及当您将更多信息压缩到每一行时的页面拆分(如果您使用数据压缩,还要考虑开销)。在某些情况下,分批执行更新可能会更好,例如,一种方法是针对每个国家/地区组合使用光标(如果您的国家/地区分布相对均匀,这将效果最佳,但如果 90%数据是US):

DECLARE @old NVARCHAR(255), @new NVARCHAR(255); -- use the right type here

DECLARE c CURSOR LOCAL FAST_FORWARD
  FOR SELECT m.[Region Country], c.CountryNameLowerCase
    FROM dbo.MyTable AS m
    INNER JOIN dbo.Countries AS c
    ON m.[Region Country] = c.ISO_ALPHA2
    GROUP BY m.[Region Country], c.CountryNameLowerCase;

OPEN c;

FETCH NEXT FROM c INTO @old, @new;

WHILE @@FETCH_STATUS = 0
BEGIN
  BEGIN TRANSACTION;

  UPDATE dbo.MyTable
    SET [Region Country] = @new
    WHERE [Region Country] = @old;

  COMMIT TRANSACTION;

  -- experiment with CHECKPOINT if simple
  -- or BACKUP LOG if full. This will depend
  -- on your current log size and autogrow
  -- settings; it can make things worse.

  --CHECKPOINT;
  --BACKUP LOG yourdb TO DISK ...;
END

另一种方法是限制行数,例如将代码的相关部分替换为这样的内容,您将更新限制为一次 1000 行:

DECLARE @rc INT;

WHILE @@FETCH_STATUS = 0
BEGIN

  SET @rc = 1;

  WHILE @rc <> 0
  BEGIN
    BEGIN TRANSACTION;

    UPDATE TOP (1000) dbo.MyTable
      SET [Region Country] = @new
      WHERE [Region Country] = @old;

    SET @rc = @@ROWCOUNT;

    COMMIT TRANSACTION;
  END

关键是保持个人交易简短。更新所有 300 万行可能是可以容忍的(取决于您执行这项工作的时间 - 例如下班时间、维护窗口等),但有一些技巧可以将这些更新批量化并最大限度地减少对事务日志和并发性的影响。在某些情况下,这种分块会使更新花费更长的时间,但不是全部,而且速度通常不是主要问题(当您第一次锁定系统更新整个表时会发现)。

(顺便说一句,现在正在写一篇关于这类事情的博客文章。)

于 2013-03-09T20:29:27.097 回答