【问题标题】:How to get the partial value of a XML Node value如何获取 XML 节点值的部分值
【发布时间】:2017-02-17 12:32:01
【问题描述】:

我是 Xpath 的新手,这是我的 XML。我正在尝试获取 appl/*__job 标记中的属性值 @name 和节点 snmp_notify/message 中的值 'TESTQUEUE ',我一次迈出一步。到目前为止,我能够获取所有_job的子节点,但无法获取节点/snmp_notifylist/snmp_notify/message中的值。这是 SQL,有人可以帮我确定我卡在哪里了。

这是在 TAB_AR 表中存储为 DEFINITION 的示例 XML 文档。

<appl xmlns="http://dto.wa.ca.com/application" name="TEST_NEW_AGENT">
   <version>12.0</version>
   <comment />
   <unix_job name="TEST_JOB">
     <dependencies><relcount>0</relcount></dependencies>
      <snmp_notifylist>
       <snmp_notify>
         <returncode>4</returncode>
           <monitor_states><monitor_state>FAILED</monitor_state></monitor_states>
           <snmpagent />
           <message>TICKET TESTQUEUE TSTMSG</message>
      </snmp_notify>
   </snmp_notifylist>
 </unix_job>
 <link name="HOLD_LINK">
    <dependencies><relcount>0</relcount></dependencies>
    <hold>true</hold>
   <job_ancestor_wait_default_ignore>true</job_ancestor_wait_default_ignore>
 </link>
 <sftp_job name="TEST_SFTP1">
    <dependencies><relcount>0</relcount></dependencies>
    <snmp_notifylist>
         <snmp_notify>
           <returncode>4</returncode>
             <monitor_states>
                 <monitor_state>FAILED</monitor_state>
             </monitor_states>
            <snmpagent />
            <message>TICKET MFG1AWA TSTMSG</message>
            </snmp_notify>
    </snmp_notifylist>
</sftp_job>
</appl>

这是我写的 SQL,

 SELECT  
 SFTP_Job_name = DEFT1.value('(@name)[1]','nvarchar(max)'),
 Server_Address = DEFT1.query('local-name(/*:snmp_notifylist/*:snmp_notify/*:message)')
 from (select CAST([DEFINITION] as XML) as DEFT from TAB_AR)TAB
 CROSS APPLY TAB.DEFT.nodes('/*:appl/*[fn:contains(local-name(),"_job")]') as XMLTAB1(DEFT1)

【问题讨论】:

    标签: sql-server xml xpath


    【解决方案1】:

    你很亲密……

    在这一行中,我不确定你真正想要得到什么:

    DEFT1.query('local-name(/*:snmp_notifylist/*:snmp_notify/*:message)')
    

    使用local-name(),您可以返回一个特定节点的名称。当您从多个以_job 结尾的节点读取数据时,返回您正在读取的元素的名称非常有意义。

    但是您告诉我们,您也在尝试阅读&lt;message&gt;。可能是,您将两个电话混合在一​​条线上?

    我稍微修改了你的代码:

    SELECT 
      SFTP_Job_name = DEFT1.value('(@name)[1]','nvarchar(max)')
     ,NodeName = DEFT1.value('local-name(.)','nvarchar(max)')
     ,Server_Address = DEFT1.value('(*:snmp_notifylist/*:snmp_notify/*:message)[1]','nvarchar(max)')
     from (select CAST([DEFINITION] as XML) as DEFT from TAB_AR)TAB
     CROSS APPLY TAB.DEFT.nodes('/*:appl/*[fn:contains(local-name(.),"_job")]') as XMLTAB1(DEFT1);
    

    返回

    SFTP_Job_name   NodeName    Server_Address
    TEST_SFTP1      sftp_job    TICKET MFG1AWA TSTMSG
    TEST_JOB        unix_job    TICKET TESTQUEUE TSTMSG
    

    就像 Roger Wolf 指出的那样,最好使用这样的指定命名空间进行读取:

     WITH XMLNAMESPACES (default 'http://dto.wa.ca.com/application')
     SELECT 
      SFTP_Job_name = DEFT1.value('(@name)[1]','nvarchar(max)')
     ,NodeName = DEFT1.value('local-name(.)','nvarchar(max)')
     ,Server_Address = DEFT1.value('(snmp_notifylist/snmp_notify/message)[1]','nvarchar(max)')
     from (select CAST([DEFINITION] as XML) as DEFT from TAB_AR)TAB
     CROSS APPLY TAB.DEFT.nodes('/appl/*[fn:contains(local-name(.),"_job")]') as XMLTAB1(DEFT1);
    

    一般规则是:尽可能具体!

    提示

    如果可以更改此设置,则应将 XML 存储在类型为 XML 的列中。 这种构造from (select CAST([DEFINITION] as XML) as DEFT from TAB_AR)TAB应该真的没有必要……

    可能是,您的专栏实际上是XML,而您只是不知道如何转移您在某处找到的代码以获得.nodes() 的正确语法?在这种情况下,试试这个:

    SELECT 
      SFTP_Job_name = DEFT1.value('(@name)[1]','nvarchar(max)')
     ,NodeName = DEFT1.value('local-name(.)','nvarchar(max)')
     ,Server_Address = DEFT1.value('(*:snmp_notifylist/*:snmp_notify/*:message)[1]','nvarchar(max)')
     from TAB_AR
     CROSS APPLY TAB_AR.[DEFINITION].nodes('/*:appl/*[fn:contains(local-name(.),"_job")]') as XMLTAB1(DEFT1);
    

    【讨论】:

    • 谢谢你!
    • 道歉..我刚刚做了..我记住了下次,非常感谢!!!!!!!!
    【解决方案2】:

    这似乎有效:

    with xmlnamespaces (default 'http://dto.wa.ca.com/application')
    select j.c.value('./@name', 'sysname') as [JobName],
        m.c.value('./text()[1]', 'varchar(max)') as [MessageText]
    from (
        select cast(t.[Definition] as xml) as [Deft] from tab_ar t
    ) sq
        cross apply sq.Deft.nodes('/appl/*[fn:contains(local-name(),"_job")]') j(c)
            cross apply j.c.nodes('./snmp_notifylist/snmp_notify/message') m(c);
    

    之后,用空格分割字符串并取中间部分应该是比较简单的。

    【讨论】:

    • 您好罗杰,感谢您的回复。但是我需要获取属性“名称”/appl/*_job。
    • 投票支持WITH XMLNAMESPACES。只是一个提示:它应该工作得更快一点,直接在j(c) 下方阅读&lt;message&gt;,不需要第二个CROSS APPLY...
    • @Shnugo,如果 OP 只需要这个单一元素 - 也许;但是,通常不止于此,apply 将是合理的。此外,我多次注意到SQL Server中各种XML方法的相对性能随引擎版本的不同而有很大差异,所以我什至没有尝试优化任何东西。
    猜你喜欢
    • 1970-01-01
    • 2013-03-19
    • 1970-01-01
    • 2016-10-09
    • 2013-08-12
    • 2012-07-05
    • 1970-01-01
    • 1970-01-01
    • 2013-05-09
    相关资源
    最近更新 更多