Преобразование одного столбца, одной строки XML данных в таблицу SQL - PullRequest
1 голос
/ 26 марта 2020

У меня есть таблица XMLTest с одним столбцом Val с XML типом данных

create table XMLTest(Val XML)

В эту таблицу я прочитал эти данные:

<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://www.w3.org/2003/05/soap-envelope">
  <SOAP-ENV:Body>
    <ns0:getInterestAndExchangeRatesResponse xmlns:ns0="http://swea.riksbank.se/xsd">
      <return xmlns="">
        <datefrom xmlns="">2020-03-25</datefrom>
        <dateto xmlns="">2020-03-26</dateto>
        <groups xmlns="">
          <groupid xmlns="">130</groupid>
          <groupname xmlns="">Currencies against Swedish kronor</groupname>
          <series xmlns="">
            <seriesid xmlns="">SEKEURPMI</seriesid>
            <seriesname xmlns="">1 EUR</seriesname>
            <unit xmlns="">1.0E0</unit>
            <resultrows xmlns="">
              <date xmlns="">2020-03-25</date>
              <period xmlns:ns1="http://www.w3.org/2001/XMLSchema-instance" xmlns="" ns1:nil="true" />
              <min xmlns:ns1="http://www.w3.org/2001/XMLSchema-instance" xmlns="" ns1:nil="true" />
              <average xmlns:ns1="http://www.w3.org/2001/XMLSchema-instance" xmlns="" ns1:nil="true" />
              <max xmlns:ns1="http://www.w3.org/2001/XMLSchema-instance" xmlns="" ns1:nil="true" />
              <ultimo xmlns:ns1="http://www.w3.org/2001/XMLSchema-instance" xmlns="" ns1:nil="true" />
              <value xmlns="">1.08823E1</value>
            </resultrows>
          </series>
        </groups>
      </return>
    </ns0:getInterestAndExchangeRatesResponse>
  </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

Итак, окончательный вариант Результатом являются все эти данные в одном столбце и одной строке. Я пытался извлечь соответствующие данные из этого XML. Я заинтересован в Seriesid и его стоимости. Таким образом, конечный результат будет:

seriesid | value
SEKEURPMI| 1.08823E1

, и в конечном итоге их будет несколько, поэтому результаты будут выглядеть примерно так:

seriesid | value
SEKEURPMI| 1.08823E1
SEKUSDPMI| 1.3823E1
....     | ...

Однако у меня возникают проблемы при создании действительного запроса для получения эта информация. Ни одно из решений, с которыми я пытался работать, не работает, потому что я делаю запросы прямо из таблицы. Я пытался реализовать это решение: Как я могу сохранить данные с xml до sql 2008? , но я не могу применить ту же технику при запросах из таблицы .

Ответы [ 2 ]

1 голос
/ 26 марта 2020

Попробуйте, как это:

