Преобразование вложенного XML с помощью XQuery в плоский XML - PullRequest
0 голосов
/ 24 сентября 2019

У меня есть вложенный фрагмент XML, который я хочу вставить в SQL.

Импорт XML:

 <RECORD>
    <RECID>118810</RECID>
    <FIELD TYPE="C">
       <NAME>proj_code</NAME>
       <VALUE>118810</VALUE>
    </FIELD>
    <FIELD TYPE="C">
       <NAME>sub_nr</NAME>
       <VALUE>99900</VALUE>
    </FIELD>
    <FIELD TYPE="C">
       <NAME>proj_desc</NAME>
       <VALUE>Nagekomen kosten Oktober 2018</VALUE>
    </FIELD>
    <FIELD TYPE="N">
       <NAME>pro_stat</NAME>
       <VALUE>9</VALUE>
    </FIELD>
    <FIELD TYPE="C">
       <NAME>comment</NAME>
       <VALUE></VALUE>
    </FIELD>
</RECORD>

Я хочу преобразовать его через XQuery в:

<RECORD>
    <RECID>118810</RECID>
    <proj_code>118810</proj_code>
    <sub_nr>99900</sub_nr>
    <proj_desc>Nagekomen kosten Oktober 2018</proj_desc>
    <pro_stat>9</pro_stat>
    <comment></comment>
</RECORD>

, чтобы я мог импортировать его в SQL.

Есть мысли?

Ответы [ 4 ]

0 голосов
/ 25 сентября 2019

Это окончательная версия с возможностью выбора для вставки в SQL:

DECLARE @xml XML = N'
<AVXML>
    <SIGNONMSGRS>
        <DTSERVER>2019-09-10T15:54:32</DTSERVER>
        <APPID>ACCOUNTVIEW</APPID>
        <APPVER>0908-</APPVER>
    </SIGNONMSGRS>  
   <EBUSMSGSRS>
      <EBUSQRYRS xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <RECORD>
        <RECID>118810</RECID>
        <FIELD TYPE="C">
            <NAME>proj_code</NAME>
            <VALUE>118810</VALUE>
        </FIELD>
        <FIELD TYPE="C">
            <NAME>sub_nr</NAME>
            <VALUE>99900</VALUE>
        </FIELD>
        <FIELD TYPE="C">
            <NAME>proj_desc</NAME>
            <VALUE>Nagekomen kosten Oktober 2018</VALUE>
        </FIELD>
        <FIELD TYPE="N">
            <NAME>pro_stat</NAME>
            <VALUE>9</VALUE>
        </FIELD>
        <FIELD TYPE="C">
            <NAME>comment</NAME>
            <VALUE></VALUE>
        </FIELD>
    </RECORD>
    <RECORD>
        <RECID>118811</RECID>
        <FIELD TYPE="C">
            <NAME>proj_code</NAME>
            <VALUE>118811</VALUE>
        </FIELD>
        <FIELD TYPE="C">
            <NAME>sub_nr</NAME>
            <VALUE>99901</VALUE>
        </FIELD>
        <FIELD TYPE="C">
            <NAME>proj_desc</NAME>
            <VALUE>Nagekomen kosten November 2019</VALUE>
        </FIELD>
        <FIELD TYPE="N">
            <NAME>pro_stat</NAME>
            <VALUE>19</VALUE>
        </FIELD>
        <FIELD TYPE="C">
            <NAME>comment</NAME>
            <VALUE>wow</VALUE>
        </FIELD>
    </RECORD>
      </EBUSQRYRS>
   </EBUSMSGSRS>
</AVXML>
';

DECLARE @tbl TABLE (ID INT IDENTITY(1,1) PRIMARY KEY, RECID VARCHAR(10), [col_name] VARCHAR(20), [col_value] VARCHAR(100));

INSERT INTO @tbl
SELECT c.value('(../RECID/text())[1]', 'VARCHAR(100)') AS [RECID] 
    , c.value('(NAME/text())[1]', 'VARCHAR(30)') AS [name]
    , c.value('(VALUE/text())[1]', 'VARCHAR(500)') AS [value]
