0

SQL Server 2000 ジョブのパフォーマンスを向上させようとしています。シナリオは次のとおりです。
テーブルAには最大があります。300,000行。100行目を更新/削除すると(挿入時間に基づいて)、その行の後に追加されたすべての行が値を更新する必要があります。行番号 101 は、行番号に基づいて値を更新する必要があります。100と行番号。102 は、行番号 101 の更新された値に基づいてその値を更新する必要があります。例えば

古いテーブル:

ID...........Value  
100..........220  
101..........(220/2) = 110  
102..........(110/2)=55  
......................

行番号 100 が新しい値 300 で更新されました。

新しいテーブル

ID...........Value  
100..........300  
101..........(300/2) = 150  
102..........(150/2)=75  
......................  

実際の値の計算はより複雑です。式は簡単にするためのものです。

現在、トリガーは update/delete ステートメントに対して定義されています。行が更新または削除されると、トリガーによって行のデータがログ テーブルに追加されます。また、更新/削除後にコード ビハインドで SQL ジョブが作成され、最終的にテーブルの次のすべての行を反復してそれらの値を更新するストアド プロシージャが起動されAます。このプロセスは、300,000 行の場合、完了するまでに約 10 日かかります。

SP が起動されると、次の行の値が更新されます。これにより、SP の更新ごとにトリガーが再度実行され、これらの行もログ テーブルに追加されると思います。また、お客様のご要望に応じてDB側での作業となります。

この問題を解決するには:
ストアド プロシージャを変更し、トリガーから直接呼び出します。その後、ストアド プロシージャはトリガーを削除し、次の行の値を更新してから、トリガーを再度作成します。

  • プログラムの複数のインスタンスが同時に実行されます。SP の実行中に別のユーザーが行を変更すると、システムはトリガーを起動せず、困ったことになります。これに対する回避策はありますか?
  • このソリューションについてどう思いますか? これを達成するためのより良い方法はありますか?

ありがとうございました。

4

2 に答える 2

1

まず、更新プロセスについて。次の行を更新する場合、プロシージャは単に自分自身を呼び出しているだけだと理解しています。300K行の場合、ロギングがなくても、これは確かにそれほど高速にはなりません(ただし、実行にかかる日数はおそらくはるかに少なくなります)。しかし、私を絶対に超えているのは、最大ネストレベルに到達することなく、32行を超える行をそのように更新できる方法です。たぶん、私は一連の行動が間違っています。

とにかく、私はおそらくそれを別の方法で行うでしょう、たった1つの指示で:

UPDATE yourtable
SET @value = Value = CASE ID
                       WHEN @id THEN @value
                       ELSE @value / 2 /* basically, your formula */
                     END
WHERE ID >= @id
OPTION (MAXDOP 1);

ステートメントのOPTION (MAXDOP 1)ビットは、ステートメントの並列度を1に制限します。したがって、行が順番に更新され、すべての値が前の値、つまり前のID値を持つ行の値に基づいていることを確認します。また、ID列はクラスター化インデックスにする必要があります。これは、主キーになると、通常はデフォルトになります。

更新手順の他の機能、つまりトリガーの削除と再作成は、おそらくそれを無効にしてから再度有効にすることで置き換える必要があります。

ALTER TABLE yourtable DISABLE TRIGGER yourtabletrigger

/* the update part */

ALTER TABLE yourtable ENABLE TRIGGER yourtabletrigger

ただし、複数のユーザーが同時にテーブルを更新する可能性があるため、トリガーを実際に削除/無効にしないでください。

それでは、トリガーには触れていません。

代わりに、テーブルに特別な列を追加することをお勧めします。これは、ユーザーが気付かない、または少なくともあまり気にしないで、どういうわけか決して触れないようにする必要がある列です。その列は、「カスケード更新」プロセスによってのみ更新する必要があります。その列が更新されているかどうかを確認することで、更新プロシージャとロギングを呼び出す必要があるかどうかがわかります。

したがって、トリガーには次のようなものがあります。

IF NOT UPDATE(SpecialColumn) BEGIN
  /* assuming that without SpecialColumn only one row can be updated */
  SELECT TOP 1 @id = ID, @value = Value FROM inserted;
  EXEC UpdateProc @id, @value;
  EXEC LogProc ...;
END

UpdateProc

UPDATE yourtable
SET @value = Value = @value / 2,
    SpecialColumn = SpecialColumn /* basically, just anything, since it can
                                     only be updated by this procedure */
WHERE ID > @id
OPTION (MAXDOP 1);

今回はUPDATEステートメントが少し違うことに気づいたかもしれません。トリガーはFORUPDATE(= AFTER UPDATE)であると理解しています。これは、@id行がユーザーによってすでに更新されていることを意味します。したがって、プロシージャはそれをスキップして次の行から開始する必要があります。これで、更新式を単なる数式にすることができます。

結論として、私のテスト更新には、テーブルの300,000行のうち299,995行が含まれ、それほど高速ではないシステムでは約3秒かかりました。もちろん、ロギングはありませんが、それはあなたにそれがどれほど速くなることができるかについての基本的な絵を与えるはずだと思います。

于 2011-05-16T20:37:11.850 に答える
1

ここに大きな理論的問題があります。1 つの行を更新するには、他の 299,900 行を更新する必要がある場合、常に非常に疑わしいです。これは、データ モデルに重大な欠陥があることを示唆しています。決して適切ではないということではなく、人々が考えているよりもはるかに少ない頻度で必要とされるだけです. このようなことが絶対に必要な場合は、通常、バッチ操作として実行されます。

奇跡的な状況で期待できる最善の方法は、その 10 日を 10 分に変えることですが、10 秒すらもかかりません。別のアプローチを検討できるように、なぜこれが必要なのかを徹底的に説明することをお勧めします。

于 2011-05-16T17:08:11.270 に答える