第四篇旨在对路径表达式中的步骤(轴步骤 、一般步骤)详细说明。
-- 本文来自happyflystone -CSDN博客,
-- 转载请标明出处:http://blog.csdn.net/happyflystone
------------------------------------------------------------------------
1、 先说说轴
轴定义移动的方向,从上下文节点开始,导航到在轴指定的方向上可访问的那些节点。用轴步骤得用到上面一节说的全文、缩写语法及绝对、相对路径。此外假设你对此已经有了解。
下面我们介绍一下2005下的XML支持哪些轴:
Axis 说明
child 返回上下文节点的子级
descendant 返回上下文节点的所有后代
parent 返回上下文节点的父级
attribute 返回上下文节点的属性
self 返回上下文节点本身
descendant-or-self 返回上下文节点及其所有后代
很显然,我们可以想像一下这些轴里除了parent 轴是向反方向的,其它全是正向,所以我们又称除了parent以外的轴是正向轴,而parent 轴是反向轴,其实我们简单点理解一下就是一个是正向向下搜索,一个是反向向上搜索。
好,下面还是以例子来说明问题:
A、下面演示用child轴操作一下查询(注意下面查询语句包括了绝对与相对路径表达式)
declare @xml xml
set @xml = '<address>
<exam>
<name> flystone1 </name>
<street> 1401 Main Street </street>
<city state="NC">Anytown</city>
<postal-code> 210000 </postal-code>
</exam>
<exam>
<name> flystone2 </name>
<street> 1402 Main Street </street>
<city state="NM">Anytown</city>
<postal-code> 210000 </postal-code>
</exam>
</address>
'
select @xml.query('/child::address/child::exam')
select @xml.query('//exam')
select @xml.query('/address/exam')
select @xml.query('address/exam')
select @xml.query('child::address/child::exam')
/*
--------------------------------------------------------------------
<exam><name> flystone1 </name><street> 1401 Main Street </street><city state="NC">Anytown</city><postal-code> 210000 </postal-code></exam><exam><name> flystone2 </name><street> 1402 Main Street </street><city state="NM">Anytown</city><postal-code>
(1 行受影响)
*/
B、下面看看descendant轴、descendant-or-self轴,其实后者只是比前多了上下文节点,大家看看例子就知道
declare @xml xml
set @xml = '<address>
<exam>
<name>
<firstname>fly</firstname>
<lastname>stone1</lastname>
</name>
<street> 1401 Main Street </street>
<city state="NC">Anytown</city>
<postal-code> 210000 </postal-code>
</exam>
<exam>
<name>
<firstname>fly</firstname>
<lastname>stone2</lastname>
</name>
<street> 1402 Main Street </street>
<city state="NM">Anytown</city>
<postal-code> 210000 </postal-code>
</exam>
</address>
'
select @xml.query('/child::address/child::exam/child::name/descendant::*')
select @xml.query('//./address/exam/name/descendant::*')
select @xml.query('/./address/exam/name/descendant::*')
select @xml.query('./address/exam/name/descendant::*')
select @xml.query('/address/exam/name/descendant::*')
select @xml.query('address/exam/name/descendant::*')
/*
-----------------------------------------
<firstname>fly</firstname><lastname>stone1</lastname><firstname>fly</firstname><lastname>stone2</lastname>
(1 行受影响)
*/
declare @xml xml
set @xml = '<address>
<exam>
<name>
<firstname>fly</firstname>
<lastname>stone1</lastname>
</name>
<street> 1401 Main Street </street>
<city state="NC">Anytown</city>
<postal-code> 210000 </postal-code>
</exam>
<exam>
<name>
<firstname>fly</firstname>
<lastname>stone2</lastname>
</name>
<street> 1402 Main Street </street>
<city state="NM">Anytown</city>
<postal-code> 210000 </postal-code>
</exam>
</address>
'
select @xml.query('/child::address/child::exam/child::name/descendant-or-self::*')
select @xml.query('//./address/exam/name/descendant-or-self::*')
select @xml.query('/./address/exam/name/descendant-or-self::*')
select @xml.query('./address/exam/name/descendant-or-self::*')
select @xml.query('/address/exam/name/descendant-or-self::*')
select @xml.query('address/exam/name/descendant-or-self::*')
/*
------------------------------------------------------------
<name><firstname>fly</firstname><lastname>stone1</lastname></name><firstname>fly</firstname><lastname>stone1</lastname><name><firstname>fly</firstname><lastname>stone2</lastname></name><firstname>fly</firstname><lastname>stone2</lastname>
(1 行受影响)
*/
C、下面看看parent轴的用法 :
declare @xml xml
set @xml = '<address>
<exam>
<name>
<firstname>fly</firstname>
<lastname>stone1</lastname>
</name>
<street> 1401 Main Street </street>
<city state="NC">Anytown</city>
<postal-code> 210000 </postal-code>
</exam>
<exam>
<name>
<firstname>fly</firstname>
<lastname>stone2</lastname>
</name>
<street> 1402 Main Street </street>
<city state="NM">Anytown</city>
<postal-code> 210000 </postal-code>
</exam>
</address>
'
select @xml.query('/child::address/child::exam/child::name/parent::exam')
select @xml.query('//name/parent::exam')
--先把上下文定位到name节点然后再找父节点
/*
------------------------------------------------------------------
<exam><name><firstname>fly</firstname><lastname>stone1</lastname></name><street> 1401 Main Street </street><city state="NC">Anytown</city><postal-code> 210000 </postal-code></exam><exam><name><firstname>fly</firstname><lastname>stone2</lastname></name><street> 1402 Main Street </street><city state="NM">Anytown</city><postal-code> 210000 </postal-code></exam>
(1 行受影响)
*/
在SQL SERVER2005看到一个有意思的东东,我们来试试(里面涉及FLWOR 语句大家先跳过,以后我们再细说FLWOR)
declare @xml xml
set @xml = '
<address>
<exam id="1">
<name>
<firstname>fly</firstname>
<lastname>stone1</lastname>
</name>
<street> 1401 Main Street </street>
<city state="NC">Anytown</city>
<postal-code> 210000 </postal-code>
</exam>
<exam id="2">
<name>
<firstname>fly</firstname>
<lastname>stone2</lastname>
</name>
<street> 1402 Main Street </street>
<city state="NM">Anytown</city>
<postal-code> 210000 </postal-code>
</exam>
</address>
'
select @xml.query('
for $f in /child::address/child::exam/name/child::*
return
<name
id="{($f/parent::name/parent::exam/attribute::id)[1]}" >
{ $f }
</name>
')
select @xml.query('
for $f in //name/child::*
return
<name
id="{($f/parent::name/parent::exam/attribute::id)[1]}" >
{ $f }
</name>
')
/*
------------------------------------------------------------------
<name id="1">
<firstname>fly</firstname>
</name>
<name id="1">
<lastname>stone1</lastname>
</name>
<name id="2">
<firstname>fly</firstname>
</name>
<name id="2">
<lastname>stone2</lastname>
</name>
(1 行受影响)
*/
请注意,在路径表达式中添加了谓词 [1]
,这是为了确保返回单一值。因为我们这样测试也没问题:
select @xml.query('
for $f in //name/child::*
return
<name
id="{($f/parent::name/parent::exam/attribute::id)}" >
{ $f }
</name>
')
2、 节点测试
节点测试是一个条件,在步骤中选定的所有节点都必须满节点名测试或节点类型测试。节点名称很好理解,就是<>内的东东,下面先说说节点类型:
节点类型 | 返回 | 示例 |
| 如果是注释节点,则返回 True。 |
|
| 无论是任何类型的节点,都返回 True。 |
|
| 如果是处理指令节点,则返回 True。 |
|
| 如果是文本节点,则返回 True。 |
|
在节点名作测试时要注意,可以使用通配符 (*) 来指定节点的本地名称或作为其命名空间前缀。
举一个简单的例子:
declare @x xml
set @x = '
<address xmlns="ns1">
<salutation>hello</salutation>
</address>
<address xmlns="ns2">
<salutation>welcome</salutation>
</address>
<exam xmlns="ns1" >
<salutation>you</salutation>
</exam>'
--注意这两个结果集
select @x.query('//*:address')—-主要匹配节点本地名称
select @x.query('declare namespace ns="ns1"; /ns:*')—主要匹配命名空间
/*
<address xmlns="ns1">
<salutation>hello</salutation>
</address>
<address xmlns="ns2">
<salutation>welcome</salutation>
</address>
(1 行受影响)
<address xmlns="ns1">
<salutation>hello</salutation>
</address>
<exam xmlns="ns1">
<salutation>you</salutation>
</exam>
(1 行受影响)
*/
以节名为测试的例子上说得太多了,下面重点说一下节点类型,比如node()是显示在上下文节点之前的所有节点,那么我们在上面介绍的通配符* ,下面我先来例子:
declare @xml xml
set @xml = '
<address>
<exam pid ="d" id="1">
<name>
<firstname>fly</firstname>
<lastname>stone1</lastname>
</name>
<street> 1401 Main Street </street>
<city state="NC">Anytown</city>
<postal-code> 210000 </postal-code>
</exam>
<exam id="2">
<name>
<firstname>fly</firstname>
<lastname>stone2</lastname>
</name>
<street> 1402 Main Street </street>
<city state="NM">Anytown</city>
<postal-code> 210000 </postal-code>
</exam>
</address>
'
select @xml.query('
for $f in /child::address/child::exam/name/child::node()
return
<name
id="{($f/parent::name/parent::exam/attribute::id)}" >
{ $f }
</name>
')
/*
--------------------------------------------------------------------
<name id="1">
<firstname>fly</firstname>
</name>
<name id="1">
<lastname>stone1</lastname>
</name>
<name id="2">
<firstname>fly</firstname>
</name>
<name id="2">
<lastname>stone2</lastname>
</name>
(1 行受影响)
*/
用node()取代*达到和上面一样的效果。
下面再来试试text()函数:
declare @xml xml
set @xml = '
<address>
<exam pid ="d" id="1">
<name>
<firstname>fly</firstname>
<lastname>stone1</lastname>
</name>
<street> 1401 Main Street </street>
<city state="NC">Anytown</city>
<postal-code> 210000 </postal-code>
</exam>
<exam id="2">
<name>
<firstname>fly</firstname>
<lastname>stone2</lastname>
</name>
<street> 1402 Main Street </street>
<city state="NM">Anytown</city>
<postal-code> 210000 </postal-code>
</exam>
</address>
'
select @xml.query('/child::address/child::exam/name/child::firstname/child::text()')
select @xml.query('//name/lastname/child::text()')
/*
---------------
flyfly
(1 行受影响)
-------------
stone1stone2
(1 行受影响)
*/
最后对另两个函数分别举一个例子来说:
declare @xml xml
set @xml = '
<?xml-stylesheet href="flystonefriend.xsl" type="text/xsl"?>
<address>
<exam pid ="d" id="1"><!-- step1 -->
<name>
<firstname>fly</firstname>
<lastname>stone1</lastname>
</name>
<street> 1401 Main Street </street>
<city state="NC">Anytown</city>
<postal-code> 210000 </postal-code>
</exam>
<exam id="2"><!-- step2 -->
<name>
<firstname>fly</firstname>
<lastname>stone2</lastname>
</name>
<street> 1402 Main Street </street>
<city state="NM">Anytown</city>
<postal-code> 210000 </postal-code>
</exam>
</address>
'
select @xml.query('/child::address/child::exam/child::comment()')
select @xml.query('//exam/child::comment()')
/*
-------------------------------------------------------------------
<!-- step1 --><!-- step2 -->
(1 行受影响)
*/
select @xml.query('/child::processing-instruction()')
select @xml.query('child::processing-instruction()')
/*
------------------------------------------------------------------
<?xml-stylesheet href="flystonefriend.xsl" type="text/xsl"?>
(1 行受影响)
*/
3、 可选谓词
这一篇太长了,这个留到下回再说