14

私が持っているもの

次のような MSSQL 2008 R2 で解析する必要がある可変サイズの XML ドキュメントがあります。

<data item_id_type="1" cfgid="{4F5BBD5E-72ED-4201-B741-F6C8CC89D8EB}" has_data_event="False">
  <item name="1">
    <field id="{EA032B25-19F1-4C1B-BDDE-3113542D13A5}" type="2">0.506543009706267</field>
    <field id="{71014ACB-571B-4C72-9C9B-05458B11335F}" type="2">-0.79500402346138</field>
    <field id="{740C36E9-1988-413E-A1D5-B3E5B4405B45}" type="2">0.0152649050024924</field>
  </item>
  <item name="2">
    <field id="{EA032B25-19F1-4C1B-BDDE-3113542D13A5}" type="2">0.366096802804087</field>
    <field id="{71014ACB-571B-4C72-9C9B-05458B11335F}" type="2">-0.386642801354842</field>
    <field id="{740C36E9-1988-413E-A1D5-B3E5B4405B45}" type="2">0.031671174184115</field>
  </item>
</data>

.

私が欲しいもの

次のような通常のテーブル型のデータセットに変換する必要があります。

item_name field_id                             field_type  field_value
--------- ------------------------------------ ----------- ---------------
1         EA032B25-19F1-4C1B-BDDE-3113542D13A5 2           0.5065430097062
1         71014ACB-571B-4C72-9C9B-05458B11335F 2           -0.795004023461
1         740C36E9-1988-413E-A1D5-B3E5B4405B45 2           0.0152649050024
2         EA032B25-19F1-4C1B-BDDE-3113542D13A5 2           0.3660968028040
2         71014ACB-571B-4C72-9C9B-05458B11335F 2           -0.386642801354
2         740C36E9-1988-413E-A1D5-B3E5B4405B45 2           0.0316711741841
3         EA032B25-19F1-4C1B-BDDE-3113542D13A5 2           0.8839620369590
3         71014ACB-571B-4C72-9C9B-05458B11335F 2           -0.781459993268
3         740C36E9-1988-413E-A1D5-B3E5B4405B45 2           0.2284423515729

.

機能するもの

このcross applyクエリは、目的の出力を作成します。

create table #temp (x xml)

insert into #temp (x)
values ('
<data item_id_type="1" cfgid="{4F5BBD5E-72ED-4201-B741-F6C8CC89D8EB}" has_data_event="False">
  <item name="1">
    <field id="{EA032B25-19F1-4C1B-BDDE-3113542D13A5}" type="2">0.506543009706267</field>
    <field id="{71014ACB-571B-4C72-9C9B-05458B11335F}" type="2">-0.79500402346138</field>
    <field id="{740C36E9-1988-413E-A1D5-B3E5B4405B45}" type="2">0.0152649050024924</field>
  </item>
  <item name="2">
    <field id="{EA032B25-19F1-4C1B-BDDE-3113542D13A5}" type="2">0.366096802804087</field>
    <field id="{71014ACB-571B-4C72-9C9B-05458B11335F}" type="2">-0.386642801354842</field>
    <field id="{740C36E9-1988-413E-A1D5-B3E5B4405B45}" type="2">0.031671174184115</field>
  </item>
  <item name="3">
    <field id="{EA032B25-19F1-4C1B-BDDE-3113542D13A5}" type="2">0.883962036959074</field>
    <field id="{71014ACB-571B-4C72-9C9B-05458B11335F}" type="2">-0.781459993268713</field>
    <field id="{740C36E9-1988-413E-A1D5-B3E5B4405B45}" type="2">0.228442351572923</field>
  </item>
</data>
')

select c.value('(../@name)','varchar(5)') as item_name
      ,c.value('(@id)','uniqueidentifier') as field_id
      ,c.value('(@type)','int') as field_type
      ,c.value('(.)','nvarchar(15)') as field_value
from   #temp cross apply
       #temp.x.nodes('/data/item/field') as y(c)

drop table #temp

.

問題

XML に数百 (またはそれ以下)<item>の要素がある場合、クエリは問題なく実行されます。ただし、<item>要素数が 1,000 の場合、SSMS で行を返し終わるまでに 24 秒かかります。要素数が 6,500 の場合、クエリ<item>の実行には約 20 分かかります。cross apply10 ~ 20,000<item>の要素を持つことができます。

.

質問

この単純なcross applyXML ドキュメントでクエリのパフォーマンスが低下し、データセットが大きくなるにつれてクエリのパフォーマンスが指数関数的に低下するのはなぜでしょうか?

XML ドキュメントを (SQL で) 表形式のデータセットに変換するより効率的な方法はありますか?

4

2 に答える 2

36

この単純な XML ドキュメントで相互適用クエリのパフォーマンスが低下し、データセットが大きくなるにつれてパフォーマンスが指数関数的に低下するのはなぜでしょうか?

親軸を使用して、項目ノードから属性 ID を取得します。

問題があるのは、クエリ プランのこの部分です。

ここに画像の説明を入力

下のテーブル値関数から 423 行が出力されていることに注目してください。

3 つのフィールド ノードを持つアイテム ノードをもう 1 つ追加するだけで、これが実現します。

ここに画像の説明を入力

732 行が返されました。

最初のクエリのノードを 2 倍にして合計 6 つの項目ノードにするとどうなるでしょうか?

ここに画像の説明を入力

なんと 1602 行が返されました。

一番上の関数の図 18 は、XML 内のすべてのフィールド ノードです。各項目に 3 つのフィールドを持つ 6 つの項目があります。これらの 18 個のノードは、他の関数に対するネストされたループ結合で使用されるため、18 回の実行で 1602 行が返されるため、反復ごとに 89 行が返されます。これはたまたま、XML 全体の正確なノード数です。実際には、すべての可視ノードよりも 1 つ多くなっています。どうしてか分かりません。このクエリを使用して、XML 内のノードの総数を確認できます。

select count(*)
from @XML.nodes('//*, //@*, //*/text()') as T(X)  

そのため、値関数で親軸を使用するときに SQL Server が値を取得するために使用するアルゴリズム..は、最初に細断対象のすべてのノード (最後の場合は 18) を見つけることです。これらのノードごとに、XML ドキュメント全体をシュレッドして返し、フィルター演算子で実際に必要なノードをチェックします。そこにあなたの指数関数的な成長があります。親軸を使用する代わりに、追加のクロス適用を 1 つ使用する必要があります。最初にアイテムを細断し、次にフィールドで細断します。

select I.X.value('@name', 'varchar(5)') as item_name,
       F.X.value('@id', 'uniqueidentifier') as field_id,
       F.X.value('@type', 'int') as field_type,
       F.X.value('text()[1]', 'nvarchar(15)') as field_value
from #temp as T
  cross apply T.x.nodes('/data/item') as I(X)
  cross apply I.X.nodes('field') as F(X)

また、フィールドのテキスト値にアクセスする方法も変更しました。を使用.すると、SQL Server が子ノードを探し、fieldそれらの値を結果に連結します。子の値がないため、結果は同じですが、クエリ プラン (UDX 演算子) にその部分を含めないようにすることをお勧めします。

XML インデックスを使用している場合、クエリ プランには親軸に関する問題はありませんが、フィールド値の取得方法を変更することでメリットが得られます。

于 2014-06-13T06:58:25.553 に答える
3

XML インデックスを追加するとうまくいきました。これで、実行に 20 分かかった 6,500 レコードが 4 秒未満になりました。

create table #temp (id int primary key, x xml)
create primary xml index idx_x on #temp (x)

insert into #temp (id, x)
values (1, '
<data item_id_type="1" cfgid="{4F5BBD5E-72ED-4201-B741-F6C8CC89D8EB}" has_data_event="False">
  <item name="1">
    <field id="{EA032B25-19F1-4C1B-BDDE-3113542D13A5}" type="2">0.506543009706267</field>
    <field id="{71014ACB-571B-4C72-9C9B-05458B11335F}" type="2">-0.79500402346138</field>
    <field id="{740C36E9-1988-413E-A1D5-B3E5B4405B45}" type="2">0.0152649050024924</field>
  </item>
  <item name="2">
    <field id="{EA032B25-19F1-4C1B-BDDE-3113542D13A5}" type="2">0.366096802804087</field>
    <field id="{71014ACB-571B-4C72-9C9B-05458B11335F}" type="2">-0.386642801354842</field>
    <field id="{740C36E9-1988-413E-A1D5-B3E5B4405B45}" type="2">0.031671174184115</field>
  </item>
  <item name="3">
    <field id="{EA032B25-19F1-4C1B-BDDE-3113542D13A5}" type="2">0.883962036959074</field>
    <field id="{71014ACB-571B-4C72-9C9B-05458B11335F}" type="2">-0.781459993268713</field>
    <field id="{740C36E9-1988-413E-A1D5-B3E5B4405B45}" type="2">0.228442351572923</field>
  </item>
</data>
')

select c.value('(../@name)','varchar(5)') as item_name
      ,c.value('(@id)','uniqueidentifier') as field_id
      ,c.value('(@type)','int') as field_type
      ,c.value('(.)','nvarchar(15)') as field_value
from   #temp cross apply
       #temp.x.nodes('/data/item/field') as y(c)

drop table #temp
于 2014-06-13T04:46:39.410 に答える