数百万件のレコードでテーブルを更新して、4日間でした


12

現在、数百万件のレコードでテーブルを更新していますが、4日間で、クエリはまだ実行中です。

クエリが実行されていることを示すアクティビティモニターを確認しました。

イベントログにエラーはまったくありません。

賢明なパフォーマンス:

  • ディスクAのTempdb(850 GBの空き領域)
  • ディスクBのデータベースファイル(750 GBの空き領域)
  • 16 GBラム

どうすればいいですか?

クエリ

UPDATE
    dbo.table1
SET 
    costPercentage = ISNULL(t2.PaymentIndex, 1.0),
    t2.TopUp_Amt = (ISNULL(t2.PaymentIndex, 1.0) - 1.0)
    * ISNULL(dbo.table1.Initial_Tariff_Amt, 0.00),
    Total_Tariff_Inc_t2 = ISNULL(t2.PaymentIndex, 1.0)
    * ISNULL(dbo.table1.Initial_Tariff_Amt, 0.00)
FROM
    dbo.table2 t2
WHERE
    LEFT(dbo.test1.procodet, 3) = LEFT(t2.ProviderCode, 3) COLLATE database_default 

回答:


3

このクエリには、最初は気付かなかった興味深い詳細があります。Fabricio Araujoの答えのおかげで、私は今それを見ています:あなたは2つのテーブルにアクセスしています。私は以前にこの種の更新ステートメントの使用法を見たことがないので、使用することはお勧めしません。Fabricioの回答ごとに、より直感的な結合構文を使用することをお勧めします。

考えられる原因は、2つのテーブル間の結合により極端な数の行が生成されることです。これは、LEFT(col, 3)式が重複値を生成する場合に発生する可能性があります。10個の重複が生成された場合、結合結果には100000x100000 = 10000000000行が含まれます。

ここでインデックス作成が役割を果たすとは思わない。SQL Serverは、この非インデックス結合をハッシュ結合またはマージ結合で問題なく解決できます。4日かかりません。

他の原因としては、おそらく結合入力または出力のカーディナリティの過小評価が考えられます。SQL Serverがループ結合を選択した可能性があります。

これはまだ推測であるため、この問題を明らかにするクエリプランを投稿することをお勧めします。


8

このクエリでは、テーブル内のすべての行をスキャンする必要があります。

  • procodetまたはProviderCodeがインデックスに登録されていないと思います
  • インデックスが付けられていても、WHERE述語の関数であるLEFTがあります
  • COLLATEもあります。これは実質的にWHERE述語の関数です

「WHERE述語の関数」は、インデックスが使用されないことを意味します

あなたはバッチがそれ(UPDATE TOP(10000)... AND costPercentage IS NULLで言う)あなたはcostPercentageにインデックスを必要とする場合と、これはあなたがそれを設定していると仮定します。

私が見る唯一の解決策は

  • たとえば、主キーに基づいて、新しいテーブルをバッチで作成します
  • LEFTおよびCOLLATE式を非表示にするインデックス付きの計算列を作成し、更新を実行します

@ gbn ..それは素晴らしいアイデアです。しかし、データが数百万単位であるため、このプロセスには時間がかかります....私はクエリの進行状況を知る方法があるかもしれないと考えていましたか?
ラッキー

1
数百万行のスキャンに4日かかるのはなぜですか?行がどれほど大きくてインデックス付けされていても、4日はかかりません。問題の根本はまだ不明です。
usr

1
通常、大量のデータを処理する場合、そのための適切なサーバーを取得するのはどうですか?SSDなど。上にデータを置く
のTomTom

1
@ラッキー確かに。私は答えに取り組んでいた。まだ見つかっていない問題があります。クエリ自体ではなく、ハードウェアでもありません。それは4日間の期間にはなりません。
usr

3
クエリが列の3文字の部分を別の列の3文字の部分に結合している場合、結果にはおそらく重複が含まれます。これは、単に数百万行を更新するよりもはるかに悪いです。数十億の作業テーブルをスキャンしているに違いない。
datagod

4

まず、クエリを次のように変更します。

UPDATE t1
SET 
    costPercentage = ISNULL(t2.PaymentIndex, 1.0),
    t2.TopUp_Amt = (ISNULL(t2.PaymentIndex, 1.0) - 1.0)
    * ISNULL(dbo.table1.Initial_Tariff_Amt, 0.00),
    Total_Tariff_Inc_t2 = ISNULL(t2.PaymentIndex, 1.0)
    * ISNULL(dbo.table1.Initial_Tariff_Amt, 0.00)
FROM
  dbo.table1 t1
  inner join dbo.table2 t2
    on LEFT(t1.procodet, 3) = LEFT(t2.ProviderCode, 3) COLLATE database_default 

その議論におけるジェフ・モデーンの最初の投稿示されているように、あなたの質問は彼が「ハロウィン効果」について警告したものと非常に似ています。

その後、それらのLEFT式にインデックスを付ける必要があります。gbnの答えは、その方法の指針を示しています。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.