Microsoft SQL Server 2000 XML 功能概述

发布日期: 4/1/2004 | 更新日期: 4/1/2004
Andrew Conrad

Microsoft Corporation 

2001 年 7 月 26 日 

下载 Conrad.exe。

Redmond 欢迎您! 我希望您会度过一个愉快的夏天。 我一直忙于应付接连不断的会议。 如果您有幸出席了今年的 Tech Ed,我想您肯定度过了非常美好的时光。 我自己就是这样的。 正如谚语“好事多磨”一样,现在我正在张贴一篇由我的同事撰写的文章,他在上个月花了不少时间来撰写这篇精彩的文章。 尽管文章语气平和,但 Andy 还是以较长的篇幅试图抨击我以前的一篇文章。 对于那些还未详细了解 SQL Server 2000 中 XML 功能的人来说,这是一篇很好的文章。本文旨在向您提供 SQL Server XML 功能的规划图,并重点介绍最近发布的功能,这些功能通过我们的 Web 发布计划交付。 非常感谢 Andy 为本文付出的时间和精力。

? Eric Schmidt 

还记得那些来自学院的 “good old 100 level”(100 个最美好的旧时光)调查分类吗? 基本上,提取一个相当广泛的主题并对它一带而过,而只突出重要的事实和活动。 这些分类似乎非常适合人类。 例如,我提取 The History of Western Civilization: 482 – present。 在第一讲才进行十分钟后,罗马帝国就衰落了。 我忽略了一两个分类并完全遗漏了文艺复兴和拿破仑战争。 对于我们这些数学/计算机科学系的人来说,这是一个满足某些等级要求并学会回答一些困难问题会的很好方式。

好吧,现在开始进入 XML for SQL Server 101。 我是 SQLXML 教授,对于本专栏的其余部分,我打算概述由 SQL Server 2000 提供的 XML 功能。正如对于其他调查分类一样,此处并不是要详述任一主题,而是让您了解由 SQL Server 2000 提供的多个 XML 功能,以及如何使用它们来解决一些常见的应用程序开发问题和情况。 在本专栏的末尾,我还将讨论您可从何处获得有关由 SQL Server 2000 提供的 XML 支持的信息。

注 本专栏基于 SQL Server XML 功能,该功能自 XML for Microsoft SQL Server 2000 Web Release 2 Beta 1 推出之后可用。 由于 SQL Server 2000 是在 2000 年秋季发布的,因此 Microsoft 的 WebData 组已经提供了两个完全支持的升级功能,用来增强和改善 XML 功能。 XML for SQL Server Web Release 1 是在 2001 年 2 月发布的,XML for SQL Server Web Release 2 目前被作为 Beta 预览版使用,其 Web 版本将于 2001 年秋季发布。之所以如此频繁地发布,是为了满足客户的要求以及 XML 标准快速变化的需要。 例如,Web Release 2 基于 W3C 建议支持 XSD。 要下载最新的 Web 版本或查找有关在 XML for SQL Server Web Release 中提供的新功能的更多信息,请参阅 http://msdn.microsoft.com/xml

XML 形式的数据

如果您还不了解将数据表示为 XML 所带来的好处,您可能将不阅读本专栏。 但是,我为那些决定沉迷于 XML 101 的用户提供快速概述。

对于应用程序开发人员,XML 确实具有如下好处: 

• 与传统的数据结构(例如,ADO 记录集)相比,XML 能够以更自然的方式来表示半结构化数据。 例如,根据需要,每个 Customer 元素只包含一个 CellPhoneNumber 子元素。 
 
• XML 是分层的。 对于大多数人来说,业务对象关系是分层的,而不是平面的。 但是,在关系世界中,我们不得不将数据存储到平面关系表中,并将对象之间的关系公开为键关系。 在 XML 中,这些关系可通过分层包容来表示。 例如,Customer 元素有一个地址子元素。 
 
• XML 是普遍存在、独立于平台的可读格式。 没错,这虽然是 XML 宣传和营销术语,但确实有一定真实性。 如果我们能从关系数据生成 XML,就不再会受到特定平台或编程语言的限制。 实际上,这会允许我们将关系数据公开到任何客户端应用程序,并从任何客户端应用程序使用关系数据。 
 

将数据存储到关系数据库中

没错,这里的确没有什么惊天动地的事情,但是,如果您的数据需要由多个用户访问和更新,到目前为止,最好还是将数据存储在 DBMS(例如,SQL Server 2000)中。实际上,大多数人在查询和修改数据时,都非常愿意使用传统的客户端数据访问机制(例如,ADO 和 DAO)。 这意味着通过 ADO Recordset 对象发出 TSQL 查询并访问/修改数据。

将关系数据公开为 XML

因此,我们现在都认为 XML 是公开分层的半结构化业务数据的最佳方式,而且还认为数据的最佳存储位置是关系数据库,但是,实际问题是“如何将关系数据公开为 XML?” 这就是 XML for SQL Server 发挥作用的地方。 该产品旨在将关系数据映射到 XML 以及将 XML 映射到关系数据。 这是通过大量功能(我们马上将讨论这些功能)来完成的。 但是,甚至在开始讨论这些功能之前,我还是打算展示对该类的最后验证。 现在,我知道这有点奇怪,但是一定要相信我,毕竟我拿到了博士学位! (没关系,我从 Internet 上花 59.95 美元订购了它。)

