SQL Server:如何将这个xml解析成表列?

时间:2017-04-13 04:15:17

标签: sql-server xml

我正在尝试将xml解析为表数据。但是,我只得到一排。我需要做什么才能获得所有数据?我有一个link来证明这一点。

INSERT BATCHES (BatchID, RawXML)
VALUES (1, '
    <ParamData>
        <moduleRole>
            <moduleId>1</moduleId>
            <bmRoleId>4</bmRoleId>
            <moduleId>2</moduleId>
            <bmRoleId>8</bmRoleId>
            <moduleId>3</moduleId>
            <bmRoleId>255</bmRoleId>
            <moduleId>8</moduleId>
            <bmRoleId>4</bmRoleId>
            <moduleId>16</moduleId>
            <bmRoleId>4</bmRoleId>
            <moduleId>64</moduleId>
            <bmRoleId>4</bmRoleId>
            <moduleId>128</moduleId>
            <bmRoleId>4</bmRoleId>
        </moduleRole>
    </ParamData>
');

SELECT  
    b.BatchID,
    x.XmlCol.value('(moduleId)[1]','INT') AS moduleId,
    x.XmlCol.value('(bmRoleId)[1]','INT') AS bmRoleId
FROM
    Batches b
CROSS APPLY 
    b.RawXml.nodes('/ParamData/moduleRole') x(XmlCol);

2 个答案:

答案 0 :(得分:3)

我认为要在moduleId上粉碎XML,然后选择当前元素和最近的兄弟bmRoleId元素:

SELECT  b.BatchID,
        x.XmlCol.value('.','INT') AS moduleId,
        x.XmlCol.value('following-sibling::bmRoleId)[1]','INT') AS bmRoleId
FROM    Batches b
CROSS APPLY b.RawXml.nodes('/ParamData/moduleRole/moduleId') x(XmlCol);

不幸的是,SQL Server不支持following-sibling轴,所以我们需要使用this post中提到的技巧:

SELECT  b.BatchID,
        x.XmlCol.value('.','INT') AS moduleId,
        x.XmlCol.value('let $c := . return (../bmRoleId[. >> $c])[1]','INT') AS bmRoleId
FROM    Batches b
CROSS APPLY b.RawXml.nodes('/ParamData/moduleRole/moduleId') x(XmlCol);

<强> sqlfiddle demo

此部分return (../bmRoleId[. >> $c])[1],获取位于当前bmRoleId之后的所有moduleId(由$c引用),然后将结果限制为第一个bmRoleId按文档顺序

答案 1 :(得分:0)

我宁愿用数字表来解决这个问题。在我的示例中,我将从master..spt_values创建一个 on-the-fly ,其中包含大约2.500个条目。这应该足够了。我们不需要值,只需返回ROW_NUMBER()

WITH Numbers AS(SELECT ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) AS Nr FROM master..spt_values)
SELECT b.BatchID
     ,b.RawXml.value(N'(/ParamData/moduleRole/moduleId)[sql:column("Nr")][1]','int') AS moduleId
     ,b.RawXml.value(N'(/ParamData/moduleRole/bmRoleId)[sql:column("Nr")][1]','int') AS bmRoleId
FROM Numbers
CROSS JOIN [BATCHES] AS b
WHERE b.RawXml.exist(N'(/ParamData/moduleRole/moduleId)[sql:column("Nr")]')=1;

结果

1   1   4
1   2   8
1   3   255
1   8   4
1   16  4
1   64  4
1   128 4

提示

如果性能很重要,那么在TOP之前使用ROW_NUMBERCROSS JOIN的数量限制为适当的数量会有所帮助......