我有一个可变大小的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
当XML中有几百个(或更少)<item>
个元素时,查询执行得很好。但是,当有1,000个<item>
元素时,完成返回SSMS中的行需要24秒。当有6,500个<item>
元素时,运行cross apply
查询大约需要20分钟。我们可以有10-20,000个<item>
元素。
是什么让cross apply
查询在这个简单的XML文档上表现得如此糟糕,并且随着数据集的增长呈指数级变慢?
是否有更有效的方法将XML文档转换为表格数据集(在SQL中)?
答案 0 :(得分:29)
什么使得交叉应用查询在这个简单的XML上执行得如此糟糕 文档,并随着数据集的增长呈指数级下降?
使用父轴来从项目节点获取属性ID。
查询计划的这一部分存在问题。
注意423行来自较低的表值函数。
只添加一个带有三个字段节点的项目节点就可以实现这一点。
返回了732行。
如果我们将第一个查询中的节点加倍到总共6个项目节点会怎样?
我们要回到1602行。
top函数中的图18是XML中的所有字段节点。我们这里有6个项目,每个项目有三个字段。这18个节点在嵌套循环中用于连接另一个函数,因此返回1602行的18次执行使得每次迭代返回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索引,查询计划不会出现父轴问题,但您仍然可以通过更改获取字段值的方式获益。
答案 1 :(得分:2)
添加XML索引就可以了。现在,需要20分钟才能运行的6,500条记录需要&lt; 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