拉出SQL Server中的所有子XML节点

时间:2017-06-10 12:55:53

标签: sql-server xml stored-procedures

我有一个SQL Server 2014数据库,可存储200万个XML文件。 XML文件如下所示:

<?xml version='1.0' encoding='UTF-16'?>
<PROJECTS xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'>
    <row>
        <APPLICATION_ID>7975883</APPLICATION_ID>
        <ACTIVITY>N01</ACTIVITY>
        <ADMINISTERING_IC>HL</ADMINISTERING_IC>
        <APPLICATION_TYPE xsi:nil="true"/>
        <ARRA_FUNDED>N</ARRA_FUNDED>
        <PIS>
           <PI>
              <PI_NAME>MICHEL, MARY Q</PI_NAME>
              <PI_ID>3704353</PI_ID>
           </PI>
           <PI>
              <PI_NAME>SMITH, ROBERT B</PI_NAME>
              <PI_ID>3704354</PI_ID>
           </PI>
           <PI>
               <PI_NAME>DOE, JOHN A</PI_NAME>
               <PI_ID>3704353</PI_ID>
            </PI>
        </PIS>
        <ORG_DUNS>600044978</ORG_DUNS>
        <ORG_COUNTRY>UNITED STATES</ORG_COUNTRY>
        <ORG_DISTRICT>08</ORG_DISTRICT>
        <ORG_ZIPCODE>208523003</ORG_ZIPCODE>
    </row>
</PROJECTS> 

我的问题是我想根据存储过程中的ORG_DUNS数字提取所有PI值。所以我的代码是:

SELECT 
    APPLICATION_ID,     
    nref.value('.','varchar(max)') TERM 
INTO 
    ADMIN_MUSC_RePORTER_TERMS                   
FROM 
    [ADMIN_Grant_Exporter_Files_XML] 
CROSS APPLY  
    XMLData.nodes('//PIS/PI') AS R(nref)
WHERE
    RECID = 1

当我使用WHERE导致基于数据库中的另一个字段但是如果我需要在xml文件中引用我遇到问题的节点时工作正常。我需要提取OR​​G_DUNS等于600044978的所有XML文件,并且我知道由于交叉应用,nref.value('ORG_DUNS[1]', 'varchar(max)')不存在。

SELECT 
    APPLICATION_ID,     
    nref.value('.','varchar(max)') TERM 
INTO 
    ADMIN_MUSC_RePORTER_TERMS                   
FROM 
    [ADMIN_Grant_Exporter_Files_XML] 
CROSS APPLY
    XMLData.nodes('//PIS/PI') as R(nref)
WHERE
    nref.value('ORG_DUNS[1]', 'varchar(max)') = '600044978'

那么如何使用ORG_DUNS作为我的WHERE来获取所有PI节点?感谢

2 个答案:

答案 0 :(得分:2)

更改Cross Apply语句以在XPath中包含过滤器逻辑:

CROSS APPLY XMLData.nodes('//PIS[../ORG_DUNS/text() = ''600044978'']/PI') AS R(nref)

要解释一下,//PIS[../ORG_DUNS/text() = ''600044978'']/PI说:

  • //PIS - 找到所有名为PIS的元素
  • [ ... ] - 为符合此条件的人筛选返回的元素
  • ../ORG_DUNS/text() = ''600044978'' - 条件:PIS的父元素ORG_DUNS的文字值等于600044978
  • 然后返回任何匹配的PI元素的子PIS元素。

每条评论更新

完整SQL,包括PI和PI_ID作为单独的值:

SELECT 
    APPLICATION_ID     
    , nref.value('(./PI_NAME/text())[1]','varchar(max)') PI 
    , nref.value('(./PI_ID/text())[1]','varchar(max)') PI_ID
INTO 
    ADMIN_MUSC_RePORTER_TERMS                   
FROM 
    [ADMIN_Grant_Exporter_Files_XML] 
CROSS APPLY 
    XMLData.nodes('//PIS[../ORG_DUNS/text() = ''600044978'']/PI') AS R(nref)
WHERE
    RECID = 1