FROM @xml.nodes('//RECORD/FIELD') AS t(c);

DECLARE @RowCount INT = (SELECT MAX(ID) FROM @tbl)
    , @recID varchar(10) = (SELECT TOP(1) RECID FROM @tbl WHERE ID = 1)
    , @xml_data VARCHAR(MAX) = '<root><RECORD>';

WHILE @RowCount > 0 BEGIN

    SELECT @xml_data += 
    IIF(@recID != RECID, 
       '<RECID>' + @recID + '</RECID>' +
       '</RECORD><RECORD>', 
       '') + 

        CONCAT('<',[col_Name],'>',[col_value],'</',[col_Name],'>')
        , @recID = RECID
    FROM @tbl 
    ORDER BY ID DESC OFFSET @RowCount - 1 ROWS FETCH NEXT 1 ROWS ONLY;

    SET @RowCount -= 1;
END;

SET @xml_data += '<RECID>' + @recID + '</RECID>' + '</RECORD></root>';

DECLARE @handler int;

exec sys.sp_xml_preparedocument @handler OUTPUT, @xml_data;
--print @xml_data

select *
from OPENXML(@handler,'/root/RECORD',11)
WITH
(   
    id nvarchar(50) 'RECID',
    proj_code nvarchar(50) 'proj_code',
    sub_nr nvarchar(50) 'sub_nr',
    proj_desc nvarchar(1000) 'proj_desc',
    pro_stat nvarchar(50) 'pro_stat',
    comment nvarchar(50) 'comment'
)
exec sys.sp_xml_removedocument @handler
0 голосов
/ 24 сентября 2019

Я попробовал это:

DECLARE @xml xml;

SET @xml =N'

    <RECORD>
        <RECID>118810</RECID>
       <FIELD TYPE="C">
          <NAME>proj_code</NAME>
          <VALUE>118810</VALUE>
       </FIELD>
       <FIELD TYPE="C">
          <NAME>sub_nr</NAME>
          <VALUE>99900</VALUE>
       </FIELD>
       <FIELD TYPE="C">
          <NAME>proj_desc</NAME>
          <VALUE>Nagekomen kosten Oktober 2018</VALUE>
       </FIELD>
       <FIELD TYPE="N">
          <NAME>pro_stat</NAME>
          <VALUE>9</VALUE>
       </FIELD>
       <FIELD TYPE="C">
          <NAME>comment</NAME>
          <VALUE></VALUE>
       </FIELD>
    </RECORD>
    <RECORD>
       <RECID>118811</RECID>
       <FIELD TYPE="C">
          <NAME>proj_code</NAME>
          <VALUE>118811</VALUE>
       </FIELD>
       <FIELD TYPE="C">
          <NAME>sub_nr</NAME>
          <VALUE>99900</VALUE>
       </FIELD>
       <FIELD TYPE="C">
          <NAME>proj_desc</NAME>
          <VALUE>Nagekomen kosten November 2018</VALUE>
       </FIELD>
       <FIELD TYPE="N">
          <NAME>pro_stat</NAME>
          <VALUE>9</VALUE>
       </FIELD>
       <FIELD TYPE="C">
          <NAME>comment</NAME>
          <VALUE></VALUE>
       </FIELD>
    </RECORD>
    ';

