分享
 
 
 

SQL Server 2000 XML之七种兵器

王朝mssql·作者佚名  2006-01-09
窄屏简体版  字體: |||超大  

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 语言的功能。

功能

项目

attribute、child、parent 和 self 轴

包括连续谓词和嵌套谓词在内的布尔值谓词

所有关系运算符

=, !=, <, <=, >, >=

算术运算符

+, -, *, 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"]

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有