XML已成为近来最热门的Web技术它是SQL Server 中的重要部分本文将综合七条SQL Server 中最重要的XML综合特性组成XML之七种兵器
兵器之一FOR XML
在SQL Server 中标准的TSQL SELECT语句包括FOR XML子句它以XML文档形式返回一个查询结果新的FOR XML子句有三种模式——RAWAUTO和EXPLICIT每个都能对XML文档格式提供附加标准的控制
下面首先介绍FOR XML的使用方法
为了从SQL Server提取XML格式的数据TSQL中加入了一个FOR XML命令在查询命令中使用FOR XML命令使得查询结果以XML格式出现FOR XML命令有三种模式RAWAUTO和EXPLICIT图所显示的SQL命令访问SQL Server提供的Pubs示例数据库有关Pubs数据库的更多信息请参见MSDN说明如果我们依次指定该SQL命令的模式为三种允许的模式之一就可以得到各种模式所支持的不同XML输出
SELECT storestor_id as Id stor_name as Name
saleord_num as OrderNosaleqty as Qty
FROM stores store inner join
sales sale on storestor_id = salestor_id
ORDER BY stor_name
FOR XML <模式>
该查询命令所生成的结果包含所有销售记录及其对应的商店结果以商店名称的字母升序排列查询的最后加上了FOR XML命令以及具体的模式比如FOR XML RAW
理想情况下SQL命令所生成的XML文档应具有如下结构
<Stores>
<Store Id=&single;&single; Name=&single;&single;>
</Sale OrderNo=&single;&single; Qty=&single;&single;>
</Store>
</Stores>
下面我们来看看具体的处理方法
RAW模式
下面是指定RAW模式时结果XML文档的一个片断
查询结果集中每一个记录包含唯一的元素<row>由于我们无法控制元素名字和文档结构因此这种模式不是很有用RAW模式所生成的文档结构与我们所希望的不符而且它的用途也非常有限
AUTO模式
下面是指定AUTO模式时结果文档的一个片断
可以看到<Stroe>和<Sale>两个元素是父子关系形成了我们所希望的层次结构这种节点关系由查询中表的声明次序决定后声明的表成为前声明表的孩子
我们可以看出查询命令所指定的别名决定了XML文档中的名字根据这一点我们可以控制XML文档元素属性的名字使得这些名字符合我们所要求的命名惯例
可见AUTO模式能够创建出我们所需要的XML文档不过它存在以下缺点
虽然可以得到层次结构但这种层次结构是线性的即每个父节点只能有一个子节点反之亦然
通过别名指定元素名字不太方便而且有时候会影响查询命令本身的可读性
无法在文档中同时生成元素和属性要么全部是元素(通过ELEMENTS关键词指定)要么全部是属性(默认) EXPLICIT模式解决了上述不足
EXPLICIT模式
EXPLICIT模式比较复杂我们将用另外一种方法来表达所显示的查询这种方法使得我们能够完全地控制查询所生成的XML文档首先我们将介绍如何改用EXPLICIT模式编写图所显示的查询然后看看这种方法如何赋予我们远远超过AUTO模式的能力
商店数据
SELECT as Tag
NULL as Parent
sstor_id as [store!!Id]
sstor_name as [store!!Name]
NULL as[sale!!OrderNo]
NULL as [sale!!Qty]
FROM stores s
UNION ALL
销售数据
SELECT
sstor_id
sstor_name
saord_num
saqty
FROM stores s sales sa
WHERE sstor_id = sastor_id
ORDER BY [store!!name]
FOR XML EXPLICIT
这个查询初看起来有点复杂其实它只是把不同的数据集(即这里的Store和Sale)分解到了独立的SELECT语句里然后再用UNION ALL操作符连结成一个查询
我们之所以要把查询写成上面的形式是为了让查询结果不仅包含XML文档所描述的数据而且还包含描述XML文档结构的元数据上述查询所生成的表称为Universal表sqlxmldll生成XML文档时需要这种格式Universal表对于编写代码的人来说是透明的但了解这个表还是很有意义的它将有助于代码的开发和调试下面是Universal表的一个例子
Tag Parent store!!id store!!name sale!!orderno sale!!qty
NULL Barnum&single;s NULL NULL
Barnum&single;s A
Barnum&single;s QA
NULL Bookbeat NULL NULL
Bookbeat LL
Universal表和EXPLICIT模式查询的元数据部分都以红色表示黑色表示数据比较查询和表就可以找出sqlxmldll生成XML文档所需要的元素我们来仔细地分析一下它们描述的是什么
Tag和Parent列是XML文档层次结构方面的信息我们可以认为每个SELECT语句代表了一个XML节点而Tag和Parent列让我们指定节点在文档层次结构中的位置如果在第二个SELECT语句中指定Tag为指定Parent为就表示为这些数据加上了一个值为的标签而这些数据的父亲是那些标签为的数据(即第一个SELECT语句)这就使得我们能够构造出<Store>和<Sale>之间的父子关系而且正如你可能猜想到的它使得我们可以生成任意合法的XML文档结构注意第一个SELECT命令的parent列设置成了NULL这表示<Store>元素处于最顶层的位置
以黑色表示的数据将成为节点的属性或元素例如Store_ID就通过列名提供了这方面的信息列名字中的!是分隔符总共可分成四项(四个参数)其中第四个参数是可选的这些参数描述的是
第一个参数描述该列所属元素的名字在这里是<Store>元素
第二个是标签编号它指定了该列信息在XML树形结构中所处位置
第三个参数指定XML文档内的属性或元素名字在这里名字指定为id
数据列默认被创建为参数所指定节点的属性即id将成为<Store>节点的属性如果要指定id是<Store>的一个子元素我们可以使用第四个可选的参数这个参数的一个作用就是让我们把该项指定为元素例如store!!id!element
由于使用了UNION ALL操作符来连结SELECT语句为了保证SQL查询的合法性所有SELECT语句的选择结果必须具有相同数量的列我们使用NULL关键词来补足SELECT语句从而避免了重复数据
通过EXPLICIT模式查询所生成的XML文档和通过AUTO模式生成的完全相同那么为什么要创建EXPLICIT模式查询呢?
假设现在有人要求在XML文档中包含商店的打折信息查看Pubs数据库我们得知每个商店都可以有到n范围内的折扣率因此一种合理的方法是在<Store>元素下面加上子元素<Discount>这样我们就得到如下XML文档结构
<STORES>
<STORE Id=&single;&single; Name=&single;&single;>
<DISCOUNT Type=&single;&single; LowQty=&single;&single; HighQty=&single;&single;>
<AMOUNT></AMOUNT>
</DISCOUNT>
<SALE OrdNo=&single;&single; Qty=&single;&single;>
</SALE>
</STORE>
</STORES>
这里的改动包括
要在<Sale>元素所在的层次增加一个XML元素<Discount>即<Discount>是<Stroe>的子元素
Amount嵌套在<Discount>里面但不应该是<Discount>元素的属性
在AUTO模式中是不可能实现这些改动的
下面是创建这个新XML文档的EXPLICIT模式查询
SELECT as Tag NULL as Parent
sstor_id as [Store!!id]
sstor_name as [Store!!name]
NULL as [Sale!!orderno]
NULL as [Sale!!ty]
NULL as [Discount!!type]
NULL as [Discount!!lowqty]
NULL as [Discount!!highqty]
NULL as [Discount!!amount!element]
FROM stores s
UNION ALL
SELECT
sstor_id
sstor_name
saord_num
saqty
NULL
NULL
NULL
NULL
FROM stores s sales sa
WHERE sstor_id = sastor_id
UNION ALL
SELECT
NULL
sstor_name
NULL
NULL
ddiscounttype
dlowqty
dhighqty
ddiscount
FROM stores s discounts d
WHERE sstor_id = dstor_id
ORDER BY [store!!name]
For XML EXPLICIT
为了创建图A所显示的EXPLICIT模式查询我们对图的查询进行了如下修改
增加了第三个子查询提取折扣数据通过Tag列声明这些数据的标签值为
通过指定Parent列为将折扣数据设置成<Store>元素的子元素