通过XQuery将嵌套的XML转换为平面xml

时间:2019-09-24 12:46:32

标签: sql xml xquery

我有一个嵌套的XML,我想插入到SQL中。

导入XML:

.gitignore

我想通过XQuery将其转换为:

 <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>

因此我可以将其导入SQL。

有什么想法吗?

4 个答案:

答案 0 :(得分:0)

只需将每个RECORD映射到一个新的映射,即可将所有FIELD映射到名称从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

答案 1 :(得分:0)

我尝试过:

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

但是我在 return元素部分出现错误: XQuery [query()]:计算元素和属性构造函数的名称表达式仅支持常量表达式。

答案 2 :(得分:0)

正如Martin Honnen指出的那样,MS SQL Server XQuery不支持计算的 dynamic 元素名称,而仅支持文字。不幸的是,甚至包括最新的SQL Server2019。这是一个丑陋的解决方案。

  

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);

答案 3 :(得分:0)

这是最终版本,可以选择将其插入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