Уничтожение вложенных XML данных и возврат наборов - PullRequest
0 голосов
/ 03 августа 2020

У меня есть 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>'

Я бы хотел выделить все эти данные в форму:

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)

, но он просто возвращает

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

Спасибо за ваше время.

1 Ответ

2 голосов
/ 03 августа 2020

Вы по какой-то причине повторно объявляете узлы root в своем 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 Server, и использование строковых псевдонимов для столбцов может сбивать с толку. Я также дал вашим столбцам и объектам более релевантные псевдонимы или псевдонимы, которые не являются зарезервированными ключевыми словами.

Я также использовал функциональность text() при получении значений из XML, поскольку это быстрее.

...