如何获取XML节点值的部分值

时间:2017-02-17 12:32:02

标签: sql-server xml xpath

我是Xpath的新手,这是我的XML。我试图在appl / * __ job标记中获取属性值@name,并在节点snmp_notify / message中获取值' TESTQUEUE'并且我在一个步骤中迈出了一步时间。截至目前,我能够获取所有_job的子节点,但我无法获取node / 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)

2 个答案:

答案 0 :(得分:1)

这似乎有效:

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);

之后,用空格分割字符串并取中间部分应该是相对微不足道的。

答案 1 :(得分:1)

你很亲密......

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

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

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

但是你告诉我们,你也试图阅读<message>。可能是,你在一行中混合两个电话?

我稍微修改了你的代码:

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);