0

MyTable次のようなデータを含むテーブルがあります。

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

私がしたいのは、そのテーブル内のすべてのレコード (300 万レコード) の 2 文字の国コード (ISO) を完全な国名に変更することです。

Countries2 文字の国コードとその「フルネーム」をすべて含むテーブルがあるので、このテーブルを参照して更新できます。

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に基づいて更新するプロセスになります。CountriesCountryNameLowerCase

これを行うための最良の方法は何ですか(パフォーマンスに関して)?

4

1 に答える 1

3

UPDATE簡単な方法は、 と を使用した 1 回のブルート フォース更新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

もう 1 つの方法は行数を制限することです。たとえば、コードの関連部分を次のように置き換えて、更新を一度に 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 に答える