问题

假设我需要在现有的旧式客户信息数据库基础上开发一个中间层应用程序。 (在本文中,我们将假设旧式数据源是 SQL Server 2000 附带的 Northwind 数据库)。 此应用程序旨在公开或使用那些发送到或源自于只能通过 HTTP 访问的客户端的客户数据。 正如前面讨论的那样,显而易见的选择是将数据的格式设置为 XML。

而且,在阅读 Eric Schmidt 上个月的 Using Schema and Serialization to Leverage Business Logic column之后,客户端应用程序开发者和我都认为,从开发 XSD 架构开始做起可能不失为一个好主意。该 XSD 架构为 Customer 实例文档定义内容和格式:

<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" >

    <xsd:element name="CustomerRecord" >
        <xsd:complexType>
            <xsd:sequence>
                <xsd:element name="Contact" type="xsd:string" /> 
                <xsd:element name="Title" type="xsd:string" /> 
                <xsd:element name="Company" type="xsd:string" />
                <xsd:element ref="Order" />
            </xsd:sequence>
                <xsd:attribute name="ID" type="xsd:string" />   
        </xsd:complexType>
    </xsd:element>

    <xsd:element name="Order" >
        <xsd:complexType>
            <xsd:sequence>    
                <xsd:element ref="Items" />
            </xsd:sequence>
            <xsd:attribute name="OrderID" type="xsd:int"/>     
            <xsd:attribute name="Ordered" type="xsd:dateTime"/>
            <xsd:attribute name="Shipped" type="xsd:dateTime"/>
        </xsd:complexType>
    </xsd:element>

    <xsd:element name="Items" >
        <xsd:complexType>
            <xsd:sequence>
                <xsd:element ref="ItemPurchased" />    
            </xsd:sequence>
            <xsd:attribute name="Price" type="xsd:int" />     
            <xsd:attribute name="Quantity"  type="xsd:int" />
        </xsd:complexType>   
    </xsd:element>

    <xsd:element name="ItemPurchased" type="Products" />

    <xsd:complexType name="Products">
        <xsd:sequence>
            <xsd:element name="Name" type="xsd:string" />    
            <xsd:element name="NumberInStock" type="xsd:int" />    
        </xsd:sequence>
    </xsd:complexType>   
   
</xsd:schema>
在这里,我们真正要做的事情是定义客户业务对象。 因此,现在问题是,客户和定单数据目前驻留在几个关系表中,而且我希望将这些数据公开为 XML。 我可以编写一些用来从 Customers、Orders、Order Details 和 Products 表选择记录的传统 ADO 代码,然后通过以下方法来亲自构建 XML:快速浏览记录集,嵌套基于键关系的元素,并添加所有相应的 XML 标记。 我还可以使用 ADO 形状提供程序来构建分层记录集,并将记录集存储到 XML 中;但是,ADO 所支持的 XML 格式与我的架构所指定的格式有很大区别。 随后,可以编写一个 XSLT 脚本,以便将 XML 转换为由Customer XSD 架构指定的格式,但这似乎确实需要大量工作。 必须寻找一个更好的方法。

有关XML查询扩展

在 SQL Server 2000 中,用户现在可以针对关系数据库对象执行 SQL 查询,然后以 XML 文档(而非标准行集合)形式返回结果。 要检索 XML 形式的查询结果,用户可将 For XML 子句添加到标准行集生成查询的末尾,并包括下列三个模式之一: RAW、AUTO 或 EXPLICIT。 例如,我可以通过 ADO 或查询分析器运行以下查询:

Select firstname, lastname, employeeid from Northwind.._
Employess where employeeid = 1 for XML RAW 
而且将生成下面的 XML:

<row firstname="Nancy" lastname="Davolio" employeeid="1"/>

哇! 我的应用程序也需要从关系数据库生成 XML,因此,看来 For XML SQL 扩展就是我需要的工具。 所以,让我们尝试使用每种模式,看一看所生成的 XML 能否适合由 Customer XSD 架构指定的结构。

我喜欢简单的模式,最简单的 For XML 模式是 RAW 模式。 让我们先尝试使用 RAW 模式, 该模式将行集合内的每一行都转换为一个具有一般标识符 “row” 的 XML 元素。 每个非 NULL 列值都映射到该 XML 元素的属性,该元素的属性名与列名相同。 

因此,让我们尝试使用 RAW 模式。 对于该示例(以及本专栏代码示例的其余部分),我打算使用标准的 ADO 机制来发出查询。 实际上,我将使用的代码与记录集生成 ADO 命令所使用的代码几乎相同,唯一的差别是我要求将结果写入输出流(而非记录集)中。 如果您考虑过这一点,将会发现这很有意义。 半结构化的分层 XML 文档确实无法完全适合传统的结构化记录集。 而且,天知道在执行查询之后将要如何处理 XML — 可能将它传递到 DOM、应用 XSLT 样式表,或者甚至只是通过 HTTP 发布文档 — 因此以流的形式返回 XML 文档会为用户带来一定的灵活性。

