私のシステムで作業を分散するために使用されるビジーキューテーブルをクエリするストアドプロシージャがあります。問題のテーブルにはWorkIDの主キーがあり、重複はありません。
クエリの簡略版は次のとおりです。
INSERT INTO #TempWorkIDs (WorkID)
SELECT
W.WorkID
FROM
dbo.WorkTable W
WHERE
(@bool_param = 0 AND
((W.InProgress = 0
AND ISNULL(W.UserID, -1) != @userid_param
AND (@bool_filtered = 0
OR W.TypeID IN (SELECT TypeID FROM #Types AS t)))
OR
(@bool_param = 1
AND W.InProgress = 1
AND W.UserID != @userid_param)
OR
(@Auto_Param = 0
AND W.UserID = @userid_param)))
OR
(@bool_param = 1 AND W.UserID = @userid_param)
OPTION
(RECOMPILE)
#Types
テーブルは、先の手順で移入されます。
先ほど述べたように、WorkTable
ビジー状態で、このクエリの実行中に、レコードの1 つが内の 1つのフィルターセットWHERE
から別のフィルターセットに移動しているようです。具体的には、誰かがアイテムで作業を開始すると、これが発生しW.InProgress
、0から1に変更されます。このクエリが挿入される一時テーブルに主キーを追加しようとすると、重複キー違反が発生します。
エラーが発生したときに生成されるクエリプランで、並列処理がなく、分離レベルがREAD COMMITTED
であり、ソーステーブルに重複レコードがないことを確認しました。JOIN
ここには、デカルト積を取得するsや他の方法がないこともわかります。
これは匿名化されたクエリプランです。
問題は、何が重複を引き起こしているのか、そしてどのようにしてそれを停止させるのかということです。
READ COMMITTED
ここで動作するはずだと思います。ロックが必要です。InProgress
クエリ中にレコードのビットが変更されると、重複が発生することはほぼ確実です。テーブルにはその変更の時間が格納されており、クエリを実行してエラーが発生してから数ミリ秒以内であるため、これはわかっています。