数据库

本类阅读TOP10

·SQL语句导入导出大全
·SQL Server日期计算
·SQL语句导入导出大全
·SQL to Excel 的应用
·Oracle中password file的作用及说明
·MS SQLServer OLEDB分布式事务无法启动的一般解决方案
·sqlserver2000数据库置疑的解决方法
·一个比较实用的大数据量分页存储过程
·如何在正运行 SQL Server 7.0 的服务器之间传输登录和密码
·SQL中两台服务器间使用连接服务器

分类导航
VC语言Delphi
VB语言ASP
PerlJava
Script数据库
其他语言游戏开发
文件格式网站制作
软件工程.NET开发
SQL Server 2000 XML之七种兵器

作者:未知 来源:月光软件站 加入时间:2005-2-28 月光软件站

XML,已成为近来最热门的Web技术,它是SQL Server 2000中的重要部分。本文将综合七条SQL Server 2000中最重要的XML综合特性组成XML之七种兵器。

  兵器之一:FOR XML

  在SQL Server 2000中,标准的T-SQL SELECT语句包括FOR XML子句,它以XML文档形式返回一个查询结果。新的FOR XML子句有三种模式——RAW,AUTO,和EXPLICIT,每个都能对XML文档格式提供附加标准的控制。

  下面首先介绍“FOR XML”的使用方法。

  为了从SQL Server提取XML格式的数据,T-SQL中加入了一个FOR XML命令。在查询命令中使用FOR XML命令使得查询结果以XML格式出现。FOR XML命令有三种模式:RAW,AUTO和EXPLICIT。图1所显示的SQL命令访问SQL Server提供的Pubs示例数据库。有关Pubs数据库的更多信息,请参见MSDN说明。如果我们依次指定该SQL命令的模式为三种允许的模式之一,就可以得到各种模式所支持的不同XML输出。

【图1 】

SELECT store.stor_id as Id, stor_name as Name,

sale.ord_num as OrderNo,sale.qty as Qty

FROM stores store inner join

sales sale on store.stor_id = sale.stor_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>两个元素是父-子关系,形成了我们所希望的层次结构。这种节点关系由查询中表的声明次序决定,后声明的表成为前声明表的孩子。

  再参考图1,我们可以看出查询命令所指定的别名决定了XML文档中的名字。根据这一点,我们可以控制XML文档元素、属性的名字,使得这些名字符合我们所要求的命名惯例。

  可见AUTO模式能够创建出我们所需要的XML文档。不过它存在以下缺点:

  虽然可以得到层次结构,但这种层次结构是线性的,即每个父节点只能有一个子节点,反之亦然。

  通过别名指定元素名字不太方便,而且有时候会影响查询命令本身的可读性。

  无法在文档中同时生成元素和属性。要么全部是元素(通过ELEMENTS关键词指定),要么全部是属性(默认)。 EXPLICIT模式解决了上述不足。


 EXPLICIT模式

  EXPLICIT模式比较复杂,我们将用另外一种方法来表达图1所显示的查询。这种方法使得我们能够完全地控制查询所生成的XML文档。首先我们将介绍如何改用EXPLICIT模式编写图1所显示的查询,然后看看这种方法如何赋予我们远远超过AUTO模式的能力。

  下面是图1查询用EXPLICIT模式表达的代码:

【图2 】

--商店数据
SELECT 1 as Tag,
NULL as Parent,
s.stor_id as [store!1!Id],
s.stor_name as [store!1!Name],
NULL as[sale!2!OrderNo],
NULL as [sale!2!Qty]
FROM stores s
UNION ALL
-- 销售数据
SELECT 2, 1,
s.stor_id,
s.stor_name,
sa.ord_num,
sa.qty
FROM stores s, sales sa
WHERE s.stor_id = sa.stor_id
ORDER BY [store!1!name]
FOR XML EXPLICIT

  这个查询初看起来有点复杂,其实它只是把不同的数据集(即这里的Store和Sale)分解到了独立的SELECT语句里,然后再用UNION ALL操作符连结成一个查询。

  我们之所以要把查询写成上面的形式,是为了让查询结果不仅包含XML文档所描述的数据,而且还包含描述XML文档结构的元数据。上述查询所生成的表称为Universal表,sqlxml.dll生成XML文档时需要这种格式。Universal表对于编写代码的人来说是透明的,但了解这个表还是很有意义的,它将有助于代码的开发和调试。下面是Universal表的一个例子:

Tag Parent store!1!id store!1!name sale!2!orderno sale!2!qty
1 NULL 7066 Barnum&single;s NULL NULL
2 1 7066 Barnum&single;s A297650 50
2 1 7066 Barnum&single;s QA7442 375
1 NULL 8042 Bookbeat NULL NULL
2 1 8042 Bookbeat 423LL9 2215

  Universal表和EXPLICIT模式查询的元数据部分都以红色表示,黑色表示数据。比较查询和表就可以找出sqlxml.dll生成XML文档所需要的元素。我们来仔细地分析一下它们描述的是什么。

  Tag和Parent列是XML文档层次结构方面的信息,我们可以认为图2中的每个SELECT语句代表了一个XML节点,而Tag和Parent列让我们指定节点在文档层次结构中的位置。如果在第二个SELECT语句中指定Tag为2、指定Parent为1,就表示为这些数据加上了一个值为2的标签,而这些数据的父亲是那些标签为1的数据(即第一个SELECT语句)。这就使得我们能够构造出<Store>和<Sale>之间的父-子关系,而且正如你可能猜想到的,它使得我们可以生成任意合法的XML文档结构。注意第一个SELECT命令的parent列设置成了NULL,这表示<Store>元素处于最顶层的位置。

  以黑色表示的数据将成为节点的属性或元素,例如,Store_ID就通过列名提供了这方面的信息。列名字中的“!”是分隔符,总共可分成四项(四个参数),其中第四个参数是可选的。这些参数描述的是:

  第一个参数描述该列所属元素的名字,在这里是<Store>元素。

  第二个是标签编号,它指定了该列信息在XML树形结构中所处位置。

  第三个参数指定XML文档内的属性或元素名字。在这里名字指定为id。

  数据列默认被创建为参数2所指定节点的属性,即id将成为<Store>节点的属性。如果要指定id是<Store>的一个子元素,我们可以使用第四个可选的参数,这个参数的一个作用就是让我们把该项指定为元素,例如store!1!id!element。

  由于使用了UNION ALL操作符来连结SELECT语句,为了保证SQL查询的合法性,所有SELECT语句的选择结果必须具有相同数量的列。我们使用NULL关键词来补足SELECT语句,从而避免了重复数据。

  通过EXPLICIT模式查询所生成的XML文档和通过AUTO模式生成的完全相同,那么为什么要创建EXPLICIT模式查询呢?

  假设现在有人要求在XML文档中包含商店的打折信息。查看Pubs数据库,我们得知每个商店都可以有0到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模式查询:

【图 2A】

