web-dev-qa-db-ja.com

SQL Serverで発生する非決定的な更新を特定するにはどうすればよいですか?

SQL Serverがオンラインで予約 状態

FROM句を指定して更新操作の基準を提供する場合は注意してください。 UPDATEステートメントの結果が未定義であるFROM句がステートメントに含まれている場合、その結果は未定義です。つまり、更新される各列オカレンスで使用できる値は1つだけです。 UPDATEステートメントは確定的ではありません。

私のデータベースで非決定的な更新が発生しているケースを調べて特定する必要があります。このような場合、SQL Serverはエラーや警告をスローしないようです。

これらの問題が発生している場所を特定したり、プランキャッシュをクエリしたりする簡単な方法があるかどうか誰かが知っていますか?アプリケーションのサイズと複雑さを考えると、これらの問題を特定するために完全なコードレビューを実行するには時間がかかります。

SQL Server 2008\2017開発版でテスト済み。

次に例を示します。

declare @t1 table (id int, col int);
insert into @t1 values(1,1);


declare @t2 table (id int, col int);
insert into @t2 values(1,10), (1,20);


update t1
set t1.col = t2.col
from @t1 t1 join @t2 t2
         on t1.id = t2.id;

select *
from @t1;
2
OzzieDBA

SQL Serverは、独自のUPDATE構文を使用しているときに警告を出しません。 MERGEステートメントは、「MERGEステートメントが同じ行を複数回UPDATEまたはDELETEしようとしました。これは、ターゲット行が複数のソース行と一致する場合に発生します。」

あなたの例の実行計画は

update t1  set t1.col = t2.col  from @t1 t1 join @t2 t2           on t1.id = t2.id
  |--Table Update(OBJECT:(@t1 AS [t1]), SET:(@t1.[col] as [t1].[col] = @t2.[col] as [t2].[col]))
       |--Stream Aggregate(GROUP BY:([Bmk1000]) DEFINE:([t2].[col]=ANY(@t2.[col] as [t2].[col])))
            |--Nested Loops(Inner Join, WHERE:(@t2.[id] as [t2].[id]=@t1.[id] as [t1].[id]))
                 |--Table Scan(OBJECT:(@t1 AS [t1]))
                 |--Table Scan(OBJECT:(@t2 AS [t2]))

Bmk列は、@t1のスキャンから出力され、一意の行識別子として機能します。次に、Bmk集計を使用して、@t2の複数の結合行が追加され、ANYごとに任意の行に折りたたまれます。 計画によっては、ANY集合体をDISTINCT SORT)に置き換える場合があります

したがって、1つの方法は、updateまたはDISTINCT SORTのいずれかを使用して子孫演算子を持つANY演算子を含むすべての実行プランをプランキャッシュで検索することです。更新を行う前に、重複を恣意的に削除していました。

これは、他の理由で計画に表示される可能性があるが、いくつかの実行可能な候補を検討する必要があるので、決してばかげているとは言えません。

WITH XMLNAMESPACES (DEFAULT 'http://schemas.Microsoft.com/sqlserver/2004/07/showplan')
SELECT st.text
FROM   sys.dm_exec_cached_plans cp
       CROSS APPLY sys.dm_exec_query_plan(cp.plan_handle) qp
       CROSS APPLY sys.dm_exec_sql_text(cp.plan_handle) st
WHERE  EXISTS(SELECT 1
              FROM   qp.query_plan.nodes('//Update') upd(n)
              WHERE  1 IN ( n.exist('//Aggregate[@AggType eq "ANY"]'), 
                            n.exist('//RelOp[@LogicalOp eq "Distinct Sort"]') )) 
3
Martin Smith