【问题标题】:Using SQL to retrieve XML attribute values使用 SQL 检索 XML 属性值
【发布时间】:2016-10-05 17:30:39
【问题描述】:

假设我有一个包含相应 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('/Document/Indices/IndexField[indexName="DOCID"]') = 1

然后,我为 indexValue 拉一个位置值:

XMLData.value('(//*[local-name()="indexValue"])[1]','varchar(max)')

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

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

【问题讨论】:

    标签: sql xml


    【解决方案1】:

    我的建议:old-fashionded-pivotGROUP BYMAX() 一起使用。缺失值将仅显示为 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
    

    【讨论】:

      【解决方案2】:

      找到具有所需indexName 值的IndexField 并提取其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)')
       --, ..
      

      【讨论】:

      • 如果将 XML 数据加载到变量中,这将起作用。您将如何修改它以针对 SQL 表工作?原始 XML 数据已加载到“XMLData”列中名为 Test 的表中 我试过这个:select DOCID = XMLDATA.value('(//IndexField[indexName[1]="DOCID"]/indexValue)[1]','varchar(max)') FROM TEST 并得到这个错误:XQuery [TEST.XMLData.value()]: 'value()' requires一个单例(或空序列),找到类型为 'xdt:untypedAtomic *' 的操作数
      • 用@Shnugo 的设置测试,运行正常。尽管如此,我同意他的解决方案应该有更好的性能。
      猜你喜欢
      • 2013-03-30
      • 2013-05-22
      • 2013-07-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-11-23
      相关资源
      最近更新 更多