注意:

  • ./PI_NAME - 从当前选定的元素(即nref列引用的元素;指向PI元素),获取其子元素{{1 }}。
  • PI_NAME - 从/text()元素中获取其子文本元素(严格来说,这不是必需的,因为当拉回值并转换为varchar时,我们会得到相同的内容结果;但我喜欢明确)。
  • PI_NAME ... ( - 返回单身人士。即使当前)[1]下有多个PI_NAME元素,我们只需要返回1个值。通过在我们的表达式周围加上括号,我们对这个表达式返回的所有值&#34;&#34 ;;并且PI表示取第一个结果(因为XPATH使用基于一个的索引而不是基于零的索引,因为大多数其他语言会这样做。)

使用变量过滤

预测你的下一期; ie&#34;如何在不构建动态SQL的情况下在运行时更改数量?&#34;,使用sql:variable函数的答案:

[1]

答案 1 :(得分:1)

这里的技巧是首先使用多个CROSS APPLY子句在文档中抓取多个级别。首先,从根目录开始抓取所有&#39; / PROJECTS / row&#39;然后使用每个&#39; PIS / PI&#39;

中的相对路径

像这样:

declare @t table(id int identity, APPLICATION_ID int default (2), XmlData xml)

insert into @t(XmlData) values (
'<PROJECTS xmlns:xsi=''http://www.w3.org/2001/XMLSchema-instance''>
<row>
<APPLICATION_ID>7975883</APPLICATION_ID>
<ACTIVITY>N01</ACTIVITY>
<ADMINISTERING_IC>HL</ADMINISTERING_IC>
<APPLICATION_TYPE xsi:nil="true"/>
<ARRA_FUNDED>N</ARRA_FUNDED>
<PIS>
      <PI>
        <PI_NAME>MICHEL, MARY Q</PI_NAME>
        <PI_ID>3704353</PI_ID>
      </PI>
<PI>
        <PI_NAME>SMITH, ROBERT B</PI_NAME>
        <PI_ID>3704354</PI_ID>
      </PI>
<PI>
        <PI_NAME>DOE, JOHN A</PI_NAME>
        <PI_ID>3704353</PI_ID>
      </PI>

</PIS>
<ORG_DUNS>600044978</ORG_DUNS>
<ORG_COUNTRY>UNITED STATES</ORG_COUNTRY>
<ORG_DISTRICT>08</ORG_DISTRICT>
<ORG_ZIPCODE>208523003</ORG_ZIPCODE>
</row>
</PROJECTS> '),(
'<PROJECTS xmlns:xsi=''http://www.w3.org/2001/XMLSchema-instance''>
<row>
<APPLICATION_ID>7975883</APPLICATION_ID>
<ACTIVITY>N01</ACTIVITY>
<ADMINISTERING_IC>HL</ADMINISTERING_IC>
<APPLICATION_TYPE xsi:nil="true"/>
<ARRA_FUNDED>N</ARRA_FUNDED>
<PIS>
      <PI>
        <PI_NAME>MICHEL, MARY Q</PI_NAME>
        <PI_ID>3704353</PI_ID>
      </PI>
<PI>
        <PI_NAME>SMITH, ROBERT B</PI_NAME>
        <PI_ID>3704354</PI_ID>
      </PI>
<PI>
        <PI_NAME>DOE, JOHN A</PI_NAME>
        <PI_ID>3704353</PI_ID>
      </PI>

</PIS>
<ORG_DUNS>600044979</ORG_DUNS>
<ORG_COUNTRY>UNITED STATES</ORG_COUNTRY>
<ORG_DISTRICT>08</ORG_DISTRICT>
<ORG_ZIPCODE>208523003</ORG_ZIPCODE>
</row>
</PROJECTS> ')

select APPLICATION_ID,      
pinode.value('PI_NAME[1]','varchar(max)') PI_NAME,
pinode.value('PI_ID[1]','varchar(max)') PI_ID           
FROM @t 
cross apply XMLData.nodes('/PROJECTS/row') as r(rownode)
cross apply rownode.nodes('PIS/PI') as p(pinode)
where rownode.value('ORG_DUNS[1]','varchar(max)') = '600044978'