【问题标题】:Pull all of the child XML nodes in SQL Server拉取 SQL Server 中的所有子 XML 节点
【发布时间】:2017-06-10 12:55:52
【问题描述】:

我有一个存储 200 万个 XML 文件的 SQL Server 2014 数据库。 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 文件中的一个节点,那就是我遇到问题的地方。我需要提取所有 ORG_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 节点?谢谢

【问题讨论】:

    标签: sql-server xml stored-procedures


    【解决方案1】:

    更改您的 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
    • 然后返回任何匹配的PIS 元素的子PI 元素。

    按 cmets 更新

    完整的 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 元素),获取其子元素PI_NAME
    • /text() - 从 PI_NAME 元素中获取它的子文本元素(严格来说,这不是必需的,因为当拉回值并转换为 varchar 时,我们会得到相同的结果;但我喜欢明确)。
    • (...)[1] - 返回一个单例。即我们只想要返回 1 个值,即使当前 PI 下有多个 PI_NAME 元素。通过在我们的表达式周围加上括号,我们说的是“对于这个表达式返回的所有值”; [1] 表示取第一个结果(因为 XPATH 使用从 1 开始的索引,而不是像大多数其他语言那样从 0 开始)。

    使用变量过滤

    预测您的下一个问题;即“如何在运行时更改数字而不构建动态 SQL?”,答案是使用 sql:variable 函数:

    declare @DunningNumber int = 600044978 --9 digit code http://www.dnb.com/duns-number.html; so can easily hold in an int: https://docs.microsoft.com/en-us/sql/t-sql/data-types/int-bigint-smallint-and-tinyint-transact-sql
    
    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() = sql:variable("@DunningNumber")]/PI') AS R(nref)
    WHERE
        RECID = 1
    

    【讨论】:

    • 这很好用而且速度很快,但是如果我需要更改我的 select 语句,所以我将 PI 和 PI_ID 作为单独的值获取,例如 SELECT APPLICATION_ID, nref.value('.','varchar (max)') PI, nref.value('.','varchar(max)') PI_ID。我该怎么做?。我知道那不在最初的问题中,但我搞砸了
    • 不用担心;几乎与您已经拥有的完全一样,只是稍作调整:nref.value('(.\PI_ID\text())[1]','varchar(max)') TermId, nref.value('(.\Name\text())[1]','varchar(max)') Term
    • 是的,是的-这是我的下一个问题(微笑),但这里还有一个如何仅使用部分节点搜索 PI 名称的方法。比如节点是“Alberg, Albert J”我要搜索“ALBERG”
    • 非常感谢——这将帮助南卡罗来纳医科大学癌症中心的研究人员——再次感谢
    • 感谢您的帮助
    【解决方案2】:

    这里的技巧是首先使用多个 CROSS APPLY 子句在文档中获取多个级别。因此,首先,从根目录开始抓取所有“/PROJECTS/row”,然后使用每个“PIS/PI”的相对路径。

    像这样:

    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'
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2023-03-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-01-07
      • 2012-09-23
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多