对于那些要求所有结果都位于单个行集合内的 XML 查询,我使用标准的 SQL 联接查询来联接我感兴趣的表,从而完成此操作:

Public Function ForXMLRawQuery()

    Dim adoConn     As New ADODB.Connection
    Dim adoCmd      As New ADODB.Command
    Dim adoStream   As New ADODB.Stream

    adoConn.Open "provider=sqlxmloledb;data " & _
         provider=sqloledb;Server=(local);Database=northwind;UID=sa;PWD=;"
    Set adoCmd.ActiveConnection = adoConn
    adoCmd.CommandType = adCmdText
    adoCmd.CommandText = "select Customers.ContactName, " & _
       "Customers.ContactTitle, " & _
       "Customers.CompanyName, Customers.CustomerID," & _
       "Orders.OrderID, Orders.OrderDate, Orders.ShippedDate, " & _
       "[Order Details].UnitPrice, [Order Details].Quantity, " & _
       "Products.ProductName , Products.UnitsInStock " & _
       "From Customers, Orders, [Order Details], Products " & _
       "Where Customers.CustomerID = Orders.CustomerID " & _
       "and Orders.OrderID = [Order Details].OrderID " & _
       "and [Order Details].ProductID = Products.ProductID " & _
       "and Customers.CustomerID='GROSR' for XML RAW"
    
    adoStream.Open
    adoCmd.Properties("Output Stream").value = adoStream
    adoCmd.Execute , , adExecuteStream
   
    Debug.Print adoStream.ReadText

End Function

生成下面的 XML:

<row ContactName="Manuel Pereira" ContactTitle="Owner"  
   CompanyName="GROSELLA-Restaurante" CustomerID="GROSR" OrderID="10268" 
   OrderDate="1996-07-30T00:00:00" ShippedDate="1996-08-02T00:00:00" 
   UnitPrice="99" Quantity="10" ProductName="Th??ringer Rostbratwurst" 
   UnitsInStock="0"/>
<row ContactName="Manuel Pereira" ContactTitle="Owner" 
   CompanyName="GROSELLA-Restaurante" CustomerID="GROSR" OrderID="10268" 
   OrderDate="1996-07-30T00:00:00" ShippedDate="1996-08-02T00:00:00" 
   UnitPrice="27.8" Quantity="4" ProductName="Mozzarella di Giovanni" 
   UnitsInStock="14"/>
<row ContactName="Manuel Pereira" ContactTitle="Owner" 
   CompanyName="GROSELLA-Restaurante" CustomerID="GROSR" OrderID="10785" 
   OrderDate="1997-12-18T00:00:00" ShippedDate="1997-12-24T00:00:00" 
   UnitPrice="31" Quantity="10" ProductName="Ikura" 
   UnitsInStock="31"/>
<row ContactName="Manuel Pereira" ContactTitle="Owner" 
   CompanyName="GROSELLA-Restaurante" CustomerID="GROSR" OrderID="10785" 
   OrderDate="1997-12-18T00:00:00" ShippedDate="1997-12-24T00:00:00" 
   UnitPrice="7.75" Quantity="10" ProductName="Rh??nbr?¤u Klosterbier" 
   UnitsInStock="125"/>

好的,我们的结果是 XML,因此我们的第一个目标实现了。 但是,得到的 XML 片段存在几个问题。 首先,它不完全是我们感兴趣的 XML 形状。换句话说,XML 对于我们已定义的 XSD Customer 架构无效,因此,我们已经与我们的合作伙伴中断了协议。 第二,在得到的 XML 文档中,有一些重复信息。 特别是,该文档中包含重复的客户和定单信息。 由于 RAW 模式实际上只是行集合的 XML 表示形式,因此,在由联接生成的行集合内复制数据时会产生重复。 总之,尽管我们现在用 XML 文档来代替行集合是一件好事,但是这将不能满足我们的需要。

因此,让我们接着尝试使用 AUTO 模式。 AUTO 模式以嵌套 XML 元素形式返回查询结果。 From 子句中的每个表都被表示为一个 XML 元素。 列在 select 子句中的列都变成属性或子元素,具体情况取决于是否指定了 ELEMENTS 选项。

Public Function ForXMLAutoQuery()

    Dim adoConn     As New ADODB.Connection
    Dim adoCmd      As New ADODB.Command
    Dim adoStream   As New ADODB.Stream

