粉碎嵌套 XML 数据并返回集合
Shredding nested XML data and returning sets
我有 xml 个集合嵌套在多个级别的数据。
DECLARE @xml AS XML
SET @xml = '<periods xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<period>
<id>1</id>
<valid_from>2020-07-31</valid_from>
<valid_to xsi:nil="true" />
<elements>
<element>
<from>1</from>
<to>2</to>
</element>
<element>
<from>1</from>
<to>3</to>
</element>
</elements>
</period>
<period>
<id>3</id>
<valid_from>2020-05-01</valid_from>
<valid_to>2020-06-01</valid_to>
<elements>
<element>
<from>7</from>
<to>9</to>
</element>
<element>
<from>10</from>
<to>11</to>
</element>
</elements>
</period>
</periods>'
我想select将所有这些数据转换成以下形式:
id valid_from valid_to from to
1 2020-07-31 00:00:00.000 NULL 1 2
1 2020-07-31 00:00:00.000 NULL 1 3
3 2020-05-01 00:00:00.000 2020-06-01 00:00:00.000 7 9
3 2020-05-01 00:00:00.000 2020-06-01 00:00:00.000 10 11
我能得到的最接近的是这个查询
SELECT 'id' = v.value('id[1]', 'int'),
'dte_from' = v.value('valid_from[1]', 'datetime'),
'dte_to' = v.value('valid_to[1][not(@xsi:nil = "true")]', 'datetime'),
'from' = y.value('from[1][not(@xsi:nil = "true")]', 'int'), 'to' = y.value('to[1]', 'int')
FROM @xml.nodes('/periods/period') x(v)
CROSS APPLY x.v.nodes('/periods/period/elements/element') z(y)
但这只是 returns
id dte_from dte_to from to
1 2020-07-31 00:00:00.000 NULL 1 2
1 2020-07-31 00:00:00.000 NULL 1 3
1 2020-07-31 00:00:00.000 NULL 7 9
1 2020-07-31 00:00:00.000 NULL 10 11
3 2020-05-01 00:00:00.000 2020-06-01 00:00:00.000 1 2
3 2020-05-01 00:00:00.000 2020-06-01 00:00:00.000 1 3
3 2020-05-01 00:00:00.000 2020-06-01 00:00:00.000 7 9
3 2020-05-01 00:00:00.000 2020-06-01 00:00:00.000 10 11
感谢您的宝贵时间。
出于某种原因,您要在 CROSS APPLY
中重新声明根节点。你不需要那些,如果你删除它们,你会得到你想要的结果:
SELECT pp.p.value('(id/text())[1]', 'int') AS id,
pp.p.value('(valid_from/text())[1]', 'datetime') AS dte_from,
pp.p.value('(valid_to[not(@xsi:nil = "true")]/text())[1]', 'datetime') AS dte_to,
ee.e.value('(from[1][not(@xsi:nil = "true")]/text())[1]', 'int') AS int_from, --don't use FROM, it's a reserve keyword,
ee.e.value('(to[1]/text())[1]', 'int') AS int_to --don't use TO, it's a reserve keyword,
FROM @xml.nodes('/periods/period') pp(p)
CROSS APPLY pp.p.nodes('elements/element') ee(e);
正如我在评论中提到的,不要使用 'string_alias' = expression
,因为它将从 SQL 服务器中删除,并且对列使用字符串别名可能会造成混淆。我还为您的列和对象提供了更相关的别名,或者不是保留关键字的别名。
从 XML 获取值时,我还使用了 text()
功能,因为这样速度更快。
我有 xml 个集合嵌套在多个级别的数据。
DECLARE @xml AS XML
SET @xml = '<periods xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<period>
<id>1</id>
<valid_from>2020-07-31</valid_from>
<valid_to xsi:nil="true" />
<elements>
<element>
<from>1</from>
<to>2</to>
</element>
<element>
<from>1</from>
<to>3</to>
</element>
</elements>
</period>
<period>
<id>3</id>
<valid_from>2020-05-01</valid_from>
<valid_to>2020-06-01</valid_to>
<elements>
<element>
<from>7</from>
<to>9</to>
</element>
<element>
<from>10</from>
<to>11</to>
</element>
</elements>
</period>
</periods>'
我想select将所有这些数据转换成以下形式:
id valid_from valid_to from to
1 2020-07-31 00:00:00.000 NULL 1 2
1 2020-07-31 00:00:00.000 NULL 1 3
3 2020-05-01 00:00:00.000 2020-06-01 00:00:00.000 7 9
3 2020-05-01 00:00:00.000 2020-06-01 00:00:00.000 10 11
我能得到的最接近的是这个查询
SELECT 'id' = v.value('id[1]', 'int'),
'dte_from' = v.value('valid_from[1]', 'datetime'),
'dte_to' = v.value('valid_to[1][not(@xsi:nil = "true")]', 'datetime'),
'from' = y.value('from[1][not(@xsi:nil = "true")]', 'int'), 'to' = y.value('to[1]', 'int')
FROM @xml.nodes('/periods/period') x(v)
CROSS APPLY x.v.nodes('/periods/period/elements/element') z(y)
但这只是 returns
id dte_from dte_to from to
1 2020-07-31 00:00:00.000 NULL 1 2
1 2020-07-31 00:00:00.000 NULL 1 3
1 2020-07-31 00:00:00.000 NULL 7 9
1 2020-07-31 00:00:00.000 NULL 10 11
3 2020-05-01 00:00:00.000 2020-06-01 00:00:00.000 1 2
3 2020-05-01 00:00:00.000 2020-06-01 00:00:00.000 1 3
3 2020-05-01 00:00:00.000 2020-06-01 00:00:00.000 7 9
3 2020-05-01 00:00:00.000 2020-06-01 00:00:00.000 10 11
感谢您的宝贵时间。
出于某种原因,您要在 CROSS APPLY
中重新声明根节点。你不需要那些,如果你删除它们,你会得到你想要的结果:
SELECT pp.p.value('(id/text())[1]', 'int') AS id,
pp.p.value('(valid_from/text())[1]', 'datetime') AS dte_from,
pp.p.value('(valid_to[not(@xsi:nil = "true")]/text())[1]', 'datetime') AS dte_to,
ee.e.value('(from[1][not(@xsi:nil = "true")]/text())[1]', 'int') AS int_from, --don't use FROM, it's a reserve keyword,
ee.e.value('(to[1]/text())[1]', 'int') AS int_to --don't use TO, it's a reserve keyword,
FROM @xml.nodes('/periods/period') pp(p)
CROSS APPLY pp.p.nodes('elements/element') ee(e);
正如我在评论中提到的,不要使用 'string_alias' = expression
,因为它将从 SQL 服务器中删除,并且对列使用字符串别名可能会造成混淆。我还为您的列和对象提供了更相关的别名,或者不是保留关键字的别名。
从 XML 获取值时,我还使用了 text()
功能,因为这样速度更快。