私が管理している多くのデータベースには、IDのリストでコードがXML文字列としてストアドプロシージャに渡されるパターンがあります。ユーザー定義関数はそれらをテーブルに変換し、IDの照合に使用されます。これを実行する関数は次のようになります。
ALTER function [dbo].[XMLIdentifiers] (@xml xml)
returns table
as
return (
--get the ids from the xml
select Item.value('.', 'int') as id from @xml.nodes('//id') as T(Item)
)
これは、SSMSのさまざまなシナリオでテストするとうまく機能します。しかし、ストアドプロシージャ内で呼び出されると、実行速度が非常に遅くなり、実行計画では、これらのIDの解析に大部分の時間を費やしていることがわかります。これは、結果を一時テーブルに書き込んだり、結合したり、サブクエリで使用したりする場合に当てはまります。
上記の関数を使用した実行プランの例:
なぜこれらのクエリからこのようなパフォーマンスの低下が見られるのかについて誰かが洞察を提供できますか? XMLから値を解析するより良い方法はありますか?このパターンを使用するデータベースのほとんどは、SQL Server 2014または2016にあります。
あなたの質問の一部に答えるために、はい、XMLから値を解析するより良い方法があります。
Always(*)は、できるだけ早くXMLノードからtext()を抽出します。
(*-「依存する」が、必ず確認するためにテストする)
あなたの場合、これは「paths」メソッドを変更して、xpathクエリの一部としてtext()ノードを使用することを意味します。
ALTER function [dbo].[XMLIdentifiers] (@xml xml)
returns table
as
return (
--get the ids from the xml
select Item.value('.', 'int') as id from @xml.nodes('//id/text()') as T(Item)
)
この単純なテストでは、単純な統計テストが劇的な改善を示しているだけでなく、実行計画も大幅に変化していることがわかります。
declare @x xml
select @x = (select top(100000) row_number() over(order by @@spid) as [n] from sys.columns as [a],sys.columns as [b] for xml auto,elements,type);
declare @c int;
set statistics io,time on;
-- Usual suspect
select @c = nd.value('.','int')
from @x.nodes('//n') x(nd);
-- Using text() when we might also need other parts of the node
select @c = nd.value('(./text())[1]','int')
from @x.nodes('//n') x(nd);
-- Using text() when that is all we need from the node
select @c = nd.value('.','int')
from @x.nodes('//n/text()') x(nd);
set statistics io,time off;
Text()を使用しない場合:CPU時間= 1281ミリ秒、経過時間= 1459ミリ秒。
text()を遅く使用:CPU時間= 719ミリ秒、経過時間= 739ミリ秒。