SELECT @xml.query('
    for $record in //RECORD
    return
    <RECORD>
    {
    $record/RECID,
    for $field in $record/FIELD 
       return element { $field/NAME } { data($field/VALUE) }
    }    
    </RECORD>
' ) as result

Но я получаю сообщение об ошибке возвращаемый элемент part: XQuery [query ()]: для константы поддерживаются только константные выраженияВыражение имени вычисляемого элемента и конструкторов атрибутов.

0 голосов
/ 25 сентября 2019

Как отметил Мартин Хоннен, MS SQL Server XQuery не поддерживает вычисляемые динамические имена элементов, только литералы.К сожалению, включая даже самый последний SQL Server 2019. Вот уродливое решение.

SQL

DECLARE @xml XML = N'<root>
    <RECORD>
        <RECID>118810</RECID>
        <FIELD TYPE="C">
            <NAME>proj_code</NAME>
            <VALUE>118810</VALUE>
        </FIELD>
        <FIELD TYPE="C">
            <NAME>sub_nr</NAME>
            <VALUE>99900</VALUE>
        </FIELD>
        <FIELD TYPE="C">
            <NAME>proj_desc</NAME>
            <VALUE>Nagekomen kosten Oktober 2018</VALUE>
        </FIELD>
        <FIELD TYPE="N">
            <NAME>pro_stat</NAME>
            <VALUE>9</VALUE>
        </FIELD>
        <FIELD TYPE="C">
            <NAME>comment</NAME>
            <VALUE></VALUE>
        </FIELD>
    </RECORD>
    <RECORD>
        <RECID>118811</RECID>
        <FIELD TYPE="C">
            <NAME>proj_code</NAME>
            <VALUE>118811</VALUE>
        </FIELD>
        <FIELD TYPE="C">
            <NAME>sub_nr</NAME>
            <VALUE>99901</VALUE>
        </FIELD>
        <FIELD TYPE="C">
            <NAME>proj_desc</NAME>
            <VALUE>Nagekomen kosten November 2019</VALUE>
        </FIELD>
        <FIELD TYPE="N">
            <NAME>pro_stat</NAME>
            <VALUE>19</VALUE>
        </FIELD>
        <FIELD TYPE="C">
            <NAME>comment</NAME>
            <VALUE>wow</VALUE>
        </FIELD>
    </RECORD>
</root>';

DECLARE @tbl TABLE (ID INT IDENTITY(1,1) PRIMARY KEY, RECID VARCHAR(10), [col_name] VARCHAR(20), [col_value] VARCHAR(100));

INSERT INTO @tbl
SELECT c.value('(../RECID/text())[1]', 'VARCHAR(100)') AS [RECID] 
    , c.value('(NAME/text())[1]', 'VARCHAR(30)') AS [name]
    , c.value('(VALUE/text())[1]', 'VARCHAR(100)') AS [value]
FROM @xml.nodes('root/RECORD/FIELD') AS t(c);

DECLARE @RowCount INT = (SELECT MAX(ID) FROM @tbl)
    , @recID varchar(10) = (SELECT TOP(1) RECID FROM @tbl WHERE ID = 1)
    , @xml_data VARCHAR(MAX) = '<root><RECORD>';

WHILE @RowCount > 0 BEGIN
    SELECT @xml_data += IIF(@recID != RECID, '</RECORD><RECORD>', '') + 
        --'<' + [col_Name] + '>' + COALESCE([col_value],'') + '</' + [col_Name] + '>'
        CONCAT('<',[col_Name],'>',[col_value],'</',[col_Name],'>')
        , @recID = RECID
    FROM @tbl 
    ORDER BY ID DESC OFFSET @RowCount - 1 ROWS FETCH NEXT 1 ROWS ONLY;

    SET @RowCount -= 1;
END;

SET @xml_data += '</RECORD></root>';
SELECT CAST(@xml_data AS XML);
0 голосов
/ 24 сентября 2019

Просто сопоставьте каждый RECORD с новым, где вы сопоставляете все FIELD s с элементами с именем из NAME и значением из VALUE:

RECORD !
<RECORD>
{
    RECID,
    FIELD ! element { NAME } { data(VALUE) }
}    
</RECORD>

https://xqueryfiddle.liberty -development.net/nbUY4kB

Для XQuery 1 вам нужно будет использовать for return вместо оператора карты !:

for $record in //RECORD
return
<RECORD>
{
    $record/RECID,
    for $field in $record/FIELD 
    return element { $field/NAME } { data($field/VALUE) }
}    
</RECORD>

https://xqueryfiddle.liberty -development.net/nbUY4kB/1

...