adoConn.Open "provider=sqlxmloledb;data " & _
       "provider=sqloledb;Server=(local);Database=northwind;UID=sa;PWD=;"
    Set adoCmd.ActiveConnection = adoConn
    adoCmd.CommandType = adCmdText
    adoCmd.CommandText = "select Customers.ContactName, " & _
       "Customers.ContactTitle, " & _
       "Customers.CompanyName, Customers.CustomerID," & _
       "Orders.OrderID, Orders.OrderDate, Orders.ShippedDate, " & _
       "[Order Details].UnitPrice, [Order Details].Quantity, " & _
       "Products.ProductName , Products.UnitsInStock " & _
       "From Customers, Orders, [Order Details], Products " & _
       "Where Customers.CustomerID = Orders.CustomerID " & _
       "and Orders.OrderID = [Order Details].OrderID " & _
       "and [Order Details].ProductID = Products.ProductID " & _
       "and Customers.CustomerID='GROSR' for XML AUTO"
    
    adoStream.Open
    adoCmd.Properties("Output Stream").value = adoStream
    adoCmd.Execute , , adExecuteStream
   
    Debug.Print adoStream.ReadText

End Function

生成下面的 XML:

<Customers ContactName="Manuel Pereira" ContactTitle="Owner" 
CompanyName="GROSELLA-Restaurante" CustomerID="GROSR">
     <Orders OrderID="10268" OrderDate="1996-07-30T00:00:00" 
     ShippedDate="1996-08-02T00:00:00">
       <Order_x0020_Details UnitPrice="99" Quantity="10">
         <Products ProductName="Th??ringer Rostbratwurst"   
          UnitsInStock="0"/>
       </Order_x0020_Details>
       <Order_x0020_Details UnitPrice="27.8" Quantity="4">
         <Products ProductName="Mozzarella di Giovanni" 
          UnitsInStock="14"/>
       </Order_x0020_Details>
     </Orders>
     <Orders OrderID="10785" OrderDate="1997-12-18T00:00:00" 
      ShippedDate="1997-12-24T00:00:00">
       <Order_x0020_Details UnitPrice="31" Quantity="10">
         <Products ProductName="Ikura" UnitsInStock="31"/>
       </Order_x0020_Details>
       <Order_x0020_Details UnitPrice="7.75" Quantity="10">
         <Products ProductName="Rh??nbr?¤u Klosterbier" 
          UnitsInStock="125"/>
       </Order_x0020_Details>
       </Orders>
</Customers>

现在,结果似乎比从 RAW 模式获得的结果好得多。 在这种情况下,我们不再有重复的客户和定单数据,XML 的形状似乎更接近于由 Customer XSD 架构指定的形状;但是仍存在几个问题。 例如,在 XSD 架构和实例文档之间,元素名和属性名仍存在一定程度的不一致。 实际上,我们可以通过将别名用作查询的一部分来解决此问题。 例如,只要您在 select 子句中正确地设置列的别名,AUTO 模式就能自动知道您希望 CustomerID 列成为得到的 XML 中的 ID 属性。 

但是,还有另一个问题。 让我们看一下 Customer 元素。 在我们的 XSD 架构中,Customer 元素包含 Contact、Title 和 Company 子元素,而不包含以 XML 形式返回的属性。 如上所述,我们可以通过修改查询来指定 ELEMENTS 选项,但是该选项将所有的列都映射到子元素,而我们的 XSD 架构指定只有某些内容才应当成为子元素。 通过向得到的 XML 应用 XSLT 转换,可能会很方便地解决此问题,但是可通过某种方法来避免出现此问题。 为找到解决方法,让我们看一下 For XML EXPLICT 模式。

在 EXPLICIT 模式中,查询编写器控制通过执行查询而返回的 XML 文档的形状。 查询必须按照特定的方式编写,以便有关预期嵌套的其他信息被显式指定为查询的一部分。 您还可以使用指令来指定其他列级配置。 例如,用户可以指定给定的列是以元素形式还是以属性内容形式出现。

对于 EXPLICIT 模式以及如何在 SQL Server 中正确编写查询,有很好的联机丛书指导,因此,我们不打算在这里深入介绍其中的任何细节。 但是,我会让您看一眼将为 Customer XSD 架构生成有效 XML 的 EXPLICIT 模式查询:

select 1 as TAG,0 as parent,
   ContactName as [CustomerRecord!1!Contact!element],
   ContactTitle as [CustomerRecord!1!Title!element],
   CompanyName as [CustomerRecord!1!Company!element],
   CustomerID as [CustomerRecord!1!ID],
   NULL as [Order!2!Shipped],
   NULL as [Order!2!Ordered],
   NULL as [Order!2!OrderID],
   NULL as [Items!3!ProductID!hide],
   NULL as [Items!3!Quantity],
   NULL as [Items!3!Price],
   NULL as [ItemPurchased!4!Name!element],
   NULL as [ItemPurchased!4!NumberInStock!element]
 from Customers  WHERE CustomerID = 'GROSR'
 UNION ALL
 select 2,1, NULL, NULL, NULL, NULL,
   Orders.ShippedDate, Orders.OrderDate, Orders.OrderID,
   NULL, NULL, NULL, NULL, NULL
 from Orders, Customers  WHERE Orders.CustomerID = Customers.CustomerID 
AND Customers.CustomerID = 'GROSR'
 UNION ALL
 select 3,2, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
   [Order Details].productid,
   [Order Details].Quantity,
   [Order Details].UnitPrice,
   NULL, NULL
 from Orders, Customers, [Order Details]  WHERE Orders.CustomerID = 