DECLARE @yourTable TABLE(ID INT IDENTITY,Val XML);
INSERT INTO @yourTable VALUES
(N'<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://www.w3.org/2003/05/soap-envelope">
  <SOAP-ENV:Body>
    <ns0:getInterestAndExchangeRatesResponse xmlns:ns0="http://swea.riksbank.se/xsd">
      <return xmlns="">
        <datefrom xmlns="">2020-03-25</datefrom>
        <dateto xmlns="">2020-03-26</dateto>
        <groups xmlns="">
          <groupid xmlns="">130</groupid>
          <groupname xmlns="">Currencies against Swedish kronor</groupname>
          <series xmlns="">
            <seriesid xmlns="">SEKEURPMI</seriesid>
            <seriesname xmlns="">1 EUR</seriesname>
            <unit xmlns="">1.0E0</unit>
            <resultrows xmlns="">
              <date xmlns="">2020-03-25</date>
              <period xmlns:ns1="http://www.w3.org/2001/XMLSchema-instance" xmlns="" ns1:nil="true" />
              <min xmlns:ns1="http://www.w3.org/2001/XMLSchema-instance" xmlns="" ns1:nil="true" />
              <average xmlns:ns1="http://www.w3.org/2001/XMLSchema-instance" xmlns="" ns1:nil="true" />
              <max xmlns:ns1="http://www.w3.org/2001/XMLSchema-instance" xmlns="" ns1:nil="true" />
              <ultimo xmlns:ns1="http://www.w3.org/2001/XMLSchema-instance" xmlns="" ns1:nil="true" />
              <value xmlns="">1.08823E1</value>
            </resultrows>
          </series>
        </groups>
      </return>
    </ns0:getInterestAndExchangeRatesResponse>
  </SOAP-ENV:Body>
</SOAP-ENV:Envelope>');

- запрос

SELECT t.ID
      ,A.gr.value('(groupid/text())[1]','int') AS groupId
      ,A.gr.value('(groupname/text())[1]','nvarchar(max)') AS groupName
      ,B.sr.value('(seriesid/text())[1]','nvarchar(max)') AS seriesId
      ,B.sr.value('(seriesname/text())[1]','nvarchar(max)') AS seriesName
      ,C.rw.value('(date/text())[1]','date') AS rowDate
      ,C.rw.value('(period/text())[1]','nvarchar(max)') AS rowPeriod
      ,C.rw.value('(value/text())[1]','float') AS rowValue
FROM @yourTable t
CROSS APPLY t.Val.nodes('//groups') A(gr)
OUTER APPLY A.gr.nodes('series') B(sr)
OUTER APPLY B.sr.nodes('resultrows') C(rw);

Идея вкратце и немного фона:

  • Форма множественного числа <groups>, <series> и <resultrows> позволяют мне думать, что это вложенные, 1:n связанные данные, следовательно, каскад из APPLY ...nods().
  • This XML есть беспорядок неправильных пространств имен ...
  • Было бы ужасно уродливо правильно определять / объявлять все пространства имен ...
  • Радостно, что значения живут в пустом пространстве имен по умолчанию.
  • Чтобы избежать пространств имен, я начинаю с //groups. Двойной // вызывает глубокий поиск. Не следуйте этому предложению, если элемент <groups> может произойти где-либо еще!
  • Мы получаем все группы
  • Мы получаем все серии в каждой группе
  • Мы получаем все строки в каждом ser ie

Подсказка: о ns1:nil="true". Предполагается, что это было сгенерировано с T- SQL с использованием ELEMENTS XSINIL. XML по умолчанию будет пропускать значения NULL. Это означает, что читатель должен знать схему. Запрос о пропущенном элементе вернет NULL. но в этом случае все элементы генерируются. Мы можем проверить, является ли атрибут nil true. Но в этом случае проще проверить узел text(). Это отсутствует и будет неявно возвращать NULL.

Некоторые замечания о пространствах имен:

  • Пространства имен для именования SOAP связанных частей в порядке, пространство имен для указания элемента <getInterestAndExchangeRatesResponse> тоже хорошо.
  • Но часто используемый xmlns="" действительно странный. Это снова и снова будет определять пустое пространство имен по умолчанию ...
  • Я предполагаю, что оно генерируется с помощью T- SQL с использованием вложенных вызовов FOR XML и путем помещения такого предварительно сгенерированного содержимого XML в SOAP конверт.
1 голос
/ 26 марта 2020

Это то, что вы ищете? Объявление пространств имен действительно важно здесь:

DECLARE @XML xml = '<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://www.w3.org/2003/05/soap-envelope">
  <SOAP-ENV:Body>
    <ns0:getInterestAndExchangeRatesResponse xmlns:ns0="http://swea.riksbank.se/xsd">
      <return xmlns="">
        <datefrom xmlns="">2020-03-25</datefrom>
        <dateto xmlns="">2020-03-26</dateto>
        <groups xmlns="">
          <groupid xmlns="">130</groupid>
          <groupname xmlns="">Currencies against Swedish kronor</groupname>
          <series xmlns="">
            <seriesid xmlns="">SEKEURPMI</seriesid>
            <seriesname xmlns="">1 EUR</seriesname>
            <unit xmlns="">1.0E0</unit>
            <resultrows xmlns="">
              <date xmlns="">2020-03-25</date>
              <period xmlns:ns1="http://www.w3.org/2001/XMLSchema-instance" xmlns="" ns1:nil="true" />
              <min xmlns:ns1="http://www.w3.org/2001/XMLSchema-instance" xmlns="" ns1:nil="true" />
              <average xmlns:ns1="http://www.w3.org/2001/XMLSchema-instance" xmlns="" ns1:nil="true" />
              <max xmlns:ns1="http://www.w3.org/2001/XMLSchema-instance" xmlns="" ns1:nil="true" />
              <ultimo xmlns:ns1="http://www.w3.org/2001/XMLSchema-instance" xmlns="" ns1:nil="true" />
              <value xmlns="">1.08823E1</value>
            </resultrows>
          </series>
        </groups>
      </return>
    </ns0:getInterestAndExchangeRatesResponse>
  </SOAP-ENV:Body>
</SOAP-ENV:Envelope>';

WITH XMLNAMESPACES ('http://www.w3.org/2003/05/soap-envelope' AS [SOAP-ENV],'http://swea.riksbank.se/xsd' AS ns0, 'http://www.w3.org/2001/XMLSchema-instance' AS ns1)
SELECT V.X.value('(SOAP-ENV:Envelope/SOAP-ENV:Body/ns0:getInterestAndExchangeRatesResponse/return/groups/series/seriesid/text())[1]','varchar(10)') AS seriesid,
       V.X.value('(SOAP-ENV:Envelope/SOAP-ENV:Body/ns0:getInterestAndExchangeRatesResponse/return/groups/series/resultrows/value/text())[1]','float') AS [value]
FROM (VALUES(@XML))V(X)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...