-
操作がページロックを取得していることが確実でない限り、セット内の10k行を更新しないでください(ページごとに複数の行が
UPDATE
の一部であるため) 手術)。問題は、ロックのエスカレーション(行またはページからテーブルへのロック)が5000ロックで発生することです。 。したがって、操作で行ロックを使用している場合に備えて、5000未満に保つのが最も安全です。 -
すべきではありません SET ROWCOUNTを使用して、変更される行の数を制限します。ここには2つの問題があります:
-
SQL Server 2005がリリースされてから(11年前)、廃止されました:
SET ROWCOUNTを使用しても、SQL Serverの将来のリリースでは、DELETE、INSERT、およびUPDATEステートメントには影響しません。新しい開発作業では、DELETE、INSERT、およびUPDATEステートメントでSET ROWCOUNTを使用することは避け、現在それを使用しているアプリケーションを変更することを計画してください。同様の動作については、TOP構文を使用してください
-
それはあなたが扱っているステートメント以上のものに影響を与える可能性があります:
SET ROWCOUNTオプションを設定すると、ほとんどのTransact-SQLステートメントは、指定された行数の影響を受けたときに処理を停止します。これにはトリガーが含まれます。 ROWCOUNTオプションは動的カーソルには影響しませんが、キーセットおよび非依存カーソルの行セットを制限します。このオプションは注意して使用する必要があります。
代わりに、
TOP ()
を使用してください 条項。 -
-
ここで明示的なトランザクションを行うことに目的はありません。コードが複雑になり、
ROLLBACK
を処理できません。 、各ステートメントは独自のトランザクション(つまり、自動コミット)であるため、これも必要ありません。 -
明示的なトランザクションを維持する理由を見つけたとすると、
TRY
はありません。 /CATCH
構造。TRY
については、DBA.StackExchangeで私の回答をご覧ください。 /CATCH
トランザクションを処理するテンプレート:トランザクションをC#コードとストアドプロシージャで処理する必要がありますか
本当のWHERE
質問のサンプルコードには句が表示されていないため、表示されている内容に依存するだけで、より良い モデルは次のようになります:
DECLARE @Rows INT,
@BatchSize INT; -- keep below 5000 to be safe
SET @BatchSize = 2000;
SET @Rows = @BatchSize; -- initialize just to enter the loop
BEGIN TRY
WHILE (@Rows = @BatchSize)
BEGIN
UPDATE TOP (@BatchSize) tab
SET tab.Value = 'abc1'
FROM TableName tab
WHERE tab.Parameter1 = 'abc'
AND tab.Parameter2 = 123
AND tab.Value <> 'abc1' COLLATE Latin1_General_100_BIN2;
-- Use a binary Collation (ending in _BIN2, not _BIN) to make sure
-- that you don't skip differences that compare the same due to
-- insensitivity of case, accent, etc, or linguistic equivalence.
SET @Rows = @@ROWCOUNT;
END;
END TRY
BEGIN CATCH
RAISERROR(stuff);
RETURN;
END CATCH;
@Rows
をテストする @BatchSize
に対して 、その最後のUPDATE
を回避できます 最終的なセットは通常、@BatchSize
よりも少ない行数であるため、クエリ(ほとんどの場合) 、この場合、処理するものがこれ以上ないことがわかります(これは、回答に示されている出力に表示されます)。行の最終セットが@BatchSize
に等しい場合のみ このコードは最終的なUPDATE
を実行しますか 0行に影響します。
WHERE
にも条件を追加しました すでに更新されている行が再度更新されないようにする句。
パフォーマンスに関する注意
上記で「より良い」を強調しました(「これはより良い」のように) モデル」)これは、O.P。の元のコードに比べていくつかの改善があり、多くの場合は正常に機能しますが、すべての場合に完全ではないためです。少なくとも特定のサイズのテーブルの場合(いくつかの要因によって異なるため、具体的には、次のいずれかの場合に修正する行が少なくなるため、パフォーマンスが低下します。
- クエリをサポートするインデックスがない、または
- インデックスがありますが、
WHERE
に少なくとも1つの列があります 句は、バイナリ照合を使用しない文字列データ型であるため、COLLATE
ここで句がクエリに追加され、バイナリ照合が強制されます。これにより、インデックスが無効になります(この特定のクエリの場合)。
これは@mikesigsが遭遇した状況であるため、別のアプローチが必要です。更新されたメソッドは、更新されるすべての行のIDを一時テーブルにコピーし、その一時テーブルを使用してINNER JOIN
クラスタ化インデックスキー列で更新されるテーブルに。 (クラスター化インデックスをキャプチャして参加することが重要です 列、それらが主キー列であるかどうかに関係なく!)
詳細については、以下の@mikesigsの回答を参照してください。その答えに示されているアプローチは、私が何度も使用した非常に効果的なパターンです。私が行う唯一の変更は次のとおりです。
-
#targetIds
を明示的に作成しますSELECT INTO...
を使用するのではなくテーブル -
#targetIds
の場合 テーブルで、列にクラスター化された主キーを宣言します。 -
#batchIds
の場合 テーブルで、列にクラスター化された主キーを宣言します。 -
#targetIds
に挿入する場合 、INSERT INTO #targetIds (column_name(s)) SELECT
を使用します およびORDER BY
を削除します 不要なので。
したがって、この操作に使用できるインデックスがなく、実際に機能するインデックスを一時的に作成できない場合(WHERE
によっては、フィルタリングされたインデックスが機能する場合があります。 UPDATE
の句 クエリ)、@ mikesigsの回答に示されているアプローチを試してください(そのソリューションを使用する場合は、賛成票を投じてください)。