Customers.CustomerID AND [Order Details].Orderid = Orders.OrderID
               AND Customers.CustomerID = 'GROSR'   
 UNION ALL
 select 4,3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
   NULL,
   Products.ProductName,
   Products.UnitsInStock
 from Orders, Customers, [Order Details], Products  WHERE 
Orders.CustomerID = Customers.CustomerID AND [Order Details].Orderid = 
Orders.OrderID
               AND [Order Details].ProductID = 
Products.ProductID AND Customers.CustomerID = 'GROSR'   
 FOR XML EXPLICIT

此过程似乎不是非常简单且易于维护的查询,对于这一点,我认为自己无需说服任何人。 但是,如果您确实喜欢使用 SQL 查询语言,而且希望能够将关系数据广泛地塑造为 XML,则 EXPLICIT 查询可能不失为一种可行的方法。 但是,对于大多数人来说,还有一个更方便的方法。

批注的映射架构

让我们回顾一下我们的 Customer XSD 架构。 最终目标是生成一个针对该架构进行验证的 XML。 让我们尝试将该架构直接映射到数据库结构,而不是通过编写 SQL 查询来生成文档。 

为此,SQL Server 已经向 XSD 架构语言中引进了许多批注,这些批注指定 XML 到关系的映射。 这些批注的基础包括 XSD 架构中的元素和属性与数据库中的表/视图和列之间的映射。 在默认情况下,批注架构中的元素名映射到指定数据库中的表(视图)名,属性名映射到列名。 这些批注还可用于指定 XML 中的分层关系(从而表示数据库中的关系)。

下面是我们的 Customer XSD 架构,但是它现在具有适当的映射批注:

<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
       xmlns:sql="urn:schemas-microsoft-com:mapping-schema">

    <xsd:element name="CustomerRecord" sql:relation="Customers" >
        <xsd:complexType>
            <xsd:sequence>
                <xsd:element name="Contact"  sql:field="ContactName" type="xsd:string" /> 
                <xsd:element name="Title" sql:field="ContactTitle" type="xsd:string" /> 
                <xsd:element name="Company"  sql:field="CompanyName" type="xsd:string" />
                <xsd:element ref="Order" sql:relationship="CustomerOrder" />
            </xsd:sequence>
                <xsd:attribute name="ID" sql:field="CustomerID" type="xsd:string" />   
        </xsd:complexType>
    </xsd:element>

    <xsd:element name="Order" sql:relation="Orders" >
        <xsd:complexType>
            <xsd:sequence>    
                <xsd:element ref="Items" sql:relationship="OrderOrderDetails" />
            </xsd:sequence>
            <xsd:attribute name="OrderID" type="xsd:int"/>     
            <xsd:attribute name="Ordered" sql:field="OrderDate" type="xsd:dateTime"/>
            <xsd:attribute name="Shipped" sql:field="ShippedDate" type="xsd:dateTime"/>
        </xsd:complexType>
    </xsd:element>

    <xsd:element name="Items" sql:relation="[Order Details]" >
        <xsd:complexType>
            <xsd:sequence>
                <xsd:element ref="ItemPurchased" 
sql:relationship="OrderDetailsProducts" />    
            </xsd:sequence>
            <xsd:attribute name="Price" sql:field="UnitPrice" type="xsd:int"/>     
            <xsd:attribute name="Quantity"  type="xsd:int"/>
        </xsd:complexType>   
    </xsd:element>

    <xsd:element name="ItemPurchased" type="Products" sql:relation="Products" />

    <xsd:complexType name="Products">
        <xsd:sequence>
            <xsd:element name="Name"  sql:field="ProductName" type="xsd:string" /> 
            <xsd:element name="NumberInStock"  sql:field="UnitsInStock" type="xsd:int" /> 
        </xsd:sequence>
    </xsd:complexType>

    <xsd:annotation>
        <xsd:appinfo>
            <sql:relationship name="CustomerOrder"                                              parent="Customers"                              parent-key="CustomerID"                              child="Orders"                              child-key="CustomerID" />            <sql:relationship name="OrderOrderDetails"                                              parent="Orders"                              parent-key="OrderID"                              child="[Order Details]"                              child-key="OrderID" />            <sql:relationship name="OrderDetailsProducts"                                            parent="[Order Details]"                              parent-key="ProductID"                              child="Products"                              child-key="ProductID" />
        </xsd:appinfo>
    </xsd:annotation>
   
</xsd:schema>

从本质上看,此处完成的所有任务就是提供一个 XML 到关系表的映射。 换句话说,这与让传统的关系视图接受 XML(而非行集合)结果非常相似。 实际上,通过构建这种映射,我已经开发了所谓的 XML 视图。 XML 视图可被视为传统数据库上的抽象层。 通过提供这种 XML 视图,用户能够以分层的半结构化方式来公开传统的关系数据,实际上,这种方式通常是用来公开业务对象的更自然的方式。 

