粉碎嵌套 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() 功能,因为这样速度更快。