随着xml文档的增长,交叉应用xml查询的执行情况会成倍地恶化

问题描述 投票:13回答:2

我有什么

我有一个可变大小的XML文档,需要在MSSQL 2008 R2上进行解析,它看起来像这样。

<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

.

问题

当有几百个(或更少)的时候 <item> 元素的时候,查询执行得还不错。 然而,当有1,000个 <item> 元素,需要24秒才能完成返回SSMS中的行。 当有6,500个 <item> 元素,大约需要20分钟的时间来运行。cross apply 查询。 我们可以有10-20,000 <item> 元素。

.

疑问

是什么使 cross apply 查询在这个简单的XML文档上的表现如此之差,并且随着数据集的增长,其执行速度也成倍地慢?

有没有更有效的方法将XML文档转换为表格数据集(在SQL中)?

sql-server performance sql-server-2008-r2 sqlxml cross-apply
2个回答
32
投票

是什么让交叉应用查询在这个简单的XML文档上表现得如此糟糕,并随着数据集的增长而指数级地慢下来?

是使用父轴从项目节点获取属性ID。

正是这部分的查询计划有问题。

enter image description here

注意下Table-valued函数出来的423行。

只要再增加一个项目节点与三个字段节点,就可以得到这样的结果。

enter image description here

返回的732条记录。

如果我们把第一次查询的节点增加一倍,共增加6个项节点呢?

enter image description here

我们就会有高达1602行的返回。

上面函数中的图18是你XML中所有的字段节点。我们这里有6个项目,每个项目中有三个字段。这18个节点被用于对其他函数的嵌套循环连接,所以18次执行返回1602行,给出了它每次迭代返回89行。这刚好是整个XML中的节点数。好吧,它实际上比所有可见节点多了一个。我不知道为什么。你可以使用这个查询来检查你的XML中的总节点数。

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

所以,当你使用父轴的时候,SQL Server使用的算法是用来获取值的 .. 值函数中,它首先找到你要粉碎的所有节点,在最后的情况下是18个。对于这些节点中的每一个,它都会切碎并返回整个XML文档,并在过滤器操作符中检查你实际想要的节点。你有你的指数增长.而不是使用父轴,你应该使用一个额外的交叉应用。首先在项目上粉碎,然后在字段上粉碎。

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索引,查询计划就不会有父轴的问题,但你仍然会从改变获取字段值的方式中受益。


3
投票

添加一个XML索引就起到了作用。 现在,花了20分钟运行的6500条记录需要<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
© www.soinside.com 2019 - 2024. All rights reserved.