让我们详细查看一下我在该架构中添加了哪些内容。 首先,我添加了 XML for SQL Server 命名空间并赋予它 sql 别名。 为了使映射批注能够被正确识别,您必须提供这个命名空间,并将它用于映射特定的所有批注。 接着,我使用 sql:field 批注将我知道的所有属性和元素都映射到数据库中的列。 请注意,有几个属性和元素与要映射到的数据库中的列同名。 在这些情况下,因为默认映射就已经足够,所以无需任何批注。 然后,我遍历了架构中的其余元素,并使用 sql:relation 批注将这些元素映射到数据库中的表。 由于存在范围规则,所以 sql:relation 批注还适用于该列映射到的所有属性和子元素。 最后,我需要指出如何联接映射到表的元素。 换句话说,创建 XML 视图的分层形式。 如果您查看架构的底部,就会发现我在 xsd:annotation 节中定义了三个联接关系,这些关系所指定的联接信息除了是用 sql:relationship 批注指定的以外,与 sql 查询中的联接信息完全相同。 为了在该文档的其他位置使用这些关系,我赋予每个关系一个别名。 随后,我使用 sql:relationship 别名,在“联接”元素时指定关系联接信息。 例如,我希望在 Customer 信息内部嵌套 Order 信息,因此我使用 CustomerOrder sql:relationship 来指定数据库中基础表之间的联接。

现在,我们已经为数据库中的 Customer 信息完全定义了 XML 视图。 它所采用的就是几个指向 XSD 架构的快速批注。 正如您所看到的那样,这似乎比前面的显式查询更简单且更易于维护。 说实话,XML for SQL Server 代码实际上是将您提供的映射信息映射成 For XML Explicit 模式查询。 

对于上面的示例,我们只使用了几个可用的映射批注。 另外还有几个批注,这些批注都为将关系数据公开为 XML 提供不同的方法。 例如,使用某些批注,可以添加 CDATA 节,提供常数元素,甚至公开不映射到数据库中特定列的开放式 XML 内容。 有关详细信息,请参阅 XML for SQL Server 联机丛书。

因此,我们已经将 XML 映射到我们的关系数据库结构,但是我们仍未生成示例文档。 XML 视图现在需要一个查询机制。

XPath 查询

传统的 SQL 查询是用来筛选和联接关系表和视图的好工具。 但是,XML 视图看上去一点都不像关系表和视图。 因此,XML 视图的查询机制是 XPath 查询语言,而非 SQL。 XPath(XML 路径语言)是一种图形导航语言。 XPath 用于从 XML 文档中选择一组节点。 每个 XPath 运算符都基于一个由以前的 XPath 运算符选择的节点集来选择一个节点集。 XML for SQL Server 支持 XPath 语言的子集,它允许用户针对由批注映射架构提供的虚拟 XML 视图执行查询。 因此,让我们尝试针对批注 XSD 架构来执行查询。 

由于 ADO 现在支持执行 XPath 查询,因此,我们可以使用同样的代码示例,只不过要进行几处改动:

 Public Function XPathQuery()

    Dim adoConn     As New ADODB.Connection
    Dim adoCmd      As New ADODB.Command
    Dim adoStream   As New ADODB.Stream

    adoConn.Open "provider=sqlxmloledb;data " & _
         provider=sqloledb;Server=(local);Database=northwind;UID=sa;PWD=;"
    Set adoCmd.ActiveConnection = adoConn
    adoCmd.CommandType = adCmdText
    adoCmd.Dialect = "{EC2A4293-E898-11D2-B1B7-00C04F680C56}"    adoCmd.Properties("Mapping Schema") = "customers.xsd"    adoCmd.CommandText = "/CustomerRecord[@ID='GROSR']"
    
    adoStream.Open
    adoCmd.Properties("Output Stream").value = adoStream
    adoCmd.Execute , , adExecuteStream
   
    Debug.Print adoStream.ReadText

End Function

首先,我们已经指定了一个不同的命令方言。 (默认方言是 SQL 方言)。 在这种情况下,我们指定命令文本是 XPATH 查询。 这可通过将命令方言的属性设置为代表 XPath 方言的全局唯一标识符 (GUID) 来完成。 接着,我们设置命令的映射方案属性,并为它赋予已批注架构的文件路径。 最后,我们将命令文本设置为一个 XPATH 查询,该查询将检索属性 ID 等于 GROSR 的所有 CustomerRecord 元素。 因此,下面列出返回的 XML 示例文档:

<CustomerRecord ID="GROSR">
   <Contact>Manuel Pereira</Contact>
   <Title>Owner</Title>
   <Company>GROSELLA-Restaurante</Company>
   <Order OrderID="10268" Ordered="1996-07-30T00:00:00" 
      Shipped="1996-08-02T00:00:00">
      <Items Price="99" Quantity="10">
         <ItemPurchased>
            <Name>Th??ringer Rostbratwurst</Name>
            <NumberInStock>0</NumberInStock>
         </ItemPurchased>
      </Items>
      <Items Price="27.8" Quantity="4">
         <ItemPurchased>
            <Name>Mozzarella di Giovanni</Name>
            <NumberInStock>14</NumberInStock>
         </ItemPurchased>
      </Items>
   </Order>
   <Order OrderID="10785" Ordered="1997-12-18T00:00:00" 
      Shipped="1997-12-24T00:00:00">
      <Items Price="31" Quantity="10">
         <ItemPurchased>   
            <Name>Ikura</Name>
            <NumberInStock>31</NumberInStock>
         </ItemPurchased>
      </Items>
      <Items Price="7.75" Quantity="10">
         <ItemPurchased>
            <Name>Rh??nbr?¤u Klosterbier</Name>
            <NumberInStock>125</NumberInStock>
         </ItemPurchased>
      </Items>
   </Order>