SELECT 1 as Tag, NULL as Parent,
s.stor_id as [Store!1!id],
s.stor_name as [Store!1!name],
NULL as [Sale!2!orderno],
NULL as [Sale!2!1ty],
NULL as [Discount!3!type],
NULL as [Discount!3!lowqty],
NULL as [Discount!3!highqty],
NULL as [Discount!3!amount!element]
FROM stores s
UNION ALL
SELECT 2, 1,
s.stor_id,
s.stor_name,
sa.ord_num,
sa.qty,
NULL,
NULL,
NULL,
NULL
FROM stores s, sales sa
WHERE s.stor_id = sa.stor_id
UNION ALL
SELECT 3, 1,
NULL,
s.stor_name,
NULL,
NULL,
d.discounttype,
d.lowqty,
d.highqty,
d.discount
FROM stores s, discounts d
WHERE s.stor_id = d.stor_id
ORDER BY [store!1!name]
For XML EXPLICIT

  为了创建图2A所显示的EXPLICIT模式查询,我们对图2的查询进行了如下修改:

  增加了第三个子查询提取折扣数据,通过Tag列声明这些数据的标签值为3。

  通过指定Parent列为1将折扣数据设置成<Store>元素的子元素。

  注意在第三个SELECT子查询中我们只包含了那些必需的列,并用NULL补足空列。这个子查询包含store_name列,虽然Discount元素并不要用到这个列,但如果把这个列也设置为NULL,则结果Universal表将不会按照解析器所要求的那样以节点升序排序(不妨自己试一下看看)。Universal表的排序列也可以用Tag列替代。

  为维持结果Universal表的完整性,第一、二两个SELECT语句也用NULL补足以反映为折扣数据增加的列。

  为指定新折扣列的元数据修改了第一个SELECT语句。

  通过在第四个参数中声明element指定Amount列为Discount的一个元素(element是可在第四个参数中声明的多个指令之一)。

  下面这个XML文档只能用EXPLICIT模式生成:

  结果XML文档中不会显示出NULL数据,如折扣lowqty和highqty。

  看来上面的介绍,大家可能已经对FOR XML的语法有所了解。通过FOR XML 我们在能够在Query Analyzer 中直接返回一个XML格式的数据或者通过其他多样化表现方式将XML格式的数据显示出来,比如可以将数据显示在浏览器上。下面这个例子就使用FOR XML和ADO将数据输出到浏览器的例子。

Dim adoConn
Set adoConn = Server.CreateObject("ADODB.Connection")

Dim sConn
sConn = "Provider=SQLOLEDB;Data Source=192.168.0.160;Initial Catalog=Northwind;User ID=SA;Password=;"
adoConn.ConnectionString = sConn
adoConn.CursorLocation = adUseClient
adoConn.Open

Dim adoCmd
Set adoCmd = Server.CreateObject("ADODB.Command")
Set adoCmd.ActiveConnection = adoConn

Dim sQuery
‘定义 FOR XML的查询。具体的语法在以后的章节中将详细介绍。
sQuery = "<ROOT xmlns:sql='urn:schemas-microsoft-com:xml-sql'><sql:query>SELECT * FROM PRODUCTS ORDER BY PRODUCTNAME FOR XML AUTO</sql:query></ROOT>"

‘建立ADODB Stream 对象,ADODB Stream 对象需要ADO2.5以上版本支持,它可以将记录集转换为数据流。
Dim adoStreamQuery
Set adoStreamQuery = Server.CreateObject("ADODB.Stream")
adoStreamQuery.Open
adoStreamQuery.WriteText sQuery, adWriteChar
adoStreamQuery.Position = 0
adoCmd.CommandStream = adoStreamQuery
adoCmd.Dialect = "{5D531CB2-E6Ed-11D2-B252-00C04F681B71}"

Response.write "Pushing XML to client for processing " & "<BR/>"

adoCmd.Properties("Output Stream") = Response
‘输出XML格式的文本
Response.write "<XML ID=MyDataIsle>"
adoCmd.Execute , , adExecuteStream
Response.write "</XML>"

‘通过IE的XML解析器,使用DOM方法将XML的文本转换为HTML
<SCRIPT language="VBScript" For="window" Event="onload">

Dim xmlDoc
Set xmlDoc = MyDataIsle.XMLDocument
xmlDoc.resolveExternals=false
xmlDoc.async=false

Dim root, child
Set root = xmlDoc.documentElement

For each child in root.childNodes
dim OutputXML
OutputXML = document.all("log").innerHTML
document.all("log").innerHTML = OutputXML & "<LI>" & child.getAttribute("ProductName") & "</LI>"
Next

</SCRIPT>

 EXPLICIT模式

  EXPLICIT模式比较复杂,我们将用另外一种方法来表达图1所显示的查询。这种方法使得我们能够完全地控制查询所生成的XML文档。首先我们将介绍如何改用EXPLICIT模式编写图1所显示的查询,然后看看这种方法如何赋予我们远远超过AUTO模式的能力。

  下面是图1查询用EXPLICIT模式表达的代码:

【图2 】

--商店数据
SELECT 1 as Tag,
NULL as Parent,
s.stor_id as [store!1!Id],
s.stor_name as [store!1!Name],
NULL as[sale!2!OrderNo],
NULL as [sale!2!Qty]
FROM stores s
UNION ALL
-- 销售数据
SELECT 2, 1,
s.stor_id,
s.stor_name,
sa.ord_num,
sa.qty
FROM stores s, sales sa
WHERE s.stor_id = sa.stor_id
ORDER BY [store!1!name]
FOR XML EXPLICIT

  这个查询初看起来有点复杂,其实它只是把不同的数据集(即这里的Store和Sale)分解到了独立的SELECT语句里,然后再用UNION ALL操作符连结成一个查询。

  我们之所以要把查询写成上面的形式,是为了让查询结果不仅包含XML文档所描述的数据,而且还包含描述XML文档结构的元数据。上述查询所生成的表称为Universal表,sqlxml.dll生成XML文档时需要这种格式。Universal表对于编写代码的人来说是透明的,但了解这个表还是很有意义的,它将有助于代码的开发和调试。下面是Universal表的一个例子:

Tag Parent store!1!id store!1!name sale!2!orderno sale!2!qty
1 NULL 7066 Barnum&single;s NULL NULL
2 1 7066 Barnum&single;s A297650 50
2 1 7066 Barnum&single;s QA7442 375
1 NULL 8042 Bookbeat NULL NULL
2 1 8042 Bookbeat 423LL9 2215

  Universal表和EXPLICIT模式查询的元数据部分都以红色表示,黑色表示数据。比较查询和表就可以找出sqlxml.dll生成XML文档所需要的元素。我们来仔细地分析一下它们描述的是什么。

  Tag和Parent列是XML文档层次结构方面的信息,我们可以认为图2中的每个SELECT语句代表了一个XML节点,而Tag和Parent列让我们指定节点在文档层次结构中的位置。如果在第二个SELECT语句中指定Tag为2、指定Parent为1,就表示为这些数据加上了一个值为2的标签,而这些数据的父亲是那些标签为1的数据(即第一个SELECT语句)。这就使得我们能够构造出<Store>和<Sale>之间的父-子关系,而且正如你可能猜想到的,它使得我们可以生成任意合法的XML文档结构。注意第一个SELECT命令的parent列设置成了NULL,这表示<Store>元素处于最顶层的位置。

  以黑色表示的数据将成为节点的属性或元素,例如,Store_ID就通过列名提供了这方面的信息。列名字中的“!”是分隔符,总共可分成四项(四个参数),其中第四个参数是可选的。这些参数描述的是:

  第一个参数描述该列所属元素的名字,在这里是<Store>元素。

  第二个是标签编号,它指定了该列信息在XML树形结构中所处位置。

  第三个参数指定XML文档内的属性或元素名字。在这里名字指定为id。

  数据列默认被创建为参数2所指定节点的属性,即id将成为<Store>节点的属性。如果要指定id是<Store>的一个子元素,我们可以使用第四个可选的参数,这个参数的一个作用就是让我们把该项指定为元素,例如store!1!id!element。

  由于使用了UNION ALL操作符来连结SELECT语句,为了保证SQL查询的合法性,所有SELECT语句的选择结果必须具有相同数量的列。我们使用NULL关键词来补足SELECT语句,从而避免了重复数据。

  通过EXPLICIT模式查询所生成的XML文档和通过AUTO模式生成的完全相同,那么为什么要创建EXPLICIT模式查询呢?

  假设现在有人要求在XML文档中包含商店的打折信息。查看Pubs数据库,我们得知每个商店都可以有0到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模式查询:

