使用SQL检索XML属性值

时间:2016-10-05 17:30:39

标签: sql xml

我们说我有一个文档,其中包含相应的XML元数据文件,如下所示。此XML文件包含与文档有关的索引字段:

<Document>
    <Indices>
        <IndexField>
            <indexName>DOCID</indexName>
            <indexValue>49626502</indexValue>
        </IndexField>
        <IndexField>
            <indexName>EMPLOYEEID</indexName>
            <indexValue>248572405</indexValue>
        </IndexField>
        <IndexField>
            <indexName>LASTNAME</indexName>
            <indexValue>BROWN</indexValue>
        </IndexField>
        <IndexField>
            <indexName>FIRSTNAME</indexName>
            <indexValue>RALPH</indexValue>
        </IndexField>
        <IndexField>
            <indexName>CITY</indexName>
            <indexValue>PORTLAND</indexValue>
        </IndexField>
        <IndexField>
            <indexName>STATE</indexName>
            <indexValue>OR</indexValue>
        </IndexField>
    </Indices>
</Document>

我已将XML文件加载到SQL表中,然后我将提取属性值并将其加载到另一个表中。我有成千上万的这些文件。生成元数据文件的方式,如果源系统没有填充字段,让我们说CITY或STATE,则不会在文件中创建XML标记。我遇到的挑战是元数据文件缺乏一致性或一致性,因为一个索引值可能比另一个索引值更多(基于源系统中填充的内容与空白)。

以下是我如何提取要加载到不同表中的属性:

SELECT 

DOCID = CASE WHEN XMLDATA.exist('/Document/Indices/IndexField[indexName="DOCID"]') = 1 then XMLData.value('(//*[local-name()="indexValue"])[1]','varchar(max)') else NULL end,

EMPLOYEEID = CASE WHEN XMLDATA.exist('/Document/Indices/IndexField[indexName="EMPLOYEEID"]') = 1 then XMLData.value('(//*[local-name()="indexValue"])[2]','varchar(max)') else NULL end,

LASTNAME = CASE WHEN XMLDATA.exist('/Document/Indices/IndexField[indexName="LASTNAME"]') = 1 then XMLData.value('(//*[local-name()="indexValue"])[3]','varchar(max)') else NULL end,

FIRSTNAME = CASE WHEN XMLDATA.exist('/Document/Indices/IndexField[indexName="FIRSTNAME"]') = 1 then XMLData.value('(//*[local-name()="indexValue"])[4]','varchar(max)') else NULL end

对于每个字段,我首先运行它以确保它存在于XML文件中:

XMLDATA.exist(&#39; / Document / Indices / IndexField [indexName =&#34; DOCID&#34;]&#39;)= 1

然后,我为indexValue提取位置值:

XMLData.value(&#39;(// * [本地名称()=&#34; indexValue&#34])[1]&#39;,&#39; VARCHAR(最大)&#39;)

我遇到的问题是如果文件中缺少XML标记,它会抛出后续字段的位置indexValue。

我的问题是 - 基于提供的XML格式,我如何推断给定indexName的indexValue?

2 个答案:

答案 0 :(得分:0)

找到需要IndexField值的indexName并提取其indexValue

declare @x xml = '<Document>
    <Indices>
        <IndexField>
            <indexName>DOCID</indexName>
            <indexValue>49626502</indexValue>
        </IndexField>
        <IndexField>
            <indexName>EMPLOYEEID</indexName>
            <indexValue>248572405</indexValue>
        </IndexField>
        <IndexField>
            <indexName>LASTNAME</indexName>
            <indexValue>BROWN</indexValue>
        </IndexField>
        <IndexField>
            <indexName>FIRSTNAME</indexName>
            <indexValue>RALPH</indexValue>
        </IndexField>
        <IndexField>
            <indexName>CITY</indexName>
            <indexValue>PORTLAND</indexValue>
        </IndexField>
        <IndexField>
            <indexName>STATE</indexName>
            <indexValue>OR</indexValue>
        </IndexField>
    </Indices>
</Document>';

select DOCID=@x.value('(//IndexField[indexName[1]="DOCID"]/indexValue)[1]','varchar(max)')
 , NOFIELD=@x.value('(//IndexField[indexName[1]="NOFIELD"]/indexValue)[1]','varchar(max)')
 --, ..

答案 1 :(得分:0)

我的建议:与GROUP BYMAX()一起使用 old-fashionded-pivot 。缺少的值只会显示为NULL

CTE DervivedTable将首先使用行方式数据创建普通表。剩下的就是pivot

DECLARE @tbl TABLE(ID INT,XmlData XML);
INSERT INTO @tbl VALUES
(1,'<Document>
    <Indices>
        <IndexField>
            <indexName>DOCID</indexName>
            <indexValue>49626502</indexValue>
        </IndexField>
        <IndexField>
            <indexName>EMPLOYEEID</indexName>
            <indexValue>248572405</indexValue>
        </IndexField>
        <IndexField>
            <indexName>LASTNAME</indexName>
            <indexValue>BROWN</indexValue>
        </IndexField>
        <IndexField>
            <indexName>FIRSTNAME</indexName>
            <indexValue>RALPH</indexValue>
        </IndexField>
        <IndexField>
            <indexName>CITY</indexName>
            <indexValue>PORTLAND</indexValue>
        </IndexField>
        <IndexField>
            <indexName>STATE</indexName>
            <indexValue>OR</indexValue>
        </IndexField>
    </Indices>
</Document>')
,(2,'<Document>
    <Indices>
        <IndexField>
            <indexName>DOCID</indexName>
            <indexValue>2222 id</indexValue>
        </IndexField>
        <IndexField>
            <indexName>EMPLOYEEID</indexName>
            <indexValue>2222 emp</indexValue>
        </IndexField>
        <IndexField>
            <indexName>LASTNAME</indexName>
            <indexValue>222 last</indexValue>
        </IndexField>
        <IndexField>
            <indexName>FIRSTNAME</indexName>
            <indexValue>222 first</indexValue>
        </IndexField>
        <IndexField>
            <indexName>CITY</indexName>
            <indexValue>222 city</indexValue>
        </IndexField>
        <IndexField>
            <indexName>STATE</indexName>
            <indexValue>222 state</indexValue>
        </IndexField>
    </Indices>
</Document>');

- 查询

WITH DerivedTable AS
(
    SELECT ID
          ,f.value('indexName[1]','nvarchar(max)') AS indexName
          ,f.value('indexValue[1]','nvarchar(max)') AS indexValue
    FROM @tbl AS tbl
    CROSS APPLY tbl.XmlData.nodes('/Document/Indices/IndexField') AS A(f)
)
SELECT ID 
      ,MAX(CASE WHEN indexName='DOCID' THEN indexValue END) AS DOCID
      ,MAX(CASE WHEN indexName='EMPLOYEEID' THEN indexValue END) AS EMPLOYEEID
      ,MAX(CASE WHEN indexName='LASTNAME' THEN indexValue END) AS LASTNAME
      ,MAX(CASE WHEN indexName='FIRSTNAME' THEN indexValue END) AS FIRSTNAME
      ,MAX(CASE WHEN indexName='CITY' THEN indexValue END) AS CITY
      ,MAX(CASE WHEN indexName='STATE' THEN indexValue END) AS [STATE]
FROM DerivedTable
GROUP BY ID

结果

+----+----------+------------+----------+-----------+----------+-----------+
| ID | DOCID    | EMPLOYEEID | LASTNAME | FIRSTNAME | CITY     | STATE     |
+----+----------+------------+----------+-----------+----------+-----------+
| 1  | 49626502 | 248572405  | BROWN    | RALPH     | PORTLAND | OR        |
+----+----------+------------+----------+-----------+----------+-----------+
| 2  | 2222 id  | 2222 emp   | 222 last | 222 first | 222 city | 222 state |
+----+----------+------------+----------+-----------+----------+-----------+

更新

您可以使用普通PIVOT

达到相同的效果
WITH DerivedTable AS
(
    SELECT ID
          ,f.value('indexName[1]','nvarchar(max)') AS indexName
          ,f.value('indexValue[1]','nvarchar(max)') AS indexValue
    FROM @tbl AS tbl
    CROSS APPLY tbl.XmlData.nodes('/Document/Indices/IndexField') AS A(f)
)
SELECT p.*
FROM
(
    SELECT * FROM DerivedTable
) AS tbl
PIVOT
(
    MAX(indexValue) FOR indexName IN(DOCID,EMPLOYEEID,LASTNAME,FIRSTNAME,CITY,STATE)
) AS p