</CustomerRecord>

太棒啦! 我们已经解决了该问题。 该文档验证初始 Customer XSD 架构,并与所需的结构完全相同。 而且,在最初将 XSD 架构映射到数据库结构之后,查询 XML 视图实际上变得相当方便。 但是,我们指定的查询实际上相当简单,实际上甚至显示不出用 XPATH 针对分层 XML 执行查询的真实能力。 例如,如果我希望使用同一个 XML 视图,但是只需要返回给定客户购买的商品列表,那该怎么办呢? 用 XPATH 即可相当方便地找到解决方案,我只需将查询修改为:

/CustomerRecord[@ID='GROSR']/Order/Items/ItemPurchased
    
这会生成下面的 XML:

<ItemPurchased>
   <Name>Th??ringer Rostbratwurst</Name>
   <NumberInStock>0</NumberInStock>
</ItemPurchased>
<ItemPurchased>
   <Name>Mozzarella di Giovanni</Name>
   <NumberInStock>14</NumberInStock>
</ItemPurchased>
<ItemPurchased>
   <Name>Ikura</Name>
   <NumberInStock>31</NumberInStock>
</ItemPurchased>
<ItemPurchased>
   <Name>Rh??nbr?¤u Klosterbier</Name>
   <NumberInStock>125</NumberInStock>
</ItemPurchased>

新查询基本上是选择 ID 等于 GROSR 的所有 ItemPurchased 元素。 生成的新文档实际上是由映射架构定义的虚拟 XML 视图的一个子节。 而且,其中所生成的 For XML 显式查询足够智能,可以仅检索构造作为查询结果返回的 XML 视图的节所需的关系数据,在本例中,返回的是产品表中的几行。 换句话说,所有的节点选择和筛选都发生在服务器上。

因此,对于这些结果来说,唯一的问题就是它不是结构完善的 XML,因为其中包含多个根元素。 之所以出现这种情况,通常是因为 XPATH 查询的结果可以是一组元素。 但是,我们可以按照下列两种方法之一来解决此问题: 我们可以使用 XML Root ADO 命令属性(只是围绕查询结果添加一个具有指定名称的根元素),也可以使用一个 XML 模板。

XML 模板是一个 XML 文件,其中可以包含多个 XML 生成命令和静态 XML 文本。 在处理某个 XML 模板时,模板中的每个命令节点都执行,而且结果都内联到得到的 XML 中。 该模板还可以指定一个后处理 XSLT 脚本。 通过使用 XML 模板,用户可以一起批处理多个 XML 生成命令(XPATH 和/或 For XML)并创建复杂的 XML 文档。 让我们执行一个模板,其中包含一个给定客户的所有 ItemsPurchase,以及与该客户一起工作的雇员列表:

 Public Function TemplateQuery()

    Dim adoConn     As New ADODB.Connection
    Dim adoCmd      As New ADODB.Command
    Dim adoStream   As New ADODB.Stream

    adoConn.Open "provider=sqlxmloledb;data _
    provider=sqloledb;Server=(local);Database=northwind;UID=sa;PWD=;"
    Set adoCmd.ActiveConnection = adoConn
    adoCmd.CommandType = adCmdText
    adoCmd.Dialect = "{5D531CB2-E6Ed-11D2-B252-00C04F681B71}"
    adoCmd.CommandText = "<ROOT xmlns:sql=""urn:schemas-microsoft-com:xml-sql"" >" & _    "<sql:query>" & _    " select employees.Firstname, employees.Lastname  from employees, orders" & _    " where employees.employeeid = orders.employeeid and orders.Customerid = 'GROSR'" & _    " for XML auto" & _  "</sql:query>" & _  "<sql:xpath-query mapping-schema=""Customers.xsd"">" & _   " /CustomerRecord[@ID='GROSR']/Order/Items/ItemPurchased" & _  "</sql:xpath-query>" & _" </ROOT>"
    
    adoStream.Open
    adoCmd.Properties("Output Stream").value = adoStream
    adoCmd.Execute , , adExecuteStream
   
    Debug.Print adoStream.ReadText

End Function   

生成下面的 XML:

<ROOT xmlns:sql="urn:schemas-microsoft-com:xml-sql">
   <employees Firstname="Laura" Lastname="Callahan"/>
   <employees Firstname="Nancy" Lastname="Davolio"/>
   <ItemPurchased>
      <Name>Th??ringer Rostbratwurst</Name>
      <NumberInStock>0</NumberInStock>
   </ItemPurchased>
   <ItemPurchased>
      <Name>Mozzarella di Giovanni</Name>
      <NumberInStock>14</NumberInStock>
   </ItemPurchased>
   <ItemPurchased>
      <Name>Ikura</Name>
      <NumberInStock>31</NumberInStock>
   </ItemPurchased>
   <ItemPurchased>
      <Name>Rh??nbr?¤u Klosterbier</Name>
      <NumberInStock>125</NumberInStock>
   </ItemPurchased> 
</ROOT>

好的,我们现在获得了有效的 XML。 另外,我们已经知道如何组合几个不同的 XML 生成查询,以便生成一个新的、更具扩展性的 XML 视图。 实际上,模板还可以由参数驱动,因此,我们可以使 CustomerID 成为一个参数,并将同样的值传递给这两个查询,这实际上会创建一个生成存储过程的 XML。 因此,通过使用批注映射架构和模板,我们已经创建了一个简单却功能强大的 XML 视图查询机制。 但是,查询只是事情的一半。 如果我希望插入或更改 XML 视图,那该怎么办呢? 

Updategram 和 XML Bulkload

XML for SQL Server 提供两个将所做的更改存储到 XML 视图的机制: Updategram 和 XML Bulkload。 正如对于 XPath 一样,这两个机制都使用批注映射架构来将 XML 转换为关系表。 如果您需要批量插入大量 XML,则可以使用 XML Bulkload 工具。 XML Bulkload 是一个可编写脚本的 COM 组件,它类似于传统的 BCP,但是其来源是 XML 文件,根据您的映射架构,它可以包括复杂的分层数据。 

如果要对 XML 视图进行更多的类似于 DML 的更改,可以使用 updategram。 Updategram 通过 XML 模板执行,它只是另一种模板命令节点类型。 在 updategram 中,用户指定 before XML 和 after XML 实例。 然后,基于映射方案,所做的更改会被转换为相应的 sql insert/ update/delete 语句。 例如,如果某个 CustomerRecord 元素出现在 before 块中,但不出现在 after 块中,则会生成一个针对 Customer 表的 delete sql 语句。 下面的示例 updategram 用来更新一个客户并插入一个定单:

<ROOT xmlns:updg="urn:schemas-microsoft-com:xml-updategram">
<updg:sync mapping-schema="Customers.XSD" >
  <updg:before>
      <CustomerRecord ID="GROSR">
<Contact>Manuel Pereira</Contact>
<Title>Owner</Title>
<Company>GROSELLA-Restaurante</Company>
             </CustomerRecord>   
  </updg:before>

  <updg:after>
      <CustomerRecord ID="GROSR">
<Contact>Manuel Pereira</Contact>
<Title>Co-Owner</Title>
<Company>GROSELLA-Restaurante</Company>
<Order OrderID="10268" Ordered="2000-07-30T00:00:00" 
Shipped="2000-08-02T00:00:00"/>
   </CustomerRecord>   
  </updg:after>
</updg:sync>
</ROOT>

这里需要注意几件事情。 首先,updategram 必须在模板中包括适当的命名空间 (urn:schemas-microsoft-com:xml-updategram)。 其次,before 块和 after 块被包含在转换为单个关系事务的 sync 块中。 换句话说,如果您对尝试存储到数据库中的 XML 视图进行了多次更改,而且其中的一个 sql 语句失败,则整个事务将被回滚。 而且,updategram 使用开放式并发来确保更新数据在从数据库中被读出之后,没有被另一个用户应用程序修改。 这可通过在 updategram 中将这些值包括在 updategram 的 <before> 块中来完成。 在更新数据库之前,updategram 针对数据库中的当前值来检查在 <before> 块中指定的值,以便确保更新有效。 之所以使用这种并发控件,是因为使用 XML 视图的大多数客户端都是断开连接的客户端。 实际上,在针对 XML 视图执行查询之后,您实际上就提取了数据的快照,因此无从知道它是否发生过更改。 对于大多数 Internet 应用程序情形来说,这是一个设计现实。

结论

您现在学习了 XML for SQL Server 101。 希望您已经对 SQL Server 所提供的 XML 功能略知一二,并对如何在应用程序域中使用 XML 视图有了一些概念。 这实际上只是一个调查分类,某些用户或许对学习更多内容或者甚至主修 XML for SQL Server 感兴趣。 一些用户可能想知道在何处报名参加 200 级课程。 您可以访问 MSDN XML 站点:http://www.msdn.microsoft.com/xml 。 在该站点,您将找到有关 XML for SQL Server Web Releases 的信息以及指向最新产品更新的链接。 新版本将每隔几个月推出,因此,请经常查看该站点,看是否有产品更新。 请注意,这些不是类似于版本更新的 Service Pack,而是完整的、受支持的新版本的 XML for SQL Server,其中包含整个新功能集。 好的,本教授必须指出这个占用期版本如何工作。 祝您好运并创造出完善的映射。

posted on 2006-01-26 10:57  kkk  阅读(290)  评论(0编辑  收藏  举报