【图 2A】

SELECT 1 as Tag, NULL as Parent,
s.stor_id as [Store!1!id],
s.stor_name as [Store!1!name],
NULL as [Sale!2!orderno],
NULL as [Sale!2!1ty],
NULL as [Discount!3!type],
NULL as [Discount!3!lowqty],
NULL as [Discount!3!highqty],
NULL as [Discount!3!amount!element]
FROM stores s
UNION ALL
SELECT 2, 1,
s.stor_id,
s.stor_name,
sa.ord_num,
sa.qty,
NULL,
NULL,
NULL,
NULL
FROM stores s, sales sa
WHERE s.stor_id = sa.stor_id
UNION ALL
SELECT 3, 1,
NULL,
s.stor_name,
NULL,
NULL,
d.discounttype,
d.lowqty,
d.highqty,
d.discount
FROM stores s, discounts d
WHERE s.stor_id = d.stor_id
ORDER BY [store!1!name]
For XML EXPLICIT

  为了创建图2A所显示的EXPLICIT模式查询,我们对图2的查询进行了如下修改:

  增加了第三个子查询提取折扣数据,通过Tag列声明这些数据的标签值为3。

  通过指定Parent列为1将折扣数据设置成<Store>元素的子元素。

  注意在第三个SELECT子查询中我们只包含了那些必需的列,并用NULL补足空列。这个子查询包含store_name列,虽然Discount元素并不要用到这个列,但如果把这个列也设置为NULL,则结果Universal表将不会按照解析器所要求的那样以节点升序排序(不妨自己试一下看看)。Universal表的排序列也可以用Tag列替代。

  为维持结果Universal表的完整性,第一、二两个SELECT语句也用NULL补足以反映为折扣数据增加的列。

  为指定新折扣列的元数据修改了第一个SELECT语句。

  通过在第四个参数中声明element指定Amount列为Discount的一个元素(element是可在第四个参数中声明的多个指令之一)。

  下面这个XML文档只能用EXPLICIT模式生成:

  结果XML文档中不会显示出NULL数据,如折扣lowqty和highqty。

  看来上面的介绍,大家可能已经对FOR XML的语法有所了解。通过FOR XML 我们在能够在Query Analyzer 中直接返回一个XML格式的数据或者通过其他多样化表现方式将XML格式的数据显示出来,比如可以将数据显示在浏览器上。下面这个例子就使用FOR XML和ADO将数据输出到浏览器的例子。

Dim adoConn
Set adoConn = Server.CreateObject("ADODB.Connection")

Dim sConn
sConn = "Provider=SQLOLEDB;Data Source=192.168.0.160;Initial Catalog=Northwind;User ID=SA;Password=;"
adoConn.ConnectionString = sConn
adoConn.CursorLocation = adUseClient
adoConn.Open

Dim adoCmd
Set adoCmd = Server.CreateObject("ADODB.Command")
Set adoCmd.ActiveConnection = adoConn

Dim sQuery
‘定义 FOR XML的查询。具体的语法在以后的章节中将详细介绍。
sQuery = "<ROOT xmlns:sql='urn:schemas-microsoft-com:xml-sql'><sql:query>SELECT * FROM PRODUCTS ORDER BY PRODUCTNAME FOR XML AUTO</sql:query></ROOT>"

‘建立ADODB Stream 对象,ADODB Stream 对象需要ADO2.5以上版本支持,它可以将记录集转换为数据流。
Dim adoStreamQuery
Set adoStreamQuery = Server.CreateObject("ADODB.Stream")
adoStreamQuery.Open
adoStreamQuery.WriteText sQuery, adWriteChar
adoStreamQuery.Position = 0
adoCmd.CommandStream = adoStreamQuery
adoCmd.Dialect = "{5D531CB2-E6Ed-11D2-B252-00C04F681B71}"

Response.write "Pushing XML to client for processing " & "<BR/>"

adoCmd.Properties("Output Stream") = Response
‘输出XML格式的文本
Response.write "<XML ID=MyDataIsle>"
adoCmd.Execute , , adExecuteStream
Response.write "</XML>"

‘通过IE的XML解析器,使用DOM方法将XML的文本转换为HTML
<SCRIPT language="VBScript" For="window" Event="onload">

Dim xmlDoc
Set xmlDoc = MyDataIsle.XMLDocument
xmlDoc.resolveExternals=false
xmlDoc.async=false

Dim root, child
Set root = xmlDoc.documentElement

For each child in root.childNodes
dim OutputXML
OutputXML = document.all("log").innerHTML
document.all("log").innerHTML = OutputXML & "<LI>" & child.getAttribute("ProductName") & "</LI>"
Next

</SCRIPT>

 EXPLICIT模式

  EXPLICIT模式比较复杂,我们将用另外一种方法来表达图1所显示的查询。这种方法使得我们能够完全地控制查询所生成的XML文档。首先我们将介绍如何改用EXPLICIT模式编写图1所显示的查询,然后看看这种方法如何赋予我们远远超过AUTO模式的能力。

  下面是图1查询用EXPLICIT模式表达的代码:

【图2 】

--商店数据
SELECT 1 as Tag,
NULL as Parent,
s.stor_id as [store!1!Id],
s.stor_name as [store!1!Name],
NULL as[sale!2!OrderNo],
NULL as [sale!2!Qty]
FROM stores s
UNION ALL
-- 销售数据
SELECT 2, 1,
s.stor_id,
s.stor_name,
sa.ord_num,
sa.qty
FROM stores s, sales sa
WHERE s.stor_id = sa.stor_id
ORDER BY [store!1!name]
FOR XML EXPLICIT

  这个查询初看起来有点复杂,其实它只是把不同的数据集(即这里的Store和Sale)分解到了独立的SELECT语句里,然后再用UNION ALL操作符连结成一个查询。

  我们之所以要把查询写成上面的形式,是为了让查询结果不仅包含XML文档所描述的数据,而且还包含描述XML文档结构的元数据。上述查询所生成的表称为Universal表,sqlxml.dll生成XML文档时需要这种格式。Universal表对于编写代码的人来说是透明的,但了解这个表还是很有意义的,它将有助于代码的开发和调试。下面是Universal表的一个例子:

Tag Parent store!1!id store!1!name sale!2!orderno sale!2!qty
1 NULL 7066 Barnum&single;s NULL NULL
2 1 7066 Barnum&single;s A297650 50
2 1 7066 Barnum&single;s QA7442 375
1 NULL 8042 Bookbeat NULL NULL
2 1 8042 Bookbeat 423LL9 2215

  Universal表和EXPLICIT模式查询的元数据部分都以红色表示,黑色表示数据。比较查询和表就可以找出sqlxml.dll生成XML文档所需要的元素。我们来仔细地分析一下它们描述的是什么。

  Tag和Parent列是XML文档层次结构方面的信息,我们可以认为图2中的每个SELECT语句代表了一个XML节点,而Tag和Parent列让我们指定节点在文档层次结构中的位置。如果在第二个SELECT语句中指定Tag为2、指定Parent为1,就表示为这些数据加上了一个值为2的标签,而这些数据的父亲是那些标签为1的数据(即第一个SELECT语句)。这就使得我们能够构造出<Store>和<Sale>之间的父-子关系,而且正如你可能猜想到的,它使得我们可以生成任意合法的XML文档结构。注意第一个SELECT命令的parent列设置成了NULL,这表示<Store>元素处于最顶层的位置。

  以黑色表示的数据将成为节点的属性或元素,例如,Store_ID就通过列名提供了这方面的信息。列名字中的“!”是分隔符,总共可分成四项(四个参数),其中第四个参数是可选的。这些参数描述的是:

  第一个参数描述该列所属元素的名字,在这里是<Store>元素。

  第二个是标签编号,它指定了该列信息在XML树形结构中所处位置。

  第三个参数指定XML文档内的属性或元素名字。在这里名字指定为id。

  数据列默认被创建为参数2所指定节点的属性,即id将成为<Store>节点的属性。如果要指定id是<Store>的一个子元素,我们可以使用第四个可选的参数,这个参数的一个作用就是让我们把该项指定为元素,例如store!1!id!element。

  由于使用了UNION ALL操作符来连结SELECT语句,为了保证SQL查询的合法性,所有SELECT语句的选择结果必须具有相同数量的列。我们使用NULL关键词来补足SELECT语句,从而避免了重复数据。

  通过EXPLICIT模式查询所生成的XML文档和通过AUTO模式生成的完全相同,那么为什么要创建EXPLICIT模式查询呢?

  假设现在有人要求在XML文档中包含商店的打折信息。查看Pubs数据库,我们得知每个商店都可以有0到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模式查询:

【图 2A】

SELECT 1 as Tag, NULL as Parent,
s.stor_id as [Store!1!id],
s.stor_name as [Store!1!name],
NULL as [Sale!2!orderno],
NULL as [Sale!2!1ty],
NULL as [Discount!3!type],
NULL as [Discount!3!lowqty],
NULL as [Discount!3!highqty],
NULL as [Discount!3!amount!element]
FROM stores s
UNION ALL
SELECT 2, 1,
s.stor_id,
s.stor_name,
sa.ord_num,
sa.qty,
NULL,
NULL,
NULL,
NULL
FROM stores s, sales sa
WHERE s.stor_id = sa.stor_id
UNION ALL
SELECT 3, 1,
NULL,
s.stor_name,
NULL,
NULL,
d.discounttype,
d.lowqty,
d.highqty,
d.discount
FROM stores s, discounts d
WHERE s.stor_id = d.stor_id
ORDER BY [store!1!name]
For XML EXPLICIT

  为了创建图2A所显示的EXPLICIT模式查询,我们对图2的查询进行了如下修改:

  增加了第三个子查询提取折扣数据,通过Tag列声明这些数据的标签值为3。

  通过指定Parent列为1将折扣数据设置成<Store>元素的子元素。

  注意在第三个SELECT子查询中我们只包含了那些必需的列,并用NULL补足空列。这个子查询包含store_name列,虽然Discount元素并不要用到这个列,但如果把这个列也设置为NULL,则结果Universal表将不会按照解析器所要求的那样以节点升序排序(不妨自己试一下看看)。Universal表的排序列也可以用Tag列替代。

  为维持结果Universal表的完整性,第一、二两个SELECT语句也用NULL补足以反映为折扣数据增加的列。

  为指定新折扣列的元数据修改了第一个SELECT语句。

  通过在第四个参数中声明element指定Amount列为Discount的一个元素(element是可在第四个参数中声明的多个指令之一)。

  下面这个XML文档只能用EXPLICIT模式生成:

  结果XML文档中不会显示出NULL数据,如折扣lowqty和highqty。

  看来上面的介绍,大家可能已经对FOR XML的语法有所了解。通过FOR XML 我们在能够在Query Analyzer 中直接返回一个XML格式的数据或者通过其他多样化表现方式将XML格式的数据显示出来,比如可以将数据显示在浏览器上。下面这个例子就使用FOR XML和ADO将数据输出到浏览器的例子。

Dim adoConn
Set adoConn = Server.CreateObject("ADODB.Connection")

Dim sConn
sConn = "Provider=SQLOLEDB;Data Source=192.168.0.160;Initial Catalog=Northwind;User ID=SA;Password=;"
adoConn.ConnectionString = sConn
adoConn.CursorLocation = adUseClient
adoConn.Open

Dim adoCmd
Set adoCmd = Server.CreateObject("ADODB.Command")
Set adoCmd.ActiveConnection = adoConn

Dim sQuery
‘定义 FOR XML的查询。具体的语法在以后的章节中将详细介绍。
sQuery = "<ROOT xmlns:sql='urn:schemas-microsoft-com:xml-sql'><sql:query>SELECT * FROM PRODUCTS ORDER BY PRODUCTNAME FOR XML AUTO</sql:query></ROOT>"

‘建立ADODB Stream 对象,ADODB Stream 对象需要ADO2.5以上版本支持,它可以将记录集转换为数据流。
Dim adoStreamQuery
Set adoStreamQuery = Server.CreateObject("ADODB.Stream")
adoStreamQuery.Open
adoStreamQuery.WriteText sQuery, adWriteChar
adoStreamQuery.Position = 0
adoCmd.CommandStream = adoStreamQuery
adoCmd.Dialect = "{5D531CB2-E6Ed-11D2-B252-00C04F681B71}"

Response.write "Pushing XML to client for processing " & "<BR/>"

adoCmd.Properties("Output Stream") = Response
‘输出XML格式的文本
Response.write "<XML ID=MyDataIsle>"
adoCmd.Execute , , adExecuteStream
Response.write "</XML>"

‘通过IE的XML解析器,使用DOM方法将XML的文本转换为HTML
<SCRIPT language="VBScript" For="window" Event="onload">

Dim xmlDoc
Set xmlDoc = MyDataIsle.XMLDocument
xmlDoc.resolveExternals=false
xmlDoc.async=false

Dim root, child
Set root = xmlDoc.documentElement

For each child in root.childNodes
dim OutputXML
OutputXML = document.all("log").innerHTML
document.all("log").innerHTML = OutputXML & "<LI>" & child.getAttribute("ProductName") & "</LI>"
Next

</SCRIPT>

 EXPLICIT模式

  EXPLICIT模式比较复杂,我们将用另外一种方法来表达图1所显示的查询。这种方法使得我们能够完全地控制查询所生成的XML文档。首先我们将介绍如何改用EXPLICIT模式编写图1所显示的查询,然后看看这种方法如何赋予我们远远超过AUTO模式的能力。

  下面是图1查询用EXPLICIT模式表达的代码:

【图2 】

--商店数据
SELECT 1 as Tag,
NULL as Parent,
s.stor_id as [store!1!Id],
s.stor_name as [store!1!Name],
NULL as[sale!2!OrderNo],
NULL as [sale!2!Qty]
FROM stores s
UNION ALL
-- 销售数据
SELECT 2, 1,
s.stor_id,
s.stor_name,
sa.ord_num,
sa.qty
FROM stores s, sales sa
WHERE s.stor_id = sa.stor_id
ORDER BY [store!1!name]
FOR XML EXPLICIT

  这个查询初看起来有点复杂,其实它只是把不同的数据集(即这里的Store和Sale)分解到了独立的SELECT语句里,然后再用UNION ALL操作符连结成一个查询。

  我们之所以要把查询写成上面的形式,是为了让查询结果不仅包含XML文档所描述的数据,而且还包含描述XML文档结构的元数据。上述查询所生成的表称为Universal表,sqlxml.dll生成XML文档时需要这种格式。Universal表对于编写代码的人来说是透明的,但了解这个表还是很有意义的,它将有助于代码的开发和调试。下面是Universal表的一个例子:

Tag Parent store!1!id store!1!name sale!2!orderno sale!2!qty
1 NULL 7066 Barnum&single;s NULL NULL
2 1 7066 Barnum&single;s A297650 50
2 1 7066 Barnum&single;s QA7442 375
1 NULL 8042 Bookbeat NULL NULL
2 1 8042 Bookbeat 423LL9 2215

  Universal表和EXPLICIT模式查询的元数据部分都以红色表示,黑色表示数据。比较查询和表就可以找出sqlxml.dll生成XML文档所需要的元素。我们来仔细地分析一下它们描述的是什么。

  Tag和Parent列是XML文档层次结构方面的信息,我们可以认为图2中的每个SELECT语句代表了一个XML节点,而Tag和Parent列让我们指定节点在文档层次结构中的位置。如果在第二个SELECT语句中指定Tag为2、指定Parent为1,就表示为这些数据加上了一个值为2的标签,而这些数据的父亲是那些标签为1的数据(即第一个SELECT语句)。这就使得我们能够构造出<Store>和<Sale>之间的父-子关系,而且正如你可能猜想到的,它使得我们可以生成任意合法的XML文档结构。注意第一个SELECT命令的parent列设置成了NULL,这表示<Store>元素处于最顶层的位置。

  以黑色表示的数据将成为节点的属性或元素,例如,Store_ID就通过列名提供了这方面的信息。列名字中的“!”是分隔符,总共可分成四项(四个参数),其中第四个参数是可选的。这些参数描述的是:

  第一个参数描述该列所属元素的名字,在这里是<Store>元素。

  第二个是标签编号,它指定了该列信息在XML树形结构中所处位置。

  第三个参数指定XML文档内的属性或元素名字。在这里名字指定为id。

  数据列默认被创建为参数2所指定节点的属性,即id将成为<Store>节点的属性。如果要指定id是<Store>的一个子元素,我们可以使用第四个可选的参数,这个参数的一个作用就是让我们把该项指定为元素,例如store!1!id!element。

  由于使用了UNION ALL操作符来连结SELECT语句,为了保证SQL查询的合法性,所有SELECT语句的选择结果必须具有相同数量的列。我们使用NULL关键词来补足SELECT语句,从而避免了重复数据。

  通过EXPLICIT模式查询所生成的XML文档和通过AUTO模式生成的完全相同,那么为什么要创建EXPLICIT模式查询呢?

  假设现在有人要求在XML文档中包含商店的打折信息。查看Pubs数据库,我们得知每个商店都可以有0到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模式查询:

【图 2A】

SELECT 1 as Tag, NULL as Parent,
s.stor_id as [Store!1!id],
s.stor_name as [Store!1!name],
NULL as [Sale!2!orderno],
NULL as [Sale!2!1ty],
NULL as [Discount!3!type],
NULL as [Discount!3!lowqty],
NULL as [Discount!3!highqty],
NULL as [Discount!3!amount!element]
FROM stores s
UNION ALL
SELECT 2, 1,
s.stor_id,
s.stor_name,
sa.ord_num,
sa.qty,
NULL,
NULL,
NULL,
NULL
FROM stores s, sales sa
WHERE s.stor_id = sa.stor_id
UNION ALL
SELECT 3, 1,
NULL,
s.stor_name,
NULL,
NULL,
d.discounttype,
d.lowqty,
d.highqty,
d.discount
FROM stores s, discounts d
WHERE s.stor_id = d.stor_id
ORDER BY [store!1!name]
For XML EXPLICIT

  为了创建图2A所显示的EXPLICIT模式查询,我们对图2的查询进行了如下修改:

  增加了第三个子查询提取折扣数据,通过Tag列声明这些数据的标签值为3。

  通过指定Parent列为1将折扣数据设置成<Store>元素的子元素。

  注意在第三个SELECT子查询中我们只包含了那些必需的列,并用NULL补足空列。这个子查询包含store_name列,虽然Discount元素并不要用到这个列,但如果把这个列也设置为NULL,则结果Universal表将不会按照解析器所要求的那样以节点升序排序(不妨自己试一下看看)。Universal表的排序列也可以用Tag列替代。

  为维持结果Universal表的完整性,第一、二两个SELECT语句也用NULL补足以反映为折扣数据增加的列。

  为指定新折扣列的元数据修改了第一个SELECT语句。

  通过在第四个参数中声明element指定Amount列为Discount的一个元素(element是可在第四个参数中声明的多个指令之一)。

  下面这个XML文档只能用EXPLICIT模式生成:

  结果XML文档中不会显示出NULL数据,如折扣lowqty和highqty。

  看来上面的介绍,大家可能已经对FOR XML的语法有所了解。通过FOR XML 我们在能够在Query Analyzer 中直接返回一个XML格式的数据或者通过其他多样化表现方式将XML格式的数据显示出来,比如可以将数据显示在浏览器上。下面这个例子就使用FOR XML和ADO将数据输出到浏览器的例子。

Dim adoConn
Set adoConn = Server.CreateObject("ADODB.Connection")

Dim sConn
sConn = "Provider=SQLOLEDB;Data Source=192.168.0.160;Initial Catalog=Northwind;User ID=SA;Password=;"
adoConn.ConnectionString = sConn
adoConn.CursorLocation = adUseClient
adoConn.Open

Dim adoCmd
Set adoCmd = Server.CreateObject("ADODB.Command")
Set adoCmd.ActiveConnection = adoConn

Dim sQuery
‘定义 FOR XML的查询。具体的语法在以后的章节中将详细介绍。
sQuery = "<ROOT xmlns:sql='urn:schemas-microsoft-com:xml-sql'><sql:query>SELECT * FROM PRODUCTS ORDER BY PRODUCTNAME FOR XML AUTO</sql:query></ROOT>"

‘建立ADODB Stream 对象,ADODB Stream 对象需要ADO2.5以上版本支持,它可以将记录集转换为数据流。
Dim adoStreamQuery
Set adoStreamQuery = Server.CreateObject("ADODB.Stream")
adoStreamQuery.Open
adoStreamQuery.WriteText sQuery, adWriteChar
adoStreamQuery.Position = 0
adoCmd.CommandStream = adoStreamQuery
adoCmd.Dialect = "{5D531CB2-E6Ed-11D2-B252-00C04F681B71}"

Response.write "Pushing XML to client for processing " & "<BR/>"

adoCmd.Properties("Output Stream") = Response
‘输出XML格式的文本
Response.write "<XML ID=MyDataIsle>"
adoCmd.Execute , , adExecuteStream
Response.write "</XML>"

‘通过IE的XML解析器,使用DOM方法将XML的文本转换为HTML
<SCRIPT language="VBScript" For="window" Event="onload">

Dim xmlDoc
Set xmlDoc = MyDataIsle.XMLDocument
xmlDoc.resolveExternals=false
xmlDoc.async=false

Dim root, child
Set root = xmlDoc.documentElement

For each child in root.childNodes
dim OutputXML
OutputXML = document.all("log").innerHTML
document.all("log").innerHTML = OutputXML & "<LI>" & child.getAttribute("ProductName") & "</LI>"
Next

</SCRIPT>

 EXPLICIT模式

  EXPLICIT模式比较复杂,我们将用另外一种方法来表达图1所显示的查询。这种方法使得我们能够完全地控制查询所生成的XML文档。首先我们将介绍如何改用EXPLICIT模式编写图1所显示的查询,然后看看这种方法如何赋予我们远远超过AUTO模式的能力。

  下面是图1查询用EXPLICIT模式表达的代码:

【图2 】

--商店数据
SELECT 1 as Tag,
NULL as Parent,
s.stor_id as [store!1!Id],
s.stor_name as [store!1!Name],
NULL as[sale!2!OrderNo],
NULL as [sale!2!Qty]
FROM stores s
UNION ALL
-- 销售数据
SELECT 2, 1,
s.stor_id,
s.stor_name,
sa.ord_num,
sa.qty
FROM stores s, sales sa
WHERE s.stor_id = sa.stor_id
ORDER BY [store!1!name]
FOR XML EXPLICIT

  这个查询初看起来有点复杂,其实它只是把不同的数据集(即这里的Store和Sale)分解到了独立的SELECT语句里,然后再用UNION ALL操作符连结成一个查询。

  我们之所以要把查询写成上面的形式,是为了让查询结果不仅包含XML文档所描述的数据,而且还包含描述XML文档结构的元数据。上述查询所生成的表称为Universal表,sqlxml.dll生成XML文档时需要这种格式。Universal表对于编写代码的人来说是透明的,但了解这个表还是很有意义的,它将有助于代码的开发和调试。下面是Universal表的一个例子:

Tag Parent store!1!id store!1!name sale!2!orderno sale!2!qty
1 NULL 7066 Barnum&single;s NULL NULL
2 1 7066 Barnum&single;s A297650 50
2 1 7066 Barnum&single;s QA7442 375
1 NULL 8042 Bookbeat NULL NULL
2 1 8042 Bookbeat 423LL9 2215

  Universal表和EXPLICIT模式查询的元数据部分都以红色表示,黑色表示数据。比较查询和表就可以找出sqlxml.dll生成XML文档所需要的元素。我们来仔细地分析一下它们描述的是什么。

  Tag和Parent列是XML文档层次结构方面的信息,我们可以认为图2中的每个SELECT语句代表了一个XML节点,而Tag和Parent列让我们指定节点在文档层次结构中的位置。如果在第二个SELECT语句中指定Tag为2、指定Parent为1,就表示为这些数据加上了一个值为2的标签,而这些数据的父亲是那些标签为1的数据(即第一个SELECT语句)。这就使得我们能够构造出<Store>和<Sale>之间的父-子关系,而且正如你可能猜想到的,它使得我们可以生成任意合法的XML文档结构。注意第一个SELECT命令的parent列设置成了NULL,这表示<Store>元素处于最顶层的位置。

  以黑色表示的数据将成为节点的属性或元素,例如,Store_ID就通过列名提供了这方面的信息。列名字中的“!”是分隔符,总共可分成四项(四个参数),其中第四个参数是可选的。这些参数描述的是:

  第一个参数描述该列所属元素的名字,在这里是<Store>元素。

  第二个是标签编号,它指定了该列信息在XML树形结构中所处位置。

  第三个参数指定XML文档内的属性或元素名字。在这里名字指定为id。

  数据列默认被创建为参数2所指定节点的属性,即id将成为<Store>节点的属性。如果要指定id是<Store>的一个子元素,我们可以使用第四个可选的参数,这个参数的一个作用就是让我们把该项指定为元素,例如store!1!id!element。

  由于使用了UNION ALL操作符来连结SELECT语句,为了保证SQL查询的合法性,所有SELECT语句的选择结果必须具有相同数量的列。我们使用NULL关键词来补足SELECT语句,从而避免了重复数据。

  通过EXPLICIT模式查询所生成的XML文档和通过AUTO模式生成的完全相同,那么为什么要创建EXPLICIT模式查询呢?

  假设现在有人要求在XML文档中包含商店的打折信息。查看Pubs数据库,我们得知每个商店都可以有0到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模式查询:

【图 2A】

SELECT 1 as Tag, NULL as Parent,
s.stor_id as [Store!1!id],
s.stor_name as [Store!1!name],
NULL as [Sale!2!orderno],
NULL as [Sale!2!1ty],
NULL as [Discount!3!type],
NULL as [Discount!3!lowqty],
NULL as [Discount!3!highqty],
NULL as [Discount!3!amount!element]
FROM stores s
UNION ALL
SELECT 2, 1,
s.stor_id,
s.stor_name,
sa.ord_num,
sa.qty,
NULL,
NULL,
NULL,
NULL
FROM stores s, sales sa
WHERE s.stor_id = sa.stor_id
UNION ALL
SELECT 3, 1,
NULL,
s.stor_name,
NULL,
NULL,
d.discounttype,
d.lowqty,
d.highqty,
d.discount
FROM stores s, discounts d
WHERE s.stor_id = d.stor_id
ORDER BY [store!1!name]
For XML EXPLICIT

  为了创建图2A所显示的EXPLICIT模式查询,我们对图2的查询进行了如下修改:

  增加了第三个子查询提取折扣数据,通过Tag列声明这些数据的标签值为3。

  通过指定Parent列为1将折扣数据设置成<Store>元素的子元素。

  注意在第三个SELECT子查询中我们只包含了那些必需的列,并用NULL补足空列。这个子查询包含store_name列,虽然Discount元素并不要用到这个列,但如果把这个列也设置为NULL,则结果Universal表将不会按照解析器所要求的那样以节点升序排序(不妨自己试一下看看)。Universal表的排序列也可以用Tag列替代。

  为维持结果Universal表的完整性,第一、二两个SELECT语句也用NULL补足以反映为折扣数据增加的列。

  为指定新折扣列的元数据修改了第一个SELECT语句。

  通过在第四个参数中声明element指定Amount列为Discount的一个元素(element是可在第四个参数中声明的多个指令之一)。

  下面这个XML文档只能用EXPLICIT模式生成:

  结果XML文档中不会显示出NULL数据,如折扣lowqty和highqty。

  看来上面的介绍,大家可能已经对FOR XML的语法有所了解。通过FOR XML 我们在能够在Query Analyzer 中直接返回一个XML格式的数据或者通过其他多样化表现方式将XML格式的数据显示出来,比如可以将数据显示在浏览器上。下面这个例子就使用FOR XML和ADO将数据输出到浏览器的例子。

Dim adoConn
Set adoConn = Server.CreateObject("ADODB.Connection")

Dim sConn
sConn = "Provider=SQLOLEDB;Data Source=192.168.0.160;Initial Catalog=Northwind;User ID=SA;Password=;"
adoConn.ConnectionString = sConn
adoConn.CursorLocation = adUseClient
adoConn.Open

Dim adoCmd
Set adoCmd = Server.CreateObject("ADODB.Command")
Set adoCmd.ActiveConnection = adoConn

Dim sQuery
‘定义 FOR XML的查询。具体的语法在以后的章节中将详细介绍。
sQuery = "<ROOT xmlns:sql='urn:schemas-microsoft-com:xml-sql'><sql:query>SELECT * FROM PRODUCTS ORDER BY PRODUCTNAME FOR XML AUTO</sql:query></ROOT>"

‘建立ADODB Stream 对象,ADODB Stream 对象需要ADO2.5以上版本支持,它可以将记录集转换为数据流。
Dim adoStreamQuery
Set adoStreamQuery = Server.CreateObject("ADODB.Stream")
adoStreamQuery.Open
adoStreamQuery.WriteText sQuery, adWriteChar
adoStreamQuery.Position = 0
adoCmd.CommandStream = adoStreamQuery
adoCmd.Dialect = "{5D531CB2-E6Ed-11D2-B252-00C04F681B71}"

Response.write "Pushing XML to client for processing " & "<BR/>"

adoCmd.Properties("Output Stream") = Response
‘输出XML格式的文本
Response.write "<XML ID=MyDataIsle>"
adoCmd.Execute , , adExecuteStream
Response.write "</XML>"

‘通过IE的XML解析器,使用DOM方法将XML的文本转换为HTML
<SCRIPT language="VBScript" For="window" Event="onload">

Dim xmlDoc
Set xmlDoc = MyDataIsle.XMLDocument
xmlDoc.resolveExternals=false
xmlDoc.async=false

Dim root, child
Set root = xmlDoc.documentElement

For each child in root.childNodes
dim OutputXML
OutputXML = document.all("log").innerHTML
document.all("log").innerHTML = OutputXML & "<LI>" & child.getAttribute("ProductName") & "</LI>"
Next

</SCRIPT>


 EXPLICIT模式

  EXPLICIT模式比较复杂,我们将用另外一种方法来表达图1所显示的查询。这种方法使得我们能够完全地控制查询所生成的XML文档。首先我们将介绍如何改用EXPLICIT模式编写图1所显示的查询,然后看看这种方法如何赋予我们远远超过AUTO模式的能力。

  下面是图1查询用EXPLICIT模式表达的代码:

【图2 】

--商店数据
SELECT 1 as Tag,
NULL as Parent,
s.stor_id as [store!1!Id],
s.stor_name as [store!1!Name],
NULL as[sale!2!OrderNo],
NULL as [sale!2!Qty]
FROM stores s
UNION ALL
-- 销售数据
SELECT 2, 1,
s.stor_id,
s.stor_name,
sa.ord_num,
sa.qty
FROM stores s, sales sa
WHERE s.stor_id = sa.stor_id
ORDER BY [store!1!name]
FOR XML EXPLICIT

  这个查询初看起来有点复杂,其实它只是把不同的数据集(即这里的Store和Sale)分解到了独立的SELECT语句里,然后再用UNION ALL操作符连结成一个查询。

  我们之所以要把查询写成上面的形式,是为了让查询结果不仅包含XML文档所描述的数据,而且还包含描述XML文档结构的元数据。上述查询所生成的表称为Universal表,sqlxml.dll生成XML文档时需要这种格式。Universal表对于编写代码的人来说是透明的,但了解这个表还是很有意义的,它将有助于代码的开发和调试。下面是Universal表的一个例子:

Tag Parent store!1!id store!1!name sale!2!orderno sale!2!qty
1 NULL 7066 Barnum&single;s NULL NULL
2 1 7066 Barnum&single;s A297650 50
2 1 7066 Barnum&single;s QA7442 375
1 NULL 8042 Bookbeat NULL NULL
2 1 8042 Bookbeat 423LL9 2215

  Universal表和EXPLICIT模式查询的元数据部分都以红色表示,黑色表示数据。比较查询和表就可以找出sqlxml.dll生成XML文档所需要的元素。我们来仔细地分析一下它们描述的是什么。

  Tag和Parent列是XML文档层次结构方面的信息,我们可以认为图2中的每个SELECT语句代表了一个XML节点,而Tag和Parent列让我们指定节点在文档层次结构中的位置。如果在第二个SELECT语句中指定Tag为2、指定Parent为1,就表示为这些数据加上了一个值为2的标签,而这些数据的父亲是那些标签为1的数据(即第一个SELECT语句)。这就使得我们能够构造出<Store>和<Sale>之间的父-子关系,而且正如你可能猜想到的,它使得我们可以生成任意合法的XML文档结构。注意第一个SELECT命令的parent列设置成了NULL,这表示<Store>元素处于最顶层的位置。

  以黑色表示的数据将成为节点的属性或元素,例如,Store_ID就通过列名提供了这方面的信息。列名字中的“!”是分隔符,总共可分成四项(四个参数),其中第四个参数是可选的。这些参数描述的是:

  第一个参数描述该列所属元素的名字,在这里是<Store>元素。

  第二个是标签编号,它指定了该列信息在XML树形结构中所处位置。

  第三个参数指定XML文档内的属性或元素名字。在这里名字指定为id。

  数据列默认被创建为参数2所指定节点的属性,即id将成为<Store>节点的属性。如果要指定id是<Store>的一个子元素,我们可以使用第四个可选的参数,这个参数的一个作用就是让我们把该项指定为元素,例如store!1!id!element。

  由于使用了UNION ALL操作符来连结SELECT语句,为了保证SQL查询的合法性,所有SELECT语句的选择结果必须具有相同数量的列。我们使用NULL关键词来补足SELECT语句,从而避免了重复数据。

  通过EXPLICIT模式查询所生成的XML文档和通过AUTO模式生成的完全相同,那么为什么要创建EXPLICIT模式查询呢?

  假设现在有人要求在XML文档中包含商店的打折信息。查看Pubs数据库,我们得知每个商店都可以有0到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模式查询:

【图 2A】

SELECT 1 as Tag, NULL as Parent,
s.stor_id as [Store!1!id],
s.stor_name as [Store!1!name],
NULL as [Sale!2!orderno],
NULL as [Sale!2!1ty],
NULL as [Discount!3!type],
NULL as [Discount!3!lowqty],
NULL as [Discount!3!highqty],
NULL as [Discount!3!amount!element]
FROM stores s
UNION ALL
SELECT 2, 1,
s.stor_id,
s.stor_name,
sa.ord_num,
sa.qty,
NULL,
NULL,
NULL,
NULL
FROM stores s, sales sa
WHERE s.stor_id = sa.stor_id
UNION ALL
SELECT 3, 1,
NULL,
s.stor_name,
NULL,
NULL,
d.discounttype,
d.lowqty,
d.highqty,
d.discount
FROM stores s, discounts d
WHERE s.stor_id = d.stor_id
ORDER BY [store!1!name]
For XML EXPLICIT

  为了创建图2A所显示的EXPLICIT模式查询,我们对图2的查询进行了如下修改:

  增加了第三个子查询提取折扣数据,通过Tag列声明这些数据的标签值为3。

  通过指定Parent列为1将折扣数据设置成<Store>元素的子元素。

  注意在第三个SELECT子查询中我们只包含了那些必需的列,并用NULL补足空列。这个子查询包含store_name列,虽然Discount元素并不要用到这个列,但如果把这个列也设置为NULL,则结果Universal表将不会按照解析器所要求的那样以节点升序排序(不妨自己试一下看看)。Universal表的排序列也可以用Tag列替代。

  为维持结果Universal表的完整性,第一、二两个SELECT语句也用NULL补足以反映为折扣数据增加的列。

  为指定新折扣列的元数据修改了第一个SELECT语句。

  通过在第四个参数中声明element指定Amount列为Discount的一个元素(element是可在第四个参数中声明的多个指令之一)。

  下面这个XML文档只能用EXPLICIT模式生成:

  结果XML文档中不会显示出NULL数据,如折扣lowqty和highqty。

  看来上面的介绍,大家可能已经对FOR XML的语法有所了解。通过FOR XML 我们在能够在Query Analyzer 中直接返回一个XML格式的数据或者通过其他多样化表现方式将XML格式的数据显示出来,比如可以将数据显示在浏览器上。下面这个例子就使用FOR XML和ADO将数据输出到浏览器的例子。

Dim adoConn
Set adoConn = Server.CreateObject("ADODB.Connection")

Dim sConn
sConn = "Provider=SQLOLEDB;Data Source=192.168.0.160;Initial Catalog=Northwind;User ID=SA;Password=;"
adoConn.ConnectionString = sConn
adoConn.CursorLocation = adUseClient
adoConn.Open

Dim adoCmd
Set adoCmd = Server.CreateObject("ADODB.Command")
Set adoCmd.ActiveConnection = adoConn

Dim sQuery
‘定义 FOR XML的查询。具体的语法在以后的章节中将详细介绍。
sQuery = "<ROOT xmlns:sql='urn:schemas-microsoft-com:xml-sql'><sql:query>SELECT * FROM PRODUCTS ORDER BY PRODUCTNAME FOR XML AUTO</sql:query></ROOT>"

‘建立ADODB Stream 对象,ADODB Stream 对象需要ADO2.5以上版本支持,它可以将记录集转换为数据流。
Dim adoStreamQuery
Set adoStreamQuery = Server.CreateObject("ADODB.Stream")
adoStreamQuery.Open
adoStreamQuery.WriteText sQuery, adWriteChar
adoStreamQuery.Position = 0
adoCmd.CommandStream = adoStreamQuery
adoCmd.Dialect = "{5D531CB2-E6Ed-11D2-B252-00C04F681B71}"

Response.write "Pushing XML to client for processing " & "<BR/>"

adoCmd.Properties("Output Stream") = Response
‘输出XML格式的文本
Response.write "<XML ID=MyDataIsle>"
adoCmd.Execute , , adExecuteStream
Response.write "</XML>"

‘通过IE的XML解析器,使用DOM方法将XML的文本转换为HTML
<SCRIPT language="VBScript" For="window" Event="onload">

Dim xmlDoc
Set xmlDoc = MyDataIsle.XMLDocument
xmlDoc.resolveExternals=false
xmlDoc.async=false

Dim root, child
Set root = xmlDoc.documentElement

For each child in root.childNodes
dim OutputXML
OutputXML = document.all("log").innerHTML
document.all("log").innerHTML = OutputXML & "<LI>" & child.getAttribute("ProductName") & "</LI>"
Next

</SCRIPT>

兵器之二:XPath

  W3C 于 1999 年 10 月 8 日提出的 XPath 语言规范,它是一种基于XML的查询语言,它能在XML文挡中处理数据。SQL Server 2000 中实现的是该规范的子集。它把table和views作为XML的组件,并把columns作为XML属性。SQL Server 2000的XML支持IIS使用URL或者模板的方式提交到SQL Server处理Xpath查询,并返回XML的结果。

  支持的功能

  下表显示 SQL Server 2000 中实现的 XPath 语言的功能。

功能 项目
attributechildparentself
包括连续谓词和嵌套谓词在内的布尔值谓词  
所有关系运算符 =, !=, <, <=, >, >=
算术运算符 +, -, *, div
显式转换函数 number()string()Boolean()
布尔运算符 AND, OR
布尔函数 true()false()not()
XPath 变量  

  不支持的功能

  下表显示 SQL Server 2000 中没有实现的 XPath 语言的功能。

功能 项目
ancestor、ancestor-or-self、descendant、descendant-or-self (//)、following、following-sibling、namespace、preceding、preceding-sibling
数值谓词  
算术运算符 mod
节点函数 ancestor、ancestor-or-self、descendant、descendant-or-self (//)、following、following-sibling、namespace、preceding、preceding-sibling
字符串函数 string()、concat()、starts-with()、contains()、substring-before()、substring-after()、substring()、string-length()、normalize()、translate()
布尔函数 lang()
数字函数 sum()、floor()、ceiling()、round()
Union 运算符 |

  XPath 查询是以表达式的形式指定的。位置路径是一种比较常用表达式,用以选择相对于上下文节点的节点集。位置路径表达式的求值结果是节点集。

XML 文档
<root>
  <order productid="Prod-28" unitprice="45.6" quantity="15">
  <discount>0.25</discount>
  </order>
  <order productid="Prod-39" unitprice="18" quantity="21">
  <discount>0.25</discount>
  </order>
  <order productid="Prod-46" unitprice="12" quantity="2">
  <discount>0.25</discount>
  </order>
</root>

  下面是查询该XML 文档的XPath位置路径表达式,它的含义是返回根节点下所有<ROOT>子元素下的ProductID属性的属性值为 "prod-39"的 <order> 元素。

  位置路径的类型

  可以分为绝对位置路径和相对位置路径。

  绝对位置路径以文档的根节点开始,由斜杠 (/) 组成,后面可以是相对位置路径。斜杠 (/) 选定文档的根节点。

  相对位置路径以文档的上下文节点(Context)开始,由斜杠 (/) 所分开的一个或多个位置步骤序列组成。每个步骤都选定相对于上下文节点的节点集。初始步骤序列选定相对于上下文节点的节点集。节点集中的每个节点都用作后续步骤的上下文节点。由后续步骤标识的节点集联接在一起。例如,child::Order/child::OrderDetail 选定上下文节点的<order> 子元素的 <orderdetail> 子元素。

  位置步骤 (绝对或相对)位置路径由包含下面三部分的位置步骤组成:

  轴

  轴指定位置步骤选定的节点与上下文节点之间的树关系。SQL Server 2000 支持 parent、child、attribute 及 self 轴。

  如果在位置路径中指定 child 轴,查询选定的所有节点都将是上下文节点的子节点。
比如说要查询从当前的上下文节点中选定所有 <Order>节点的子节点,可以使用 child:: Order

  如果指定 parent 轴,选定的节点将是上下文节点的父节点。
比如说要查询从当前的上下文节点中选定所有 <Order>节点的父节点,可以使用 parent:: Order

  如果指定 attribute 轴,选定的节点是上下文节点的属性。
比如说要查询从当前的上下文节点中选定所有 <Order>节点的属性,可以使用 attribute:: Order

  节点测试

  节点测试指定位置步骤选定的节点类型。每个轴(child、parent、attribute 和 self)都具有主要节点类型。对于 attribute 轴,主要节点类型是 <attribute>。对于 parent、child 和 self 轴,主要节点类型是 <element>。

  例如,如果位置路径指定 child::Order,则将选定上下文节点的 <Order> 子元素。因为 child 轴以 <element> 为其主要节点类型,而且如果Order是 <element> 节点,则节点测试Order为 TRUE。

  选择谓词(零个或多个)

  谓词围绕着轴筛选节点集。在 XPath 表达式中指定选择谓词与在 SELECT 语句中指定 WHERE 子句相似。谓词在括号中指定。应用在选择谓词中指定的测试将对节点测试返回的节点进行筛选。对于要筛选的节点集中的每个节点,在对谓词表达式取值时将此节点作为上下文节点,将节点集中的节点数作为上下文大小。如果谓词表达式对此节点取值为 TRUE,则此节点将包括在最后所得到的节点集中。

  例如:
  1. Child::Order [attribute::ProductID="Prod-39"] 表示从当前的上下文节点中选定ProductID属性值为Prod-39的所有 <order>子节点。

  2. Child::Order [child:: Discount] 表示从当前的上下文节点中选定包含一个或多个 <Discount> 子节点的所有 <Order> 子节点。

  3. Child::Order [not(child:: Discount)] 表示从当前的上下文节点中选定不包含 <Discount> 子节点的所有 <Order> 子节点。

  位置步骤的语法

  是用两个冒号 (::) 分开的轴名和节点测试,后面是分别放在方括号中的零个或多个表达式。

  例如,在 XPath 表达式(位置路径)child::Order[attribute::ProductID="prod-39"]中,选定上下文节点的所有 <order> 子元素。然后将谓词中的测试应用于节点集,将只返回 ProductID属性的属性值为 "prod-39"的 <order> 元素节点。

  缩略语法

  Sqlservr2000支持下面的位置路径缩略语法:
  attribute::可以缩略为 @。
  比如:[attribute::ProductID="Prod-39"] 可以缩写成 [@ProductID =" Prod-39"]

  child::可以在位置步骤中省略。
  比如:位置路径child::ROOT/child::Orde可以缩写成 ROOT /Order

  self::node() 可缩略为一个句点 (.),而 parent::node() 可缩略两个句点 (..)。

  所以 /child::ROOT/child::Order[attribute::ProductID="prod-39"]也可以缩写成 /ROOT/Order[@ProductID="prod-39"]




相关文章

相关软件