发布时间:2014-08-01 00:00 来源:未知
一、System.Xml、SQLXML 和 XML 数据类型简介
本节简要介绍 Microsoft SQL Server 2000 中提供的 XML 支持的发展历史,并概要介绍 Microsoft Visual Studio 2005/SQL Server 2005 环境中提供的用于处理 XML 和关系数据的三个选项。这三个选项是:1) System.Xml 命名空间中的类,2) SQLXML 类,3) SQL Server 2005 中提供的 XML 数据类型。
XML 支持最早添加到 Microsoft SQL Server 2000 中,为用户提供以下功能:
• |
将关系数据作为 XML 公开 |
• |
将 XML 文档拆分到行集合 |
• |
通过使用 XML 数据精简 (XDR) 方案,将 XML 方案映射到数据库方案,从而创建 XML 视图 |
• |
使用 XPath 在 XML 视图上创建查询 |
• |
在 SQL Server 中通过 HTTP 公开数据 |
此支持在 SQLXML Web 的后续版本中得到了进一步增强。增强功能包括:
• |
更新程序和 XML 海量加载可以保留对 XML 视图的更改 |
• |
支持带批注的 XML 方案定义语言 (XSD),以便描述映射(仍然支持 XDR,但不建议使用) |
• |
客户端的 FOR XML |
• |
SQLXML 托管类 |
• |
支持 Web 服务 |
Microsoft .NET Framework 1.0 对读取、写入和处理 XML 文档提供了广泛的支持。这种支持在 .NET Framework 2.0 中得到了进一步增强,提高了各种 XML 类的性能和可用性。.NET Framework 在 System.Xml 命名空间中提供的新类可用于在 XML 数据与关系数据之间进行相互映射。
SQLXML 是一组使 SQL Server 数据库中的关系数据能够与 XML 无缝集成的库和技术。它是一个中间层组件,不包括由 FOR XML 和 OPENXML 提供的服务器端 XML 支持。SQLXML 提供了一个方案驱动的映射方法,能够从关系源数据生成 XML 并将表示关系信息的 XML 加载回关系表中。SQLXML 类对 SQL Server 2000(及更高版本)的数据库提供了 XML 支持。
Microsoft SQL Server 2005 以 XML 数据类型的形式添加了内置的 XML 支持。XML 数据可以存储在 XML 数据类型列内部。另外,通过将一个 XML 方案集合与此 XML 数据类型列关联,还可以对其进行进一步的限制。存储在 XML 数据类型列中的 XML 值可以借助 XQuery 和 XML 数据修改语言 (DML) 进行处理。可以在 XML 数据上建立索引,以增强查询性能。此外,FOR XML 和 OPENXML 也已得到增强,能够支持新的 XML 数据类型。
SQL Server 2005 中引入的存储和处理 XML 数据的新功能与 SQL Server 早期版本中提供的 XML 功能结合在一起,为开发人员提供了多种在 XML 应用程序中存储和处理 XML 数据的方法。由于使用 SQL Server 2005 提供的方法,有多种方法可以生成 XML 应用程序,因此,了解各种不同技术的方案,以及如何在各种技术之间进行权衡和配合对于作出正确的选择是至关重要的。本文提供了如何选择适当的方法,使用 SQL Server 2005 开发 XML 应用程序的指南。
二、XML 使用方案XML 的应用领域可大致划分为:
• |
用于商业集成的 XML:商业集成,也称为企业应用集成 (EAI),包括 A2A(应用程序对应用程序)、B2B(企业对企业)以及 B2C(企业对用户)应用程序。运行在不同系统上的应用程序使用基于 XML 的消息相互通讯。 |
• |
用于内容管理的 XML:使用基于 XML 的内容管理系统,用户可以存储、检索、修改和查询 XML 文档。这些系统以其原有格式存储 XML 文档。 |
下面介绍符合上述分类的几种方案。这些方案的解决方案将在以下几节中给出,并且会详细介绍 SQL Server 2005/Visual Studio 2005 环境中提供的各种 XML 选项的处理情况。
方案 1:保险理赔
一家汽车保险公司在 Internet 上提供服务,允许其投保人或代理通过公司的网站进入保险理赔。理赔将由位于公司总部的中央系统进行处理。处理完毕后,系统会将与该理赔相关的信息以指定的 XML 格式存储起来。系统中必须保留这些 XML 文档的精确副本,以便用于法律用途。此方案展示了 XML 在内容管理中的应用。
方案 2:汽车制造商与零件供应商之间的数据交换 I
一家汽车制造商与多家零件供应商进行交互,以便获得公司所需的零件。通常,该制造商要接收供应商的发票。然后,将与发票相关的数据手动输入到老式的发票处理系统中。发票处理系统以关系格式存储数据。而现在,该制造商希望将发票数据自动输入到老式的发票处理系统中。此方案是 XML 用于商业集成的示例。
方案 3:汽车制造商与零件供应商之间的数据交换 II
此方案包含一家与多家零件供应商进行交互的汽车制造商,如前一方案中所述。该制造商目前使用的系统不能为供应商提供查询发票状态或者从制造商获得付款说明的副本的功能。目前,供应商只能通过电话获得这些信息。该汽车制造商需要通过 Web 公开这些信息,以使供应商能够自动执行这些任务。此方案展示了 XML 在商业集成中的应用。
方案 4:内容管理系统
某公司通过 Web、书籍和 CD-ROM 等多种渠道向其客户提供医学、法律和技术等领域的信息。该公司要建立一套内容管理系统,以帮助其在较短的时间内向客户提供高质量的内容。此方案展示了 XML 在内容管理中的应用。
方案 5:客户调查
某公司在 Internet 上提供机票预订业务,要对每个季节进行调查,以确定当前季节最受客户喜爱的旅游地点。每个季节所用的调查表均不同,而且将来调查表也可能改变。该公司将对调查信息进行分析,并根据分析结果设计出满足大多数客户要求的旅游套餐。此方案可看作 XML 在内容管理中的应用。
三、.NET Framework 中的 XML 类Microsoft .NET Framework 对开发基于 XML 的产品提供了卓越的支持。在 .NET Framework 中,XmlTextReader、XmlTextWriter、XmlDocument 和 XmlValidatingReader 等核心类可以在 System.Xml 命名空间中获得,该命名空间是所有 XML 类的根命名空间。使用这些核心类,用户可以使用基于数据流和基于 DOM(基于文档对象模型)的两种浏览/访问模型来读取、写入和验证 XML 文档。System.Xml 命名空间包含以下子命名空间:
• |
System.Xml.Schema - 包含用于处理 XML 方案定义语言 (XSD) 方案的类。 |
• |
System.Xml.Serialization - 提供将对象序列化为 XML 格式的文档或数据流的类。 |
• |
System.Xml.XPath - 包括使用 XPath 表达式浏览 XML 文档的类。 |
• |
System.Xml.Xsl - 具有用于执行可扩展样式表转换 (XSLT) 的类。 |
System.Xml 命名空间的增强功能
在 Visual Studio 2005 中,新类(如 XsltCommand)和对现有 XML 类(如 XmlDocument)的增强可用于对 XML 数据进行各种操作,包括修改 XML 文档、应用 XSL 转换等。
Visual Studio 2005 中与 System.Xml 命名空间中的 XML 类相关的一些增强功能如下:
• |
XmlDocument 类添加了 XML 方案验证支持。 |
• |
XmlReader 和 XmlWriter 类的功能得到了增强,性能显著提高,并支持 XML 方案类型。而且,还添加了静态 Create 方法,提供了一种较为简单的方法,可使用 XmlReaderSettings 和 XmlWriterSettings 类创建 XmlReader 和 XmlWriter 实例,以便配置所创建的类型。 |
有关 System.Xml 增强功能的详细信息,请参见 What's New in System.Xml for Visual Studio 2005 and the .NET Framework 2.0 Release 白皮书。
System.Xml 命名空间中的类可用于实现自定义的 XML 分析、处理和存储逻辑。利用 SQL Server 2005 的公共语言运行库 (CLR) 的宿主功能以及 Visual Studio 2005 中的 XML 类,可以在中间层或数据库层处理 XML。
.NET Framework 中的 XML 类的使用包括将 XML 文档作为类型 [n]varchar(max) 或 [n]varbinary(max) 的列保存在数据库中,或者作为文件保存在文件系统中,以及使用 System.Xml 命名空间中的类在中间层或数据库层处理这些文档。.NET Framework 中的 XML 类还可用于操作以 XML 数据类型存储的数据。
.NET Framework 中的 XML 类适用于以下情况:
• |
访问 .NET Framework XML 的所有功能,如数据流分析、文档类型定义 (DTD)、XSD 验证、XSLT 处理等。 |
• |
仅仅将 SQL Server 用作 XML 文档的数据存储区,不需要在数据库内部进行深入访问。 |
• |
使用 .NET Framework 中的 XML 类进行 XML 文档的大多数或全部处理并在文档级进行更新。 |
可以使用 [n]varchar(max)、[n]varbinary(max) 或 XML 数据类型来存储 XML。
假如使用 [n]varchar(max) 或 [n]varbinary(max),可以获得以下好处:
• |
可以保留 XML 文档的高保真的副本,包括空格和格式。 |
• |
应用程序在对整个文档进行插入和检索操作方面可以获得可能实现的最高性能。 |
后面将介绍使用 XML 数据类型的好处。
在中间层进行 XML 处理
可以使用 .NET Framework 提供的各种 XML 类在中间层进行 XML 处理。如上所述,采用这种方法时,XML 文档可以作为类型 [n]varchar(max) 的列或 XML 存储在数据库中,也可以作为文件存储在文件系统中。在中间层,这些文档是从数据库中检索并根据用户的需要进行处理的,如下所述:
• |
假如需要读取 XML 文档,可以使用通过 XmlReader.Create() 方法创建的 XmlReader 来加载从数据库中获得的文档。使用 Read() 浏览文档。XmlReader 类提供对 XML 文档的最快的只读、只向前和非缓冲的访问。 |
• |
假如需要对 XML 文档进行写入访问并需要对 XML 数据进行完全浏览访问,则可以使用 XmlDocument 类加载和访问 XML 文档。XmlDocument 是文档对象模型 (DOM) 在 .NET Framework 中的一种实现,它是 XML 文档在内存中的树型表示,使用它可以浏览和编辑此文档。 |
• |
假如需要根据 DTD/XSD 来验证 XML 文档,或者在运行时获得 XSD 信息,则可以使用 XmlReader 类。在 XmlReaderSettings 类上,创建方法时将 XsdValidation 或 DTDValidation 设置为 True。还可以设置 ValidationEventHandle() 事件处理程序来处理读取过程中出现的验证错误。 |
• |
假如需要对 XML 文档应用 XSL 转换,可以使用 XPathDocument 类加载 XML 文档,然后使用 XslCommand 类应用转换。XPathDocument 类使用 XSLT 为 XML 文档处理提供了快速的高性能缓存。 |
• |
假如需要使用 XPath 表达式来查询 XML 文档,则可以使用 XPathDocument(只读)或 XmlDocument(读/写)来加载 XML 文档。使用 CreateNavigator() 方法创建一个 XPathNavigator 实例,并将所需的 XPath 表达式作为参数传递给 XPathNavigator 的 Select() 方法。 |
在数据库中进行 XML 处理
SQL Server 2005 与 CLR 的集成使开发人员也能够使用 .NET Framework 提供的 XML 类在数据库层进行处理。这种集成提供了以 .NET Framework 支持的任何语言编写存储过程、函数、触发器以及用户定义的类型的功能。此外,CLR 宿主还提供了访问整个 .NET Framework 基类库的功能。因此,上一节中介绍的各种 XML 处理选项也可以在数据库中使用。
使用 CLR 集成的优点如下:
• |
它提供了使用面向对象语言(如 C# 和 Visual Basic .NET)以托管代码的形式编写数据库对象的能力。 |
• |
托管的数据库对象比 SQL Server 早期版本中提供的扩展存储过程更安全。 |
• |
它具有定义用户定义的数据类型和用户定义的合计的能力。 |
• |
在某些情况下,编译的托管数据库对象比 Transact-SQL 具有更高的性能。 |
在 SQL Server 2005 中,数据库开发人员可以选择两种方法来编写存储过程、触发器和用户定义的函数。这两种方法是 Transact-SQL 和可在 .NET Framework 中使用的任何语言,如 C# 或 Visual Basic .NET。语言的选择取决于对数据执行的操作类型。假如代码在进行大多数数据访问时只需要使用很少甚至不使用过程逻辑,选择 Transact-SQL 最合适。托管类最适用于数学性较强的函数和过程,如字符串处理、日期操作、系统资源访问、文件访问、图像处理等。
在数据库层使用 .NET Framework 中的 XML 类的步骤如下:
• |
开发托管程序集。使用可在 .NET Framework 中使用的任何语言,以程序集的形式实现处理功能,并将程序集打包为 DLL。程序集也可以引用其他程序集。 |
• |
注册程序集并授予权限。使用 .NET Framework 开发的程序集可以使用 CREATE ASSEMBLY T-SQL 语句注册到 SQL Server 中。注册程序集时,还可以指定要授予该程序集的代码访问权限。使用 DROP ASSEMBLY T-SQL 语句可以取消程序集的注册。 |
• |
向 T-SQL 公开托管类型。程序集提供的处理功能可以通过用户定义的标量函数、用户定义的表函数、用户定义的过程 (UDP) 或者用户定义的触发器向 T-SQL 公开。用户定义的标量函数可在任何标量表达式中使用;用户定义的表函数可在任何 FROM 子句中使用;UDP 可在 EXEC 语句中调用。 |
分析方案
保险理赔包含以数据为主的信息(如理赔 ID、政策编号、理赔结算数据等)和以文档为主的信息(如事故损伤说明等)。XML 文档在汇集以数据为主和以文档为主的信息时功能非常强大。当前方案的主要要求是需要以 XML 格式保留一份保险理赔的精确副本。使用 SQL Server,通过将保险理赔作为类型 [n]varchar(max) 或 [n]varbinary(max) 的列存储在数据库中的方法,很容易满足这项要求。值得注意的是,假如需要保留无关紧要的空格、属性顺序、命名空间前缀以及 XML 声明等信息,则不应使用 XML 数据类型存储文档。
优点
使用 [n]varchar(max) 或 [n]varbinary(max) 作为存储媒介,使用 System.Xml 命名空间中的类处理 XML 文档的优点如下:
• |
可以灵活地对 XML 文档的方案进行更改。在同一列中使用不同的方案存储 XML 文档时非常有用。 |
• |
当使用 [n]varchar(max) 或 [n]varbinary(max) 存储 XML 时,能提供高保真的 XML 文档。此功能对于处理法律文档(如保险理赔)的应用程序可能是必不可少的。 |
• |
与将 XML 实例作为文件存储在文件系统中相比,还可以利用数据库的功能,如事务更新、并发访问、备份、复制等。 |
• |
由于这种方法不依赖于数据库提供的 XML 支持,因此很容易对应用程序进行扩展,以支持多种数据库服务器,如 SQL Server、Oracle 等。 |
• |
可以使用客户端系统的处理能力,从而减轻了服务器的负载。通过在中间层进行 CPU 密集的 XML 处理,减轻了服务器的负载,使其能够用于其他重要的任务。 |
• |
在文档级进行插入和检索操作方面提供了可能实现的最佳性能。 |
• |
复杂的操作(如 XSL 转换)可以作为存储过程、触发器或函数在数据库中完成。 |
限制
使用 [n]varchar(max) 或 [n]varbinary(max) 进行存储,使用 System.Xml 命名空间中的类处理 XML 实例的限制总结如下:
• |
与 XML 数据类型(请参见 SQL Server 2005 中的 XML 数据类型一节)或 SQLXML 选项(请参见 SQLXML 一节)相比,编码的复杂性较高。即使数据库逻辑非常简单,在中间层或数据库层用代码来实现 XML 的分析和处理也很复杂。 |
• |
实现此解决方案的代码量很大。因此,与 SQLXML 选项相比,维护成本也较高。 |
• |
由于 XML 文档作为 [n]varchar(max) 存储在数据库中,因此不能对 XML 文档进行深入地更新、插入或删除操作。查询能力也有限。 |
• |
以 [n]varchar (max) 数据类型存储的 XML 文档最大不能超过 2GB。 |
• |
根据 XML 内容搜索以这种方法存储的文档列的代价非常高。 |
使用 .NET Framework 中的 XML 类的示例
考虑本文前面介绍的保险理赔方案。一旦理赔获得批准,保险公司将存储理赔信息,以便用于法律用途。理赔信息可以作为 [n]varchar(max) 数据类型存储在数据库中。
应用程序的流程如下:
1. |
处理理赔后,应用程序批准或拒绝理赔。 |
2. |
使用 System.Xml 命名空间中的类,为理赔生成 XML 文档。 |
3. |
将生成的 XML 文档发送到存储过程。 |
4. |
存储过程将 XML 文档插入到表中。 |
以下代码示例使用系统获得的理赔详细信息生成一个 XML 文档并将该 XML 文档插入到数据库中。
using System; using System.Xml; using System.IO; using System.Text; using System.Data; using System.Data.SqlClient; namespace InsuranceClaim { class Insurance { static void Main(string[] args) { Insurance.InsertInsuranceClaim(); } static void InsertInsuranceClaim() { StringWriter strWriter = null; XmlWriter writer = null; XmlWriterSettings settings = null; SqlConnection connection = null; SqlCommand command = null; try { strWriter = new StringWriter(); settings = new XmlWriterSettings(); //使用缩进功能以增强可读性。 settings.Indent = true; settings.Encoding = System.Text.Encoding.UTF8; writer = XmlWriter.Create(strWriter, settings); //编写 XML 声明。 writer.WriteStartDocument(); writer.WriteStartElement("InsuranceClaim"); writer.WriteStartElement("ClaimInfo"); writer.WriteElementString("ClaimID", "C1234"); writer.WriteElementString("ClaimType", "3"); writer.WriteStartElement("SettlementDetails"); writer.WriteStartElement("PaymentDetails"); writer.WriteElementString("PaidTo", "Jeff"); writer.WriteElementString("Amount", "2000"); writer.WriteElementString("Date", "05/12/2002"); writer.WriteElementString("ApprovedBy", "Mike"); writer.WriteEndElement();//PaymentDetails 结束 writer.WriteEndElement();//SettlementDetails 结束 writer.WriteEndElement();//ClaimInfo 结束 writer.WriteStartElement("DamageReport"); writer.WriteString("Minor accident occured on "); writer.WriteElementString("Address", "ABC Street, Sample City, Sample State"); writer.WriteString(" due to "); writer.WriteElementString("Cause", "bad weather"); writer.WriteString(" resulted in damage to "); writer.WriteElementString("DamagedItem", "Head Lights"); writer.WriteElementString("DamagedItem", "Engine"); writer.WriteEndElement();//DamageReport 结束 writer.WriteEndElement();//InsuranceClaim 结束 writer.WriteEndDocument(); //将 XML 写入文件并关闭写入程序。 writer.Flush(); connection = new SqlConnection(); connection.ConnectionString = @"server=localhost; database=AdventureWorks;Integrated Security=SSPI;"; command = connection.CreateCommand(); command.CommandText = "InsertInsuranceClaim"; command.CommandType = System.Data.CommandType.StoredProcedure; command.Parameters.Add("@CustomerID", System.Data.SqlDbType.Char); command.Parameters.Add("@Claim", System.Data.SqlDbType.VarChar); String xml = strWriter.ToString(); string strCustomerID = "1001"; command.Parameters[0].Value = strCustomerID; command.Parameters[0].Size = strCustomerID.Length; command.Parameters[1].Value = xml; command.Parameters[1].Size = xml.Length; connection.Open(); command.ExecuteNonQuery(); connection.Close(); } finally { if (connection.State == ConnectionState.Open) connection.Close(); if (writer != null) writer.Close(); if (strWriter != null) strWriter.Close(); } } } }
下面是用于创建数据库表的脚本:
CREATE TABLE [InsuranceClaim]( [CustomerID] [char](4) NOT NULL, [Claim] [varchar](max) NOT NULL, [ModifiedDate] [datetime] NOT NULL DEFAULT (getdate()) )
以下存储过程用于将 XML 文档插入到数据库中。
CREATE PROCEDURE [dbo].[InsertInsuranceClaim] @CustomerID [char](4), @Claim [varchar](max) AS BEGIN SET NOCOUNT ON; INSERT INTO [InsuranceClaim] ( CustomerID, Claim ) VALUES ( @CustomerID, @Claim ) END;四、SQLXML
SQLXML 是与 SQL Server 2000 一起引入的,它包含在客户端处理 XML 所需的全部功能。它是一组使 SQL Server 数据库中的关系数据能够与 XML 描述的关系结构数据无缝集成的库和技术。
在 SQL 2000 问世之前,开发人员必须为关系数据与 XML 格式的数据之间的交互提供代码层。SQLXML 的出现使这种操作变得很简单,因为它在关系数据与 XML 之间提供了链接。本文只讨论 SQLXML 托管类。有关该库其他功能的适用性的详细信息,请参见 MSDN 网站上的 SQLXML 页。
SQLXML 包含很多在 SQL Server 中引入的支持 XML 的功能。包括:
• |
在客户端将查询结果转换为 XML | ||||||||
• |
使用带批注的 XSD 映射方案文件创建关系数据的 XML 视图,使您能够:
| ||||||||
• |
使用 HTTP 访问 SQL Server,使您能够:
| ||||||||
• |
以基于 SOAP 的 Web 服务形式公开存储过程、用户定义的函数以及模板查询所提供的功能。 | ||||||||
• |
在 .NET Framework 中编写代码,以便利用 SQLXML 通过 SQLXML 托管类所提供的 XML 功能 |
客户端 XML 格式化。在客户端指定 FOR XML 子句,可以使中间层在由服务器返回的行集合上进行 FOR XML 转换,以响应查询。要在客户端进行 XML 格式化:
• |
假如使用 SQLXML 托管类,则将 SqlXmlCommand 对象的 ClientSideXml 属性设置为 True。 |
• |
假如使用 SQLXMLOLEDB 提供程序,则将 ClientSideXML 提供程序特有的属性设置为 True。 |
• |
假如使用模板查询,则在模板中指定 client-side-xml="1"。 |
• |
假如使用 HTTP 访问 SQL Server,则在 Settings(设置)选项卡中的虚拟目录下选择 Run on the Client(在客户端运行)选项。 |
• |
在客户端上,FOR XML 提供的有效 XML 格式化模式为 RAW、NESTED 和 EXPLICIT。使用 RAW 模式时,所得到的 XML 文档在查询结果的每一行中都包含一个 XML 元素,而与行对应的每一列中都包含一个属性。当指定 NESTED 模式时,基表名将作为所得到的 XML 文档的元素名返回。EXPLICIT 模式允许在查询中指定所需的 XML 格式,因此能够生成任何格式的 XML 文档。 |
• |
XML 视图。XML 视图是使用带批注的 XSD 方案创建的,该方案定义了关系数据与 XML 数据之间的映射。可以使用 XPath 查询对这些 XML 视图进行查询。通过 XML 视图公开的关系数据也可以被修改,然后使用更新程序将所进行的修改提交到数据库。此外,XML 视图还可借助基于 COM 的 XML Bulk Load(XML 海量加载)对象将大量 XML 文档插入到数据库中。 |
• |
使用 HTTP 访问 SQL Server。SQLXML 提供了一个名为“IIS 虚拟目录管理”的工具,该工具可用于设置一个 IIS 虚拟目录,通过 HTTP 公开 SQL Server 的 XML 功能。借助 SQL ISAPI 扩展的功能,它支持在 URL 中直接指定 SQL 语句、存储过程、模板查询、模板文件以及 XPath 查询。 |
• |
SQLXML 中的 Web 服务支持。SQLXML 3.0 中增加了一项功能,即支持以基于 SOAP 的 Web 服务形式公开 SQL Server 的功能。此功能使 SQL Server 能够从客户端接收 SOAP HTTP 请求,以便执行存储过程、用户定义的函数和模板。 |
• |
SQLXML 托管类。使用 SQLXML 托管类,可以在 .NET Framework 中访问 SQLXML 功能。SQLXML 中有三种托管类: |
• |
SqlXmlCommand - 处理数据库连接和查询执行 |
• |
SqlXmlParameter - 帮助指定查询中的参数 |
• |
SqlXmlAdapter - 便于与 .NET Framework 中的数据集进行交互 |
使用 SQLXML 托管类,您可以:
• |
使用 FOR XML 子句执行 SQL 查询 |
• |
针对映射方案执行 XPath 查询 |
• |
执行模板查询 |
• |
执行模板查询文件 |
• |
执行更新程序 |
• |
执行区分程序 |
使用 SQLXML 将关系数据作为 XML 文档公开非常适用于以下情况:
• |
应用程序接收结构良好且能较好地映射到关系表中的 XML 数据。 |
• |
应用程序必须将来自外部应用程序的大量 XML 文档加载到数据库中,并保持其关系格式。 |
• |
应用程序不需要保留文档的顺序。 |
• |
应用程序需要以不同的格式向多个数据使用者提供相同的数据。 |
• |
DML 操作的性能对于应用程序至关重要。 |
• |
应用程序需要充分利用优化程序进行查询优化。 |
• |
应用程序需要进行深入的数据操作。 |
• |
应用程序需要以 XML 的形式公开现有的关系数据。 |
分析方案
在 XML 使用方案中介绍的第一个数据交换方案(请参见方案 2:汽车制造商与零件供应商之间的数据交换 I 一节),一个汽车制造商与多个零件供应商之间的交互是使用 SQLXML 的典型案例。制造商必须与不同的供应商进行通讯以便交换发票数据。建议的解决方案使用 Web 服务和 SQLXML 来解决此问题。制造商公开一个 Web 服务,供应商可使用该服务将发票发送给制造商。Web 服务使用了一个针对客户的 XSLT,将发票由供应商的格式转换为制造商使用的通用格式。然后,Web 服务使用 XML 视图来分解 XML 文档,将发票文档的内容映射到关系表中的列。老式的发票处理系统将能够从关系表中选取数据并进行处理。在此方案中使用 XML 视图的优点如下:
• |
降低了维护成本。通过修改针对供应商的 XSLT 文件,很容易适应供应商对发票方案所进行的任何更改。 |
• |
与 FOR XML 相比,编码的复杂程度降低了(请参见关系/XML 集成的服务器端支持 (FOR XML/OPENXML) 一节)。 |
• |
通过创建针对供应商的 XSLT 文件,很容易支持新的供应商。 |
优点
使用 SQLXML 的优点总结如下:
• |
与服务器端的 FOR XML EXPLICIT 相比,创建带批注的映射方案将关系数据映射到 XML 数据是一种相对简单、易于维护的解决方案。 |
• |
SQLXML 能够创建可更新的双向 XML 视图,而使用 FOR XML 只能创建关系数据的只读 XML 表示形式。 |
• |
映射 XSD 便于适应 XML 格式的更改请求,而不必进行主代码的更改。这会使维护简单易行。 |
• |
SQLXML 允许用户通过将 SqlXMLCommand 类的 ClientSideXML 属性设置为 True,在客户端进行 XML 格式化,从而减轻服务器的负载。 |
限制
从不利的方面来看,从客户端使用 SQLXML 有一些限制:
• |
XML 视图不适用于 XML 文档的层次结构过多或者递归深度未知的情况。 |
• |
SQLXML 不适用于包含混合内容标记和有序数据的说明性文档,例如产品目录、新闻报告等。 |
• |
由于不能保留文档顺序,很难重新构造出原始的 XML 文档。 |
• |
虽然将 XML 文档分解到关系表中提供了卓越的搜索性能,但是在关系数据与 XML 之间进行相互转换的代价非常昂贵。 |
• |
当在 XSD 映射方案中使用默认映射时,可能会公开数据库表名和列信息,这可能无意中造成信息泄漏。这种风险可以通过为表和列指定明确的映射加以避免。 |
• |
URL 中的 SQL 语句只能用于可信的局域网中。在 Internet 上使用这种查询可能导致潜在的安全问题。 |
使用 SQLXML 的示例
以上概要介绍了 SQLXML 的功能,下面将详细分析一个可以应用 SQLXML 托管类的示例。考虑一个简单的示例 - 为特定客户导出销售订单的详细信息。该示例所用的表可以在 AdventureWorks 数据库中获得。
数据库中的数据必须能够在客户端以 XML 格式获得,并能被表示层显示。下面您将看到,如何使用 SQLXML 类将 SQL 数据库中的关系数据作为 XML 数据进行处理。该示例使用映射 XML 方案进行处理,并将 XML 节点名映射到表字段。有关使用 SQLXML 托管库处理关系数据的详细信息,请参见 MSDN 网站上的SQLXML页。
以下带批注的 XSD 方案定义了关系表 [Sales.Customer]、[Sales.SalesOrderHeader] 和 [Sales.SalesOrderDetail] 与客户销售订单详细信息的目标 XML 表示之间的映射。还可以使用 XSD 映射方案来定义 XML 中的父子关系,如以下 XSD 方案所示。
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:sql="urn:schemas-microsoft-com:mapping-schema"> <xsd:annotation> <xsd:appinfo> <sql:relationship name="CustomerOrderHeader" parent="Sales.Customer" parent-key="CustomerID" child="Sales.SalesOrderHeader" child-key="CustomerID" /> <sql:relationship name="OrderHeaderOrderDetail" parent="Sales.SalesOrderHeader" parent-key="SalesOrderID" child="Sales.SalesOrderDetail" child-key="SalesOrderID" /> </xsd:appinfo> </xsd:annotation> <xsd:element name="Customer" sql:relation="Sales.Customer" > <xsd:complexType> <xsd:sequence> <xsd:element name="Order" sql:relation="Sales.SalesOrderHeader" sql:relationship="CustomerOrderHeader" maxOccurs="unbounded" > <xsd:complexType> <xsd:sequence> <xsd:element name="OrderDetail" sql:relation="Sales.SalesOrderDetail" sql:relationship="OrderHeaderOrderDetail" maxOccurs="unbounded" > <xsd:complexType> <xsd:attribute name="SalesOrderID" type="xsd:integer" /> <xsd:attribute name="ProductID" type="xsd:integer" /> <xsd:attribute name="OrderQty" type="xsd:integer" /> </xsd:complexType> </xsd:element> </xsd:sequence> <xsd:attribute name="SalesOrderID" type="xsd:integer" /> <xsd:attribute name="CustomerID" type="xsd:integer" /> <xsd:attribute name="OrderDate" type="xsd:date" /> <xsd:attribute name="ShipDate" type="xsd:date" /> </xsd:complexType> </xsd:element> </xsd:sequence> <xsd:attribute name="CustomerID" type="xsd:integer" /> </xsd:complexType> </xsd:element> </xsd:schema> class ExportOrders { /// <摘要> /// 此方法使用 SqlXmlCommand 类从 /// Sales.Customer、Sales.SalesOrderHeader 和 Sales.SalesOrderDetail /// 表中选择记录。数据是从服务器获取并在客户端 /// 格式化为 xml 的。请注意,ClientSideXml 设置为 True。 /// <摘要> static void Main(string[] args) { if (args.Length < 1) { Console.WriteLine("Usage"); Console.WriteLine("CustomerOrders <CustomerID> [OrderID]"); return; } try { StringBuilder strBuilder = new StringBuilder(); strBuilder.Append("/Customer[@CustomerID='"); strBuilder.Append(args[0]); strBuilder.Append("']"); if (args.Length > 1) { strBuilder.Append("/Order[@SalesOrderID='"); strBuilder.Append(args[1]); strBuilder.Append("']"); } SqlXmlCommand xmlCommand = new SqlXmlCommand(@"Provider= SQLOLEDB; Server=localhost; database=AdventureWorks; Integrated Security=SSPI;"); xmlCommand.ClientSideXml = true; xmlCommand.RootTag = "CustomerOrders"; xmlCommand.SchemaPath = @"CustomerOrderDetails.xsd"; xmlCommand.CommandType = SqlXmlCommandType.XPath; xmlCommand.CommandText = strBuilder.ToString(); Stream reader = xmlCommand.ExecuteStream( ); FileStream fsOut = File.Create("CustomerOrder.xml"); StreamWriter sw = new StreamWriter(fsOut); using (StreamReader sr = new StreamReader(reader)) { sw.Write(sr.ReadToEnd()); } sw.Flush(); sw.Close(); fsOut.Close(); } catch (Exception exception) { Console.WriteLine( exception.ToString() ); } } }
上述方法为作为应用程序命令行参数指定的客户 ID 导出了销售订单的详细信息。数据在客户端转换为 XML 格式,从而避免了服务器端的性能问题。请注意,上述带批注的 XSD 方案映射必须保存为 CustomerOrderDetails.xsd,才能使上述代码片段正常运行。
注意:从此示例中可以看出,用于从数据库中检索 XML 数据的代码非常少。
五、关系/XML 集成的服务器端支持 (FOR XML/OPENXML)SQL Server 通过 SELECT 语句的 FOR XML 扩展功能,支持在服务器端以 XML 文档的形式返回 SQL 查询结果。另外,关键字 OPENXML 还能够提供从 XML 文档中提取行集合的能力。
FOR XML
服务器端的 FOR XML 支持四种 XML 转换模式 - RAW、AUTO、EXPLICIT 和 PATH。
默认情况下,RAW 模式将查询结果集中的每一行映射到一个 XML 元素,并将行中的每一列映射到一个属性。当使用 ROW 模式指定 ELEMENTS 选项时,行中的每一列将被映射到为该行生成的元素的子元素中。还可以通过指定 XMLSCHEMA 选项为所生成的 XML 请求一个内联方案。
AUTO 模式支持生成嵌套的 XML 元素,默认情况下,FROM 子句中的每个表(在 SELECT 子句中至少列出一列)将映射到一个 XML 元素,SELECT 子句中的列将映射到属性(假如指定 ELEMENTS 选项,则映射到子元素)。
EXPLICIT 模式能够最大限度地控制由查询结果生成的 XML 的格式。使用它,您可以通过在查询中指定所需的 XML 格式来生成任何格式的 XML。
使用 EXPLICT 模式编写复杂的 XML 文档是相当繁琐的。假如不想编写复杂的 EXPLICIT 模式请求,还可以使用 PATH 模式,该模式还具有编写嵌套 FOR XML 查询和 TYPE 指令以返回 XML 类型实例的能力。PATH 模式通过将列名解释为类似 XPath 的语法,将 SELECT 查询返回的行集合中的列映射到属性和子元素。有关 SQL Server 2005 中 FOR XML 增强功能的详细信息,请参见 What's New in FOR XML in Microsoft SQL Server 2005 白皮书。
OPENXML
OPENXML 与 sp_xml_preparedocument 和 sp_xml_removedocument 系统存储过程共同提供了 XML 文档的关系行集合视图。为了对 XML 文档使用 OPENXML,必须使用 sp_xml_preparedocument 创建 XML 文档的一个内存中表示。此存储过程使用 MSXML 分析器分析 XML 文档,并向 XML 文档返回一个可用于 OPENXML 的句柄。现在,XML 文档句柄、行模式(将 XML 数据的节点映射到行的 XPath 表达式)、行集合方案以及行集合列与 XML 节点之间的映射等参数都可以传递到 OPENXML 以获得行集合。当不再需要 XML 文档时,必须使用 sp_xml_removedocument 存储过程将其从内存中卸载。
FOR XML 的增强功能
在 SQL Server 2005 中,FOR XML 增强了以下功能:
• |
使用新的 TYPE 指令转换 FOR XML 结果的类型 |
• |
将 FOR XML 的结果分配给各种类型的 XML |
• |
对 FOR XML 查询进行嵌套处理,以生成 XML 层次结构 |
• |
使用新的 PATH 模式生成复杂的 XML 文档 |
• |
使用 XMLDATA 和 XMLSCHEMA 选项分别生成 XDR 或 XSD 格式的内联方案 |
• |
在 RAW 模式下使用 ELEMENTS 指令生成以元素为主的 XML |
• |
在 ELEMENT 指令中使用 XSINIL 选项,将 NULL 值映射到属性为 xsi:nil="true" 的元素 |
OPENXML 的增强功能
在 SQL Server 2005 中,OPENXML 的功能得到了增强,支持以下功能:
• |
将 XML 类型数据传递到 sp_xml_preparedocument |
• |
在 WITH 子句中使用新数据类型 |
使用 FOR XML 和 OPENXML 编写和分解 XML 文档非常适用于以下情况:
• |
应用程序需要按照一定的关系存储数据,并将此信息以 XML 的形式公开给另一个应用程序。 |
• |
应用程序不需要保留 XML 的顺序。 |
• |
应用程序要执行大量元素级 DML 操作。 |
• |
应用程序需要进行深入的数据访问和更新。 |
• |
应用程序需要通过 Web 服务公开关系数据。 |
分析方案
方案 3 中的要求(请参见方案 3:汽车制造商与零件供应商之间的数据交换 II 一节)是提供一种 Web 服务,使供应商可以使用该服务获得发票的状态或者获得付款说明的副本。FOR XML 与 Web 服务结合,提供了一种解决方案,允许制造商在 Internet 上公开这些服务。供应商可以使用 Web 服务查询其发票的状态。然后,Web 服务将使用供应商提供的发票 ID,并使用 FOR XML 语句从关系数据中生成 XML 格式的响应。生成的 XML 文档将返回给供应商。在当前方案中,使用基于 FOR XML 语句的方法具有以下优点:
• |
FOR XML 提供了一种从关系数据中动态编写简单 XML 文档的简单方法。 |
• |
当用于编写简单的 XML 文档时,FOR XML 查询较 XML 视图更易于维护。 |
优点
使用 FOR XML/OPENXML 的优点如下:
• |
FOR XML 提供了一种在服务器上从关系数据生成 XML 的简单方法。 |
• |
FOR XML 提供了通过 Web 服务公开商业信息的能力。 |
• |
使用 OPENXML,可以将行集合以 XML 格式传递给存储过程,这将使您能够在一个网络往返中进行大量的 INSERT、UPDATE 和 DELETE 操作。 |
• |
FOR XML 与 XSL 结合,可用于应用程序集成或商业集成。 |
限制
使用 FOR XML/OPENXML 编写和分解 XML 文档的限制如下:
• |
使用 FOR XML 的 EXPLICT 选项构造 XML 的结构非常困难。 |
• |
很难维护使用 FOR XML EXPLICIT 编写的复杂查询。 |
• |
由 FOR XML AUTO 生成的 XML 文档可能会公开数据库表名和列信息,导致无意中泄漏信息。这种情况可以通过为表和列指定别名加以避免。 |
使用 FOR XML 和 OPENXML 的示例
以下示例使用 SQL Server 2005 附带的 AdventureWorks 数据库。此示例使用 FOR XML 为指定范围内的客户从 [Sales.Customer]、[Sales.SalesOrderHeader]、[Production.Product] 和 [Sales.SalesOrderDetail] 表中获得客户、订单以及订单详细信息等信息。
示例:使用 FOR XML
SELECT Cust.CustomerID, OrderHeader.CustomerID, OrderHeader.SalesOrderID, Detail.SalesOrderID, Detail.LineNumber,Detail.ProductID, Product.Name, Detail.OrderQty FROM Sales.Customer Cust, Sales.SalesOrderHeader OrderHeader, Sales.SalesOrderDetail Detail, Production.Product Product WHERE Cust.CustomerID = OrderHeader.CustomerID AND OrderHeader.SalesOrderID = Detail.SalesOrderID AND Detail.ProductID = Product.ProductID AND (Cust.CustomerID BETWEEN 44 AND 46) ORDER BY OrderHeader.CustomerID, OrderHeader.SalesOrderID FOR XML AUTO
查询结果如下:
<Cust CustomerID="44"> <OrderHeader CustomerID="44" SalesOrderID="53575"> <Detail SalesOrderID="53575" LineNumber="2" ProductID="952" OrderQty="2"> <Product Name="Chain" /> </Detail> <Detail SalesOrderID="53575" LineNumber="1" ProductID="969" OrderQty="1"> <Product Name="Touring-1000 Blue, 60" /> </Detail> <Detail SalesOrderID="53575" LineNumber="3" ProductID="972" OrderQty="1"> <Product Name="Touring-2000 Blue, 54" /> </Detail> </OrderHeader> <OrderHeader CustomerID="44" SalesOrderID="59024"> <Detail SalesOrderID="59024" LineNumber="1" ProductID="972" OrderQty="3"> <Product Name="Touring-2000 Blue, 54" /> </Detail> <Detail SalesOrderID="59024" LineNumber="2" ProductID="957" OrderQty="2"> <Product Name="Touring-1000 Yellow, 60" /> </Detail> </OrderHeader> </Cust> <Cust CustomerID="46"> <OrderHeader CustomerID="46" SalesOrderID="48354"> <Detail SalesOrderID="48354" LineNumber="1" ProductID="730" OrderQty="1"> <Product Name="LL Road Frame - Red, 62" /> </Detail> </OrderHeader> </Cust>
以下示例使用 OPENXML 和 XPath 表达式提取 XML 文档中指定的订单详细信息。
示例:使用 OPENXML
DECLARE @XmlDocumentHandle int DECLARE @XmlDocument nvarchar(max) SET @XmlDocument = N'<ROOT> <Cust CustomerID="44"> <OrderHeader CustomerID="44" SalesOrderID="53575"> <Detail SalesOrderID="53575" LineNumber="2" ProductID="952" OrderQty="2"> <Product Name="Chain" /> </Detail> <Detail SalesOrderID="53575" LineNumber="1" ProductID="969" OrderQty="1"> <Product Name="Touring-1000 Blue, 60" /> </Detail> <Detail SalesOrderID="53575" LineNumber="3" ProductID="972" OrderQty="1"> <Product Name="Touring-2000 Blue, 54" /> </Detail> </OrderHeader> <OrderHeader CustomerID="44" SalesOrderID="59024"> <Detail SalesOrderID="59024" LineNumber="1" ProductID="972" OrderQty="3"> <Product Name="Touring-2000 Blue, 54" /> </Detail> <Detail SalesOrderID="59024" LineNumber="2" ProductID="957" OrderQty="2"> <Product Name="Touring-1000 Yellow, 60" /> </Detail> </OrderHeader> </Cust> <Cust CustomerID="46"> <OrderHeader CustomerID="46" SalesOrderID="48354"> <Detail SalesOrderID="48354" LineNumber="1" ProductID="730" OrderQty="1"> <Product Name="LL Road Frame - Red, 62" /> </Detail> </OrderHeader> </Cust> </ROOT>'
-- 创建 XML 文档的内部表示。
EXEC sp_xml_preparedocument @XmlDocumentHandle OUTPUT, @XmlDocument -- 使用 OPENXML 行集合提供程序执行 SELECT 语句。 SELECT * FROM OPENXML (@XmlDocumentHandle, '/ROOT/Cust/OrderHeader/Detail',2) WITH (CustomerID varchar(10) '../@CustomerID', OrderID int '../@SalesOrderID', LineNumber int '@LineNumber', ProductID int '@ProductID', Quantity int '@OrderQty')
-- 删除内部表示。
EXEC sp_xml_removedocument @XmlDocumentHandle
查询结果如下:
-------------------------------------------------------- CustomerID OrderID LineNumber ProductID Quantity -------------------------------------------------------- 44 53575 2 952 2 44 53575 1 969 1 44 53575 3 972 1 44 59024 1 972 3 44 59024 2 957 2 46 48354 1 730 1 --------------------------------------------------------六、SQL Server 2005 中的 XML 数据类型
XML 数据的层次结构特性使其很难被建模成关系数据,因为这会使数据结构变得非常复杂(如层次的深度将增加等)。而且,当 XML 数据映射到关系数据时,也无法保留 XML 实例中的元素顺序,因此从分解得到的关系数据编写原始的 XML 文档代价相当高。由于关系模型对存储 XML 数据有诸多限制,因此在内部存储 XML 实例是一种较理想的方法。内部 XML 实例不会受关系模型的限制,并能提供多种功能,例如能够处理层次结构或嵌套数据、能够保留元素顺序、能够直接存储和检索 XML 数据以及能够灵活地支持多种方案等。
Microsoft SQL Server 2005 为 XML 数据处理提供了广泛支持。使用 SQL Server 2005,XML 值可存储在内部的 XML 数据类型列中,可以根据 XML 方案的集合设置其类型,也可以不设置类型。还可以通过 XQuery 和 XML DML 支持深入的数据处理,后者是针对数据修改的扩展。此外,还可以为 XML 列创建索引,以提高查询性能。
有类型的 XML
有类型的 XML 非常适用于拥有描述 XML 数据的 XML 方案的情况。在这些情况下,可以将 XML 方案的集合与 XML 列关联,以生成有类型的 XML。对 XML 类型列的验证是根据与该列关联的 XML 方案集合进行的。另外,包含有类型的 XML 数据的查询比包含无类型的 XML 数据的查询的性能要好,因为它不需要在运行时转换节点值。
无类型的 XML
无类型的 XML 适用于有方案但不想让服务器验证数据的情况,或者没有方案的情况。在下列情况下,即使有方案,可能也要存储无类型的 XML:
• |
没有固定的方案 |
• |
在服务器端存储数据之前,已在客户端进行验证 |
• |
临时存储根据方案确定无效的 XML 数据 |
• |
使用服务器端不支持的方案组件(如 key/keyref) |
即使无类型的 XML 文档不与任何方案关联,系统仍要对其进行检查,以确保其格式良好。值得注意的是,由于无类型的 XML 需要在运行时转换节点值,其性能会受到一定的影响,因为节点值是作为 Unicode 字符串内部存储的。
XML 数据类型的使用方案
使用 SQL Server 2005 中的新 XML 数据类型,可以实现以下功能:
• |
创建既包含关系列,又包含有 XML 类型列的表。 |
• |
通过与 XML 方案集合关联,创建有类型的 XML 列类型。 |
• |
对涉及其他 XML 或非 XML 类型列的 XML 列进行限制,以强制实现业务规则。 |
• |
创建可用于存储 XML 数据类型实例的 XML 类型的变量。 |
• |
为存储过程或用户定义的函数创建 XML 类型的参数。 |
• |
从用户定义的函数中返回 XML 类型值。 |
• |
将使用新的 TYPE 指令获得的 FOR XML 查询结果分配给 XML 类型的变量。 |
• |
运行 XQuery 子集,在 XML 结构内查询并转换 XML 数据。 |
• |
根据 XML 类型的列创建计算列。 |
• |
为 XML 类型的列创建 XML 索引,以提高查询性能。 |
• |
使用 XML DML 对 XML 实例进行元素级的插入、删除和更新操作。 |
• |
将 XML 类型数据的实例传递到 sp_xml_preparedocument,以准备 XML 文档的内存中表示。 |
• |
使用 XQuery 和 XML DML 编写包含关系和 XML 列的跨域查询。 |
• |
使用 CAST 和 CONVERT,分别将 XML 类型转换为 varchar 或 nvarchar 类型。 |
• |
使用 CAST 或 CONVERT,将字符串数据类型(如 [n]varchar、[n]text、varbinary 和 image)转换为 XML 类型。 |
XML 数据类型方法和 XML DML
可以通过五种方法对 XML 数据类型列进行查询和处理。使用 XML 数据类型的 query() 方法,可以提取 XML 文档的片段;query() 方法接受 XQuery 表达式作为参数,并返回无类型的 XML 实例;使用 value() 方法,通过指定 XQuery 表达式以及需要返回的 SQL 类型,可以从 XML 实例中提取标量值;使用 exist() 方法,可以检查 XML 实例是否存在;使用 nodes() 方法,可以方便地将 XML 文档分解为关系数据。
使用 modify() 方法,可以对 XML 实例进行数据处理。通过 XQuery 中添加的 insert、delete 和 update 关键字提供了对 XML DML 的支持。使用 insert、delete 和 update 关键字可以分别插入、删除和更新一个或多个节点。
XML 索引
假如 XML 实例非常大,那么在对 XML 数据类型列执行查询处理操作时所进行的分析和分解可能要持续相当长的时间。在这些列上创建索引将提高对 XML 数据类型的查询性能。XML 数据的大小和使用方案对确定所需的索引类型非常重要。SQL Server 支持两种类型的索引 - 主 XML 索引和次 XML 索引;没有前者,后者就不能存在。
在 XML 列上创建主 XML 索引,将分解 XML BLOB 并将这些值存储在一个内部表中。这样,由于运行时不必进行分解,就可以提高运行时的查询性能。根据使用方案的不同,还可以创建次 XML 索引,进一步提高查询性能。可以创建三种类型的次 XML 索引 - PATH、PROPERTY 和 VALUE,分别提高路径、属性和值的查询性能。有关为 XML 类型列选择适当的次索引的方法,请参见“Performance Optimizations for the XML Data Type”白皮书。
将 XML 文档存储为 XML 数据类型非常适用于以下情况:
• |
应用程序需要保留 XML 实例的信息集内容。XML 文档的信息集内容包括文档层次结构、元素顺序、元素值及属性等。应用程序不保留属性顺序、命名空间前缀、无关紧要的空格以及 XML 声明等信息。 |
• |
应用程序需要对 XML 文档进行元素级的修改和查询操作。 |
• |
应用程序需要 XML 数据类型列的索引,以加快查询处理。 |
• |
XML 数据不一定有方案。 |
• |
应用程序使用各种结构的 XML 文档,或者 XML 文档要符合难以映射到关系结构的各种不同或复杂方案。 |
分析方案
分析方案:内容管理系统
现在,让我们来分析一下 XML 使用方案中介绍的内容管理系统。出版公司要处理文本、图像、音频、视频等各种形式的信息。可独立使用的信息块是从各种来源收集并在数据库中进行维护的。这些信息块被称为“组件”。将各个组件组装起来即可创建文档。文档中要包括哪些组件取决于用户的需要。这些文档将通过各种渠道发送给预订的用户。内容管理系统通常需要具有高性能、高可缩放性地存储、检索、搜索和更新内容的能力。
XML 作为一种统一标准的数据模型提供了一种极好的方法,可以在同一个文档中同时存储 XML 数据和 XML 内容。XML 还具有将显示方式与数据本身隔离的能力,这一点非常重要,因为相同的信息用于不同用户的显示方式可能不同。SQL Server 2005 提供的内置 XML 数据类型可以满足这种内容管理系统的需要。通过 XML 数据类型,可以存储 XML 文档,使用 XML DML 在元素级修改 XML 文档,以及使用 XQuery 对 XML 文档进行查询。
分析方案:客户调查
在客户调查 XML 使用方案中,主要要求是存储具有多种方案的调查信息。不能使用一个关系表为没有固定方案的数据建模。包含 XML 列的关系表是存储此类信息的最佳选择。还可以为关系表另外添加一列,用于存储调查类型。通过使用调查类型列提取该调查类型的所有记录,可以对与某种类型调查相关的信息进行分析。在一般的调查中,用户通常不会回答所有问题。因此,与其创建多个列(每列对应调查中的一个问题)并在数据库中为未回答的问题存储 NULL 值,倒不如将每个客户的调查信息作为 XML 文档存储在一列中。将客户的调查信息作为 XML 类型列存储更适合此方案,因为:
• |
使用 XML 类型列,不同方案的调查信息可以存储在一个 XML 类型列中。将 XML 类型列与 XML 方案集合关联,这样用户就可以为多个调查类型存储数据。 |
• |
调查信息可以在用户界面上进行验证,而不必将此信息作为有类型的 XML 数据类型列进行存储,从而实现数据库级的验证。 |
• |
XQuery 可用于执行数据分析。 |
优点
在 XML 数据类型列中存储 XML 数据的优点总结如下:
• |
XML 数据类型提供了一种在服务器上存储 XML 数据的简单直观的方法,同时还保留了文档顺序和文档结构。这一点特别适用于文档顺序和文档结构非常重要的文档。假定一个简单的方案:应用程序从某个数据源获得了一个 XML 文档,并且要存储该文档。将其存储在 nvarchar 或 text 列都不能保证 XML 的格式良好,而且不能轻易访问其内容。在这种情况下,最好将传入的 XML 文档存储在内部的 XML 列中。 |
• |
XML 数据类型能够对 XML 数据进行深入的查询和修改操作。在 SQL Server 2005 之前,没有办法在数据库内部存储 XML。因此,假如要修改或查询 XML 数据,必须从 nvarchar 或 text 列中加载数据,由字符串创建 XML 文档,然后才能进行修改。将修改过的数据写回数据库的步骤与此类似。而现在使用 XML 数据类型,这些步骤就变得非常简单。 |
• |
使用 XML 数据类型,可以为 XML 数据类型列创建索引,从而加快查询处理。 |
• |
使用 XML 数据类型时,可以对 XML 数据使用 XML 方案集合和限制,从而强制实现业务规则。XML 方案可用于验证数据,添加基于类型的操作语义,在编写查询和数据修改语句过程中进行比无类型的 XML 更精确的类型检查,并且可以优化存储和查询处理。 |
• |
由于 XML 类型的数据存储在数据库中,因此它还具有很多数据库的功能,如备份和恢复、SQL Server 安全性、事务、日志等。 |
限制
以下是使用新的 XML 数据类型时必须了解的一些限制:
• |
不能存储精确的数据副本。不能保留无关紧要的空格、命名空间前缀、属性顺序和 XML 声明。 | ||||
• |
XML 文档的层次最多为 128 层。 | ||||
• |
XML 文档的内部二进制表示最大为 2GB。 | ||||
• |
XML 实例不能进行比较。因此:
| ||||
• |
XML 不能转换为 text、ntext 和 image 数据类型,因为在 SQL Server 2005 中不能使用这些类型。但是,XML 数据类型可以转换为 [n]varchar 和 [n]varbinary 类型。 |
使用 XML 数据类型的示例
示例应用程序使用 AdventureWorks 数据库中的 Sales.Store 表。Sales.Store 表包含 CustomerID 作为主键,包含 Demographics 作为 XML 列。Demographics 列包含存储调查信息。存储调查中存储的信息是可选的。这意味着,Demographics 列不一定包含所有元素。假如相同的信息已存储为关系格式,则需要将这些元素创建为表的列。由于大多数存储调查信息是可选的,因此对于大多数数据,这些列中将包含 NULL 值。这将导致表空间的浪费。为了避免这种浪费,将 Demographics 列中的存储调查信息存储为 XML 格式。Demographics 列包含每个客户的销售信息,即年销售额、年收入及银行名称等。这些字段作为 XML 元素存储在数据中。
示例应用程序具有以下功能:
• |
显示所有客户及其调查信息的列表。 |
• |
显示特定客户的调查信息。 |
• |
向 Sales.Store 表中插入新客户及其调查信息。 |
• |
修改给定客户的调查信息的某些元素(如年销售额、年收入等)。 |
• |
删除给定客户的调查信息。 |
应用程序使用 System.Data.SqlTypes.SqlXml 类从 XML 列中检索数据。SQLXML 类是对 XML 列的直接映射。
使用 SQLXML 类,可以直接从 XML 列检索数据,而不需要任何映射或转换。
下面,让我们看一个如何为客户 ID 12 检索年收入元素的示例。以下代码示例展示了上述的第二种功能。
Public void RetreiveAnnualRevenue () { SqlConnection conn = new SqlConnection(); conn.ConnectionString = @"Server=localhost; Database=AdventureWorks; integrated security=SSPI"; conn.Open(); SqlCommand command = conn.CreateCommand(); command.CommandText = @"select Demographics.query( 'declare namespace SS="http://schemas.microsoft.com/sqlserver/2004/07/adventure-works/StoreSurvey" <StoreInfo> <AnnualRevenue> {data(/SS:StoreSurvey/SS:AnnualRevenue)} </AnnualRevenue> </StoreInfo>') as Result from Sales.Store where CustomerID=12"; SqlDataReader datareader = command.ExecuteReader(); System.Text.StringBuilder builder = new System.Text.StringBuilder(); While (datareader.Read()) { SqlXml sqlxml = datareader.GetSqlXml(0); builder.Append(sqlxml.Value); } //注意:xml1 是一个 XML Web 控件 this.xml1.DocumentContent = builder.ToString(); this.xml1.TransformSource = @"StoreInfo.xslt"; }
上述方法在 AdventureWorks 数据库中创建了一个 SqlConnection 对象实例。命令文本属性包含为客户 ID 12 检索年收入字段值的查询。
这是一个 XQuery,将在 XML 列上直接执行。查询的返回值将是一个 XML 片段,该片段将映射到 SQLXML 类。然后,可以使用 SQLXML 类的 Value 属性检索该 XML 片段。
检索到的 XML 片段将使用 XML Web 服务器控件显示在客户端应用程序中。
七、不同方法的比较功能 | .NET Framework 中的 XML 类 | FOR XML / OPENXML | SQLXML | XML 数据类型 |
代码的复杂性 |
高。没有类可以在 XML 数据和关系数据之间直接进行映射。 |
中。使用 FOR XML EXPLICIT 编写查询非常困难。 |
低。SQLXML 类提供了一种将关系数据作为 XML 数据进行处理的机制,而且更新程序还可以方便地更新记录。 |
低。由于 XML 数据同样也存储在列中,因此复杂性很低。另外,Visual Studio 2005 还提供了处理 XML 数据类型的类。XML DML 可用于修改 XML 数据。 |
可维护性 |
复杂。对表中字段或 XML 的更改需要更改代码。 |
困难。对表中字段或 XML 的更改需要更改查询。 |
容易。在大多数情况下,修改映射 XSD 文件即可适应更改。 |
容易。XQuery 提供了在数据库中查询 XML 列的简单语法。 |
安装 |
除 .NET Framework 外,不需要进行其他安装。 |
不需要进行其他安装。 |
需要在客户端计算机上安装 SQLXML 库。 |
不需要进行其他安装。 |
安全性 |
在很大程度上是安全的,因为通常情况下,数据类型和格式不会在客户端公开。 |
假如适当注意避免泄漏表名和列名,则是安全的。 |
假如映射 XSD 文件存储在客户端而不是在中间层,则在设计时应考虑安全因素。 |
安全 |
支持 .Net Compact Framework |
有限的支持。Microsoft .NET Compact Framework 不支持 XmlDataDocument。 |
支持 |
不支持 |
不支持。假如 SQL Server 中的 XML 数据类型列与 SQL Server Mobile 同步,则它将转换为 ntext。 |
数据验证 |
可由客户端和服务器实施。 |
可由服务器实施。 |
可由客户端实施。 |
可由服务器使用 XML 方案实施。 |
数据存储 |
[n]varchar(max)、XML 或 [n]varbinary(max) |
关系表(可将 XML 作为字段使用)。 |
关系表(可将 XML 作为字段使用)。 |
XML 数据类型 |
保真度 |
高保真(保留字节级的 XML 数据) |
关系保真(保留数据的层次结构,但忽略元素的顺序) |
关系保真 |
信息集保真(保留 XML 数据的信息集内容) |
存储数据的访问和更新 |
支持文档级更新。 |
支持深入的数据访问和更新。 |
支持深入的数据访问和更新。 |
支持深入的数据访问和更新。 |
本文为用户提供了在 SQL Server 2005 中处理 XML 的各种选项,并对 System.Xml 命名空间、SQLXML 和 XML 数据类型进行了讨论,介绍了其相关的优点和局限性,并给出了示例方案。根据本文介绍的理想方案的性能,用户可以为自己的应用程序选择适当的 XML 选项。
一、System.Xml、SQLXML 和 XML 数据类型简介
本节简要介绍 Microsoft SQL Server 2000 中提供的 XML 支持的发展历史,并概要介绍 Microsoft Visual Studio 2005/SQL Server 2005 环境中提供的用于处理 XML 和关系数据的三个选项。这三个选项是:1) System.Xml 命名空间中的类,2) SQLXML 类,3) SQL Server 2005 中提供的 XML 数据类型。
XML 支持最早添加到 Microsoft SQL Server 2000 中,为用户提供以下功能:
• |
将关系数据作为 XML 公开 |
• |
将 XML 文档拆分到行集合 |
• |
通过使用 XML 数据精简 (XDR) 方案,将 XML 方案映射到数据库方案,从而创建 XML 视图 |
• |
使用 XPath 在 XML 视图上创建查询 |
• |
在 SQL Server 中通过 HTTP 公开数据 |
此支持在 SQLXML Web 的后续版本中得到了进一步增强。增强功能包括:
• |
更新程序和 XML 海量加载可以保留对 XML 视图的更改 |
• |
支持带批注的 XML 方案定义语言 (XSD),以便描述映射(仍然支持 XDR,但不建议使用) |
• |
客户端的 FOR XML |
• |
SQLXML 托管类 |
• |
支持 Web 服务 |
Microsoft .NET Framework 1.0 对读取、写入和处理 XML 文档提供了广泛的支持。这种支持在 .NET Framework 2.0 中得到了进一步增强,提高了各种 XML 类的性能和可用性。.NET Framework 在 System.Xml 命名空间中提供的新类可用于在 XML 数据与关系数据之间进行相互映射。
SQLXML 是一组使 SQL Server 数据库中的关系数据能够与 XML 无缝集成的库和技术。它是一个中间层组件,不包括由 FOR XML 和 OPENXML 提供的服务器端 XML 支持。SQLXML 提供了一个方案驱动的映射方法,能够从关系源数据生成 XML 并将表示关系信息的 XML 加载回关系表中。SQLXML 类对 SQL Server 2000(及更高版本)的数据库提供了 XML 支持。
Microsoft SQL Server 2005 以 XML 数据类型的形式添加了内置的 XML 支持。XML 数据可以存储在 XML 数据类型列内部。另外,通过将一个 XML 方案集合与此 XML 数据类型列关联,还可以对其进行进一步的限制。存储在 XML 数据类型列中的 XML 值可以借助 XQuery 和 XML 数据修改语言 (DML) 进行处理。可以在 XML 数据上建立索引,以增强查询性能。此外,FOR XML 和 OPENXML 也已得到增强,能够支持新的 XML 数据类型。
SQL Server 2005 中引入的存储和处理 XML 数据的新功能与 SQL Server 早期版本中提供的 XML 功能结合在一起,为开发人员提供了多种在 XML 应用程序中存储和处理 XML 数据的方法。由于使用 SQL Server 2005 提供的方法,有多种方法可以生成 XML 应用程序,因此,了解各种不同技术的方案,以及如何在各种技术之间进行权衡和配合对于作出正确的选择是至关重要的。本文提供了如何选择适当的方法,使用 SQL Server 2005 开发 XML 应用程序的指南。
二、XML 使用方案XML 的应用领域可大致划分为:
• |
用于商业集成的 XML:商业集成,也称为企业应用集成 (EAI),包括 A2A(应用程序对应用程序)、B2B(企业对企业)以及 B2C(企业对用户)应用程序。运行在不同系统上的应用程序使用基于 XML 的消息相互通讯。 |
• |
用于内容管理的 XML:使用基于 XML 的内容管理系统,用户可以存储、检索、修改和查询 XML 文档。这些系统以其原有格式存储 XML 文档。 |
下面介绍符合上述分类的几种方案。这些方案的解决方案将在以下几节中给出,并且会详细介绍 SQL Server 2005/Visual Studio 2005 环境中提供的各种 XML 选项的处理情况。
方案 1:保险理赔
一家汽车保险公司在 Internet 上提供服务,允许其投保人或代理通过公司的网站进入保险理赔。理赔将由位于公司总部的中央系统进行处理。处理完毕后,系统会将与该理赔相关的信息以指定的 XML 格式存储起来。系统中必须保留这些 XML 文档的精确副本,以便用于法律用途。此方案展示了 XML 在内容管理中的应用。
方案 2:汽车制造商与零件供应商之间的数据交换 I
一家汽车制造商与多家零件供应商进行交互,以便获得公司所需的零件。通常,该制造商要接收供应商的发票。然后,将与发票相关的数据手动输入到老式的发票处理系统中。发票处理系统以关系格式存储数据。而现在,该制造商希望将发票数据自动输入到老式的发票处理系统中。此方案是 XML 用于商业集成的示例。
方案 3:汽车制造商与零件供应商之间的数据交换 II
此方案包含一家与多家零件供应商进行交互的汽车制造商,如前一方案中所述。该制造商目前使用的系统不能为供应商提供查询发票状态或者从制造商获得付款说明的副本的功能。目前,供应商只能通过电话获得这些信息。该汽车制造商需要通过 Web 公开这些信息,以使供应商能够自动执行这些任务。此方案展示了 XML 在商业集成中的应用。
方案 4:内容管理系统
某公司通过 Web、书籍和 CD-ROM 等多种渠道向其客户提供医学、法律和技术等领域的信息。该公司要建立一套内容管理系统,以帮助其在较短的时间内向客户提供高质量的内容。此方案展示了 XML 在内容管理中的应用。
方案 5:客户调查
某公司在 Internet 上提供机票预订业务,要对每个季节进行调查,以确定当前季节最受客户喜爱的旅游地点。每个季节所用的调查表均不同,而且将来调查表也可能改变。该公司将对调查信息进行分析,并根据分析结果设计出满足大多数客户要求的旅游套餐。此方案可看作 XML 在内容管理中的应用。
三、.NET Framework 中的 XML 类Microsoft .NET Framework 对开发基于 XML 的产品提供了卓越的支持。在 .NET Framework 中,XmlTextReader、XmlTextWriter、XmlDocument 和 XmlValidatingReader 等核心类可以在 System.Xml 命名空间中获得,该命名空间是所有 XML 类的根命名空间。使用这些核心类,用户可以使用基于数据流和基于 DOM(基于文档对象模型)的两种浏览/访问模型来读取、写入和验证 XML 文档。System.Xml 命名空间包含以下子命名空间:
• |
System.Xml.Schema - 包含用于处理 XML 方案定义语言 (XSD) 方案的类。 |
• |
System.Xml.Serialization - 提供将对象序列化为 XML 格式的文档或数据流的类。 |
• |
System.Xml.XPath - 包括使用 XPath 表达式浏览 XML 文档的类。 |
• |
System.Xml.Xsl - 具有用于执行可扩展样式表转换 (XSLT) 的类。 |
System.Xml 命名空间的增强功能
在 Visual Studio 2005 中,新类(如 XsltCommand)和对现有 XML 类(如 XmlDocument)的增强可用于对 XML 数据进行各种操作,包括修改 XML 文档、应用 XSL 转换等。
Visual Studio 2005 中与 System.Xml 命名空间中的 XML 类相关的一些增强功能如下:
• |
XmlDocument 类添加了 XML 方案验证支持。 |
• |
XmlReader 和 XmlWriter 类的功能得到了增强,性能显著提高,并支持 XML 方案类型。而且,还添加了静态 Create 方法,提供了一种较为简单的方法,可使用 XmlReaderSettings 和 XmlWriterSettings 类创建 XmlReader 和 XmlWriter 实例,以便配置所创建的类型。 |
有关 System.Xml 增强功能的详细信息,请参见 What's New in System.Xml for Visual Studio 2005 and the .NET Framework 2.0 Release 白皮书。
System.Xml 命名空间中的类可用于实现自定义的 XML 分析、处理和存储逻辑。利用 SQL Server 2005 的公共语言运行库 (CLR) 的宿主功能以及 Visual Studio 2005 中的 XML 类,可以在中间层或数据库层处理 XML。
.NET Framework 中的 XML 类的使用包括将 XML 文档作为类型 [n]varchar(max) 或 [n]varbinary(max) 的列保存在数据库中,或者作为文件保存在文件系统中,以及使用 System.Xml 命名空间中的类在中间层或数据库层处理这些文档。.NET Framework 中的 XML 类还可用于操作以 XML 数据类型存储的数据。
.NET Framework 中的 XML 类适用于以下情况:
• |
访问 .NET Framework XML 的所有功能,如数据流分析、文档类型定义 (DTD)、XSD 验证、XSLT 处理等。 |
• |
仅仅将 SQL Server 用作 XML 文档的数据存储区,不需要在数据库内部进行深入访问。 |
• |
使用 .NET Framework 中的 XML 类进行 XML 文档的大多数或全部处理并在文档级进行更新。 |
可以使用 [n]varchar(max)、[n]varbinary(max) 或 XML 数据类型来存储 XML。
假如使用 [n]varchar(max) 或 [n]varbinary(max),可以获得以下好处:
• |
可以保留 XML 文档的高保真的副本,包括空格和格式。 |
• |
应用程序在对整个文档进行插入和检索操作方面可以获得可能实现的最高性能。 |
后面将介绍使用 XML 数据类型的好处。
在中间层进行 XML 处理
可以使用 .NET Framework 提供的各种 XML 类在中间层进行 XML 处理。如上所述,采用这种方法时,XML 文档可以作为类型 [n]varchar(max) 的列或 XML 存储在数据库中,也可以作为文件存储在文件系统中。在中间层,这些文档是从数据库中检索并根据用户的需要进行处理的,如下所述:
• |
假如需要读取 XML 文档,可以使用通过 XmlReader.Create() 方法创建的 XmlReader 来加载从数据库中获得的文档。使用 Read() 浏览文档。XmlReader 类提供对 XML 文档的最快的只读、只向前和非缓冲的访问。 |
• |
假如需要对 XML 文档进行写入访问并需要对 XML 数据进行完全浏览访问,则可以使用 XmlDocument 类加载和访问 XML 文档。XmlDocument 是文档对象模型 (DOM) 在 .NET Framework 中的一种实现,它是 XML 文档在内存中的树型表示,使用它可以浏览和编辑此文档。 |
• |
假如需要根据 DTD/XSD 来验证 XML 文档,或者在运行时获得 XSD 信息,则可以使用 XmlReader 类。在 XmlReaderSettings 类上,创建方法时将 XsdValidation 或 DTDValidation 设置为 True。还可以设置 ValidationEventHandle() 事件处理程序来处理读取过程中出现的验证错误。 |
• |
假如需要对 XML 文档应用 XSL 转换,可以使用 XPathDocument 类加载 XML 文档,然后使用 XslCommand 类应用转换。XPathDocument 类使用 XSLT 为 XML 文档处理提供了快速的高性能缓存。 |
• |
假如需要使用 XPath 表达式来查询 XML 文档,则可以使用 XPathDocument(只读)或 XmlDocument(读/写)来加载 XML 文档。使用 CreateNavigator() 方法创建一个 XPathNavigator 实例,并将所需的 XPath 表达式作为参数传递给 XPathNavigator 的 Select() 方法。 |
在数据库中进行 XML 处理
SQL Server 2005 与 CLR 的集成使开发人员也能够使用 .NET Framework 提供的 XML 类在数据库层进行处理。这种集成提供了以 .NET Framework 支持的任何语言编写存储过程、函数、触发器以及用户定义的类型的功能。此外,CLR 宿主还提供了访问整个 .NET Framework 基类库的功能。因此,上一节中介绍的各种 XML 处理选项也可以在数据库中使用。
使用 CLR 集成的优点如下:
• |
它提供了使用面向对象语言(如 C# 和 Visual Basic .NET)以托管代码的形式编写数据库对象的能力。 |
• |
托管的数据库对象比 SQL Server 早期版本中提供的扩展存储过程更安全。 |
• |
它具有定义用户定义的数据类型和用户定义的合计的能力。 |
• |
在某些情况下,编译的托管数据库对象比 Transact-SQL 具有更高的性能。 |
在 SQL Server 2005 中,数据库开发人员可以选择两种方法来编写存储过程、触发器和用户定义的函数。这两种方法是 Transact-SQL 和可在 .NET Framework 中使用的任何语言,如 C# 或 Visual Basic .NET。语言的选择取决于对数据执行的操作类型。假如代码在进行大多数数据访问时只需要使用很少甚至不使用过程逻辑,选择 Transact-SQL 最合适。托管类最适用于数学性较强的函数和过程,如字符串处理、日期操作、系统资源访问、文件访问、图像处理等。
在数据库层使用 .NET Framework 中的 XML 类的步骤如下:
• |
开发托管程序集。使用可在 .NET Framework 中使用的任何语言,以程序集的形式实现处理功能,并将程序集打包为 DLL。程序集也可以引用其他程序集。 |
• |
注册程序集并授予权限。使用 .NET Framework 开发的程序集可以使用 CREATE ASSEMBLY T-SQL 语句注册到 SQL Server 中。注册程序集时,还可以指定要授予该程序集的代码访问权限。使用 DROP ASSEMBLY T-SQL 语句可以取消程序集的注册。 |
• |
向 T-SQL 公开托管类型。程序集提供的处理功能可以通过用户定义的标量函数、用户定义的表函数、用户定义的过程 (UDP) 或者用户定义的触发器向 T-SQL 公开。用户定义的标量函数可在任何标量表达式中使用;用户定义的表函数可在任何 FROM 子句中使用;UDP 可在 EXEC 语句中调用。 |
分析方案
保险理赔包含以数据为主的信息(如理赔 ID、政策编号、理赔结算数据等)和以文档为主的信息(如事故损伤说明等)。XML 文档在汇集以数据为主和以文档为主的信息时功能非常强大。当前方案的主要要求是需要以 XML 格式保留一份保险理赔的精确副本。使用 SQL Server,通过将保险理赔作为类型 [n]varchar(max) 或 [n]varbinary(max) 的列存储在数据库中的方法,很容易满足这项要求。值得注意的是,假如需要保留无关紧要的空格、属性顺序、命名空间前缀以及 XML 声明等信息,则不应使用 XML 数据类型存储文档。
优点
使用 [n]varchar(max) 或 [n]varbinary(max) 作为存储媒介,使用 System.Xml 命名空间中的类处理 XML 文档的优点如下:
• |
可以灵活地对 XML 文档的方案进行更改。在同一列中使用不同的方案存储 XML 文档时非常有用。 |
• |
当使用 [n]varchar(max) 或 [n]varbinary(max) 存储 XML 时,能提供高保真的 XML 文档。此功能对于处理法律文档(如保险理赔)的应用程序可能是必不可少的。 |
• |
与将 XML 实例作为文件存储在文件系统中相比,还可以利用数据库的功能,如事务更新、并发访问、备份、复制等。 |
• |
由于这种方法不依赖于数据库提供的 XML 支持,因此很容易对应用程序进行扩展,以支持多种数据库服务器,如 SQL Server、Oracle 等。 |
• |
可以使用客户端系统的处理能力,从而减轻了服务器的负载。通过在中间层进行 CPU 密集的 XML 处理,减轻了服务器的负载,使其能够用于其他重要的任务。 |
• |
在文档级进行插入和检索操作方面提供了可能实现的最佳性能。 |
• |
复杂的操作(如 XSL 转换)可以作为存储过程、触发器或函数在数据库中完成。 |
限制
使用 [n]varchar(max) 或 [n]varbinary(max) 进行存储,使用 System.Xml 命名空间中的类处理 XML 实例的限制总结如下:
• |
与 XML 数据类型(请参见 SQL Server 2005 中的 XML 数据类型一节)或 SQLXML 选项(请参见 SQLXML 一节)相比,编码的复杂性较高。即使数据库逻辑非常简单,在中间层或数据库层用代码来实现 XML 的分析和处理也很复杂。 |
• |
实现此解决方案的代码量很大。因此,与 SQLXML 选项相比,维护成本也较高。 |
• |
由于 XML 文档作为 [n]varchar(max) 存储在数据库中,因此不能对 XML 文档进行深入地更新、插入或删除操作。查询能力也有限。 |
• |
以 [n]varchar (max) 数据类型存储的 XML 文档最大不能超过 2GB。 |
• |
根据 XML 内容搜索以这种方法存储的文档列的代价非常高。 |
使用 .NET Framework 中的 XML 类的示例
考虑本文前面介绍的保险理赔方案。一旦理赔获得批准,保险公司将存储理赔信息,以便用于法律用途。理赔信息可以作为 [n]varchar(max) 数据类型存储在数据库中。
应用程序的流程如下:
1. |
处理理赔后,应用程序批准或拒绝理赔。 |
2. |
使用 System.Xml 命名空间中的类,为理赔生成 XML 文档。 |
3. |
将生成的 XML 文档发送到存储过程。 |
4. |
存储过程将 XML 文档插入到表中。 |
以下代码示例使用系统获得的理赔详细信息生成一个 XML 文档并将该 XML 文档插入到数据库中。
using System; using System.Xml; using System.IO; using System.Text; using System.Data; using System.Data.SqlClient; namespace InsuranceClaim { class Insurance { static void Main(string[] args) { Insurance.InsertInsuranceClaim(); } static void InsertInsuranceClaim() { StringWriter strWriter = null; XmlWriter writer = null; XmlWriterSettings settings = null; SqlConnection connection = null; SqlCommand command = null; try { strWriter = new StringWriter(); settings = new XmlWriterSettings(); //使用缩进功能以增强可读性。 settings.Indent = true; settings.Encoding = System.Text.Encoding.UTF8; writer = XmlWriter.Create(strWriter, settings); //编写 XML 声明。 writer.WriteStartDocument(); writer.WriteStartElement("InsuranceClaim"); writer.WriteStartElement("ClaimInfo"); writer.WriteElementString("ClaimID", "C1234"); writer.WriteElementString("ClaimType", "3"); writer.WriteStartElement("SettlementDetails"); writer.WriteStartElement("PaymentDetails"); writer.WriteElementString("PaidTo", "Jeff"); writer.WriteElementString("Amount", "2000"); writer.WriteElementString("Date", "05/12/2002"); writer.WriteElementString("ApprovedBy", "Mike"); writer.WriteEndElement();//PaymentDetails 结束 writer.WriteEndElement();//SettlementDetails 结束 writer.WriteEndElement();//ClaimInfo 结束 writer.WriteStartElement("DamageReport"); writer.WriteString("Minor accident occured on "); writer.WriteElementString("Address", "ABC Street, Sample City, Sample State"); writer.WriteString(" due to "); writer.WriteElementString("Cause", "bad weather"); writer.WriteString(" resulted in damage to "); writer.WriteElementString("DamagedItem", "Head Lights"); writer.WriteElementString("DamagedItem", "Engine"); writer.WriteEndElement();//DamageReport 结束 writer.WriteEndElement();//InsuranceClaim 结束 writer.WriteEndDocument(); //将 XML 写入文件并关闭写入程序。 writer.Flush(); connection = new SqlConnection(); connection.ConnectionString = @"server=localhost; database=AdventureWorks;Integrated Security=SSPI;"; command = connection.CreateCommand(); command.CommandText = "InsertInsuranceClaim"; command.CommandType = System.Data.CommandType.StoredProcedure; command.Parameters.Add("@CustomerID", System.Data.SqlDbType.Char); command.Parameters.Add("@Claim", System.Data.SqlDbType.VarChar); String xml = strWriter.ToString(); string strCustomerID = "1001"; command.Parameters[0].Value = strCustomerID; command.Parameters[0].Size = strCustomerID.Length; command.Parameters[1].Value = xml; command.Parameters[1].Size = xml.Length; connection.Open(); command.ExecuteNonQuery(); connection.Close(); } finally { if (connection.State == ConnectionState.Open) connection.Close(); if (writer != null) writer.Close(); if (strWriter != null) strWriter.Close(); } } } }
下面是用于创建数据库表的脚本:
CREATE TABLE [InsuranceClaim]( [CustomerID] [char](4) NOT NULL, [Claim] [varchar](max) NOT NULL, [ModifiedDate] [datetime] NOT NULL DEFAULT (getdate()) )
以下存储过程用于将 XML 文档插入到数据库中。
CREATE PROCEDURE [dbo].[InsertInsuranceClaim] @CustomerID [char](4), @Claim [varchar](max) AS BEGIN SET NOCOUNT ON; INSERT INTO [InsuranceClaim] ( CustomerID, Claim ) VALUES ( @CustomerID, @Claim ) END;四、SQLXML
SQLXML 是与 SQL Server 2000 一起引入的,它包含在客户端处理 XML 所需的全部功能。它是一组使 SQL Server 数据库中的关系数据能够与 XML 描述的关系结构数据无缝集成的库和技术。
在 SQL 2000 问世之前,开发人员必须为关系数据与 XML 格式的数据之间的交互提供代码层。SQLXML 的出现使这种操作变得很简单,因为它在关系数据与 XML 之间提供了链接。本文只讨论 SQLXML 托管类。有关该库其他功能的适用性的详细信息,请参见 MSDN 网站上的 SQLXML 页。
SQLXML 包含很多在 SQL Server 中引入的支持 XML 的功能。包括:
• |
在客户端将查询结果转换为 XML | ||||||||
• |
使用带批注的 XSD 映射方案文件创建关系数据的 XML 视图,使您能够:
| ||||||||
• |
使用 HTTP 访问 SQL Server,使您能够:
| ||||||||
• |
以基于 SOAP 的 Web 服务形式公开存储过程、用户定义的函数以及模板查询所提供的功能。 | ||||||||
• |
在 .NET Framework 中编写代码,以便利用 SQLXML 通过 SQLXML 托管类所提供的 XML 功能 |
客户端 XML 格式化。在客户端指定 FOR XML 子句,可以使中间层在由服务器返回的行集合上进行 FOR XML 转换,以响应查询。要在客户端进行 XML 格式化:
• |
假如使用 SQLXML 托管类,则将 SqlXmlCommand 对象的 ClientSideXml 属性设置为 True。 |
• |
假如使用 SQLXMLOLEDB 提供程序,则将 ClientSideXML 提供程序特有的属性设置为 True。 |
• |
假如使用模板查询,则在模板中指定 client-side-xml="1"。 |
• |
假如使用 HTTP 访问 SQL Server,则在 Settings(设置)选项卡中的虚拟目录下选择 Run on the Client(在客户端运行)选项。 |
• |
在客户端上,FOR XML 提供的有效 XML 格式化模式为 RAW、NESTED 和 EXPLICIT。使用 RAW 模式时,所得到的 XML 文档在查询结果的每一行中都包含一个 XML 元素,而与行对应的每一列中都包含一个属性。当指定 NESTED 模式时,基表名将作为所得到的 XML 文档的元素名返回。EXPLICIT 模式允许在查询中指定所需的 XML 格式,因此能够生成任何格式的 XML 文档。 |
• |
XML 视图。XML 视图是使用带批注的 XSD 方案创建的,该方案定义了关系数据与 XML 数据之间的映射。可以使用 XPath 查询对这些 XML 视图进行查询。通过 XML 视图公开的关系数据也可以被修改,然后使用更新程序将所进行的修改提交到数据库。此外,XML 视图还可借助基于 COM 的 XML Bulk Load(XML 海量加载)对象将大量 XML 文档插入到数据库中。 |
• |
使用 HTTP 访问 SQL Server。SQLXML 提供了一个名为“IIS 虚拟目录管理”的工具,该工具可用于设置一个 IIS 虚拟目录,通过 HTTP 公开 SQL Server 的 XML 功能。借助 SQL ISAPI 扩展的功能,它支持在 URL 中直接指定 SQL 语句、存储过程、模板查询、模板文件以及 XPath 查询。 |
• |
SQLXML 中的 Web 服务支持。SQLXML 3.0 中增加了一项功能,即支持以基于 SOAP 的 Web 服务形式公开 SQL Server 的功能。此功能使 SQL Server 能够从客户端接收 SOAP HTTP 请求,以便执行存储过程、用户定义的函数和模板。 |
• |
SQLXML 托管类。使用 SQLXML 托管类,可以在 .NET Framework 中访问 SQLXML 功能。SQLXML 中有三种托管类: |
• |
SqlXmlCommand - 处理数据库连接和查询执行 |
• |
SqlXmlParameter - 帮助指定查询中的参数 |
• |
SqlXmlAdapter - 便于与 .NET Framework 中的数据集进行交互 |
使用 SQLXML 托管类,您可以:
• |
使用 FOR XML 子句执行 SQL 查询 |
• |
针对映射方案执行 XPath 查询 |
• |
执行模板查询 |
• |
执行模板查询文件 |
• |
执行更新程序 |
• |
执行区分程序 |
使用 SQLXML 将关系数据作为 XML 文档公开非常适用于以下情况:
• |
应用程序接收结构良好且能较好地映射到关系表中的 XML 数据。 |
• |
应用程序必须将来自外部应用程序的大量 XML 文档加载到数据库中,并保持其关系格式。 |
• |
应用程序不需要保留文档的顺序。 |
• |
应用程序需要以不同的格式向多个数据使用者提供相同的数据。 |
• |
DML 操作的性能对于应用程序至关重要。 |
• |
应用程序需要充分利用优化程序进行查询优化。 |
• |
应用程序需要进行深入的数据操作。 |
• |
应用程序需要以 XML 的形式公开现有的关系数据。 |
分析方案
在 XML 使用方案中介绍的第一个数据交换方案(请参见方案 2:汽车制造商与零件供应商之间的数据交换 I 一节),一个汽车制造商与多个零件供应商之间的交互是使用 SQLXML 的典型案例。制造商必须与不同的供应商进行通讯以便交换发票数据。建议的解决方案使用 Web 服务和 SQLXML 来解决此问题。制造商公开一个 Web 服务,供应商可使用该服务将发票发送给制造商。Web 服务使用了一个针对客户的 XSLT,将发票由供应商的格式转换为制造商使用的通用格式。然后,Web 服务使用 XML 视图来分解 XML 文档,将发票文档的内容映射到关系表中的列。老式的发票处理系统将能够从关系表中选取数据并进行处理。在此方案中使用 XML 视图的优点如下:
• |
降低了维护成本。通过修改针对供应商的 XSLT 文件,很容易适应供应商对发票方案所进行的任何更改。 |
• |
与 FOR XML 相比,编码的复杂程度降低了(请参见关系/XML 集成的服务器端支持 (FOR XML/OPENXML) 一节)。 |
• |
通过创建针对供应商的 XSLT 文件,很容易支持新的供应商。 |
优点
使用 SQLXML 的优点总结如下:
• |
与服务器端的 FOR XML EXPLICIT 相比,创建带批注的映射方案将关系数据映射到 XML 数据是一种相对简单、易于维护的解决方案。 |
• |
SQLXML 能够创建可更新的双向 XML 视图,而使用 FOR XML 只能创建关系数据的只读 XML 表示形式。 |
• |
映射 XSD 便于适应 XML 格式的更改请求,而不必进行主代码的更改。这会使维护简单易行。 |
• |
SQLXML 允许用户通过将 SqlXMLCommand 类的 ClientSideXML 属性设置为 True,在客户端进行 XML 格式化,从而减轻服务器的负载。 |
限制
从不利的方面来看,从客户端使用 SQLXML 有一些限制:
• |
XML 视图不适用于 XML 文档的层次结构过多或者递归深度未知的情况。 |
• |
SQLXML 不适用于包含混合内容标记和有序数据的说明性文档,例如产品目录、新闻报告等。 |
• |
由于不能保留文档顺序,很难重新构造出原始的 XML 文档。 |
• |
虽然将 XML 文档分解到关系表中提供了卓越的搜索性能,但是在关系数据与 XML 之间进行相互转换的代价非常昂贵。 |
• |
当在 XSD 映射方案中使用默认映射时,可能会公开数据库表名和列信息,这可能无意中造成信息泄漏。这种风险可以通过为表和列指定明确的映射加以避免。 |
• |
URL 中的 SQL 语句只能用于可信的局域网中。在 Internet 上使用这种查询可能导致潜在的安全问题。 |
使用 SQLXML 的示例
以上概要介绍了 SQLXML 的功能,下面将详细分析一个可以应用 SQLXML 托管类的示例。考虑一个简单的示例 - 为特定客户导出销售订单的详细信息。该示例所用的表可以在 AdventureWorks 数据库中获得。
数据库中的数据必须能够在客户端以 XML 格式获得,并能被表示层显示。下面您将看到,如何使用 SQLXML 类将 SQL 数据库中的关系数据作为 XML 数据进行处理。该示例使用映射 XML 方案进行处理,并将 XML 节点名映射到表字段。有关使用 SQLXML 托管库处理关系数据的详细信息,请参见 MSDN 网站上的SQLXML页。
以下带批注的 XSD 方案定义了关系表 [Sales.Customer]、[Sales.SalesOrderHeader] 和 [Sales.SalesOrderDetail] 与客户销售订单详细信息的目标 XML 表示之间的映射。还可以使用 XSD 映射方案来定义 XML 中的父子关系,如以下 XSD 方案所示。
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:sql="urn:schemas-microsoft-com:mapping-schema"> <xsd:annotation> <xsd:appinfo> <sql:relationship name="CustomerOrderHeader" parent="Sales.Customer" parent-key="CustomerID" child="Sales.SalesOrderHeader" child-key="CustomerID" /> <sql:relationship name="OrderHeaderOrderDetail" parent="Sales.SalesOrderHeader" parent-key="SalesOrderID" child="Sales.SalesOrderDetail" child-key="SalesOrderID" /> </xsd:appinfo> </xsd:annotation> <xsd:element name="Customer" sql:relation="Sales.Customer" > <xsd:complexType> <xsd:sequence> <xsd:element name="Order" sql:relation="Sales.SalesOrderHeader" sql:relationship="CustomerOrderHeader" maxOccurs="unbounded" > <xsd:complexType> <xsd:sequence> <xsd:element name="OrderDetail" sql:relation="Sales.SalesOrderDetail" sql:relationship="OrderHeaderOrderDetail" maxOccurs="unbounded" > <xsd:complexType> <xsd:attribute name="SalesOrderID" type="xsd:integer" /> <xsd:attribute name="ProductID" type="xsd:integer" /> <xsd:attribute name="OrderQty" type="xsd:integer" /> </xsd:complexType> </xsd:element> </xsd:sequence> <xsd:attribute name="SalesOrderID" type="xsd:integer" /> <xsd:attribute name="CustomerID" type="xsd:integer" /> <xsd:attribute name="OrderDate" type="xsd:date" /> <xsd:attribute name="ShipDate" type="xsd:date" /> </xsd:complexType> </xsd:element> </xsd:sequence> <xsd:attribute name="CustomerID" type="xsd:integer" /> </xsd:complexType> </xsd:element> </xsd:schema> class ExportOrders { /// <摘要> /// 此方法使用 SqlXmlCommand 类从 /// Sales.Customer、Sales.SalesOrderHeader 和 Sales.SalesOrderDetail /// 表中选择记录。数据是从服务器获取并在客户端 /// 格式化为 xml 的。请注意,ClientSideXml 设置为 True。 /// <摘要> static void Main(string[] args) { if (args.Length < 1) { Console.WriteLine("Usage"); Console.WriteLine("CustomerOrders <CustomerID> [OrderID]"); return; } try { StringBuilder strBuilder = new StringBuilder(); strBuilder.Append("/Customer[@CustomerID='"); strBuilder.Append(args[0]); strBuilder.Append("']"); if (args.Length > 1) { strBuilder.Append("/Order[@SalesOrderID='"); strBuilder.Append(args[1]); strBuilder.Append("']"); } SqlXmlCommand xmlCommand = new SqlXmlCommand(@"Provider= SQLOLEDB; Server=localhost; database=AdventureWorks; Integrated Security=SSPI;"); xmlCommand.ClientSideXml = true; xmlCommand.RootTag = "CustomerOrders"; xmlCommand.SchemaPath = @"CustomerOrderDetails.xsd"; xmlCommand.CommandType = SqlXmlCommandType.XPath; xmlCommand.CommandText = strBuilder.ToString(); Stream reader = xmlCommand.ExecuteStream( ); FileStream fsOut = File.Create("CustomerOrder.xml"); StreamWriter sw = new StreamWriter(fsOut); using (StreamReader sr = new StreamReader(reader)) { sw.Write(sr.ReadToEnd()); } sw.Flush(); sw.Close(); fsOut.Close(); } catch (Exception exception) { Console.WriteLine( exception.ToString() ); } } }
上述方法为作为应用程序命令行参数指定的客户 ID 导出了销售订单的详细信息。数据在客户端转换为 XML 格式,从而避免了服务器端的性能问题。请注意,上述带批注的 XSD 方案映射必须保存为 CustomerOrderDetails.xsd,才能使上述代码片段正常运行。
注意:从此示例中可以看出,用于从数据库中检索 XML 数据的代码非常少。
五、关系/XML 集成的服务器端支持 (FOR XML/OPENXML)SQL Server 通过 SELECT 语句的 FOR XML 扩展功能,支持在服务器端以 XML 文档的形式返回 SQL 查询结果。另外,关键字 OPENXML 还能够提供从 XML 文档中提取行集合的能力。
FOR XML
服务器端的 FOR XML 支持四种 XML 转换模式 - RAW、AUTO、EXPLICIT 和 PATH。
默认情况下,RAW 模式将查询结果集中的每一行映射到一个 XML 元素,并将行中的每一列映射到一个属性。当使用 ROW 模式指定 ELEMENTS 选项时,行中的每一列将被映射到为该行生成的元素的子元素中。还可以通过指定 XMLSCHEMA 选项为所生成的 XML 请求一个内联方案。
AUTO 模式支持生成嵌套的 XML 元素,默认情况下,FROM 子句中的每个表(在 SELECT 子句中至少列出一列)将映射到一个 XML 元素,SELECT 子句中的列将映射到属性(假如指定 ELEMENTS 选项,则映射到子元素)。
EXPLICIT 模式能够最大限度地控制由查询结果生成的 XML 的格式。使用它,您可以通过在查询中指定所需的 XML 格式来生成任何格式的 XML。
使用 EXPLICT 模式编写复杂的 XML 文档是相当繁琐的。假如不想编写复杂的 EXPLICIT 模式请求,还可以使用 PATH 模式,该模式还具有编写嵌套 FOR XML 查询和 TYPE 指令以返回 XML 类型实例的能力。PATH 模式通过将列名解释为类似 XPath 的语法,将 SELECT 查询返回的行集合中的列映射到属性和子元素。有关 SQL Server 2005 中 FOR XML 增强功能的详细信息,请参见 What's New in FOR XML in Microsoft SQL Server 2005 白皮书。
OPENXML
OPENXML 与 sp_xml_preparedocument 和 sp_xml_removedocument 系统存储过程共同提供了 XML 文档的关系行集合视图。为了对 XML 文档使用 OPENXML,必须使用 sp_xml_preparedocument 创建 XML 文档的一个内存中表示。此存储过程使用 MSXML 分析器分析 XML 文档,并向 XML 文档返回一个可用于 OPENXML 的句柄。现在,XML 文档句柄、行模式(将 XML 数据的节点映射到行的 XPath 表达式)、行集合方案以及行集合列与 XML 节点之间的映射等参数都可以传递到 OPENXML 以获得行集合。当不再需要 XML 文档时,必须使用 sp_xml_removedocument 存储过程将其从内存中卸载。
FOR XML 的增强功能
在 SQL Server 2005 中,FOR XML 增强了以下功能:
• |
使用新的 TYPE 指令转换 FOR XML 结果的类型 |
• |
将 FOR XML 的结果分配给各种类型的 XML |
• |
对 FOR XML 查询进行嵌套处理,以生成 XML 层次结构 |
• |
使用新的 PATH 模式生成复杂的 XML 文档 |
• |
使用 XMLDATA 和 XMLSCHEMA 选项分别生成 XDR 或 XSD 格式的内联方案 |
• |
在 RAW 模式下使用 ELEMENTS 指令生成以元素为主的 XML |
• |
在 ELEMENT 指令中使用 XSINIL 选项,将 NULL 值映射到属性为 xsi:nil="true" 的元素 |
OPENXML 的增强功能
在 SQL Server 2005 中,OPENXML 的功能得到了增强,支持以下功能:
• |
将 XML 类型数据传递到 sp_xml_preparedocument |
• |
在 WITH 子句中使用新数据类型 |
使用 FOR XML 和 OPENXML 编写和分解 XML 文档非常适用于以下情况:
• |
应用程序需要按照一定的关系存储数据,并将此信息以 XML 的形式公开给另一个应用程序。 |
• |
应用程序不需要保留 XML 的顺序。 |
• |
应用程序要执行大量元素级 DML 操作。 |
• |
应用程序需要进行深入的数据访问和更新。 |
• |
应用程序需要通过 Web 服务公开关系数据。 |
分析方案
方案 3 中的要求(请参见方案 3:汽车制造商与零件供应商之间的数据交换 II 一节)是提供一种 Web 服务,使供应商可以使用该服务获得发票的状态或者获得付款说明的副本。FOR XML 与 Web 服务结合,提供了一种解决方案,允许制造商在 Internet 上公开这些服务。供应商可以使用 Web 服务查询其发票的状态。然后,Web 服务将使用供应商提供的发票 ID,并使用 FOR XML 语句从关系数据中生成 XML 格式的响应。生成的 XML 文档将返回给供应商。在当前方案中,使用基于 FOR XML 语句的方法具有以下优点:
• |
FOR XML 提供了一种从关系数据中动态编写简单 XML 文档的简单方法。 |
• |
当用于编写简单的 XML 文档时,FOR XML 查询较 XML 视图更易于维护。 |
优点
使用 FOR XML/OPENXML 的优点如下:
• |
FOR XML 提供了一种在服务器上从关系数据生成 XML 的简单方法。 |
• |
FOR XML 提供了通过 Web 服务公开商业信息的能力。 |
• |
使用 OPENXML,可以将行集合以 XML 格式传递给存储过程,这将使您能够在一个网络往返中进行大量的 INSERT、UPDATE 和 DELETE 操作。 |
• |
FOR XML 与 XSL 结合,可用于应用程序集成或商业集成。 |
限制
使用 FOR XML/OPENXML 编写和分解 XML 文档的限制如下:
• |
使用 FOR XML 的 EXPLICT 选项构造 XML 的结构非常困难。 |
• |
很难维护使用 FOR XML EXPLICIT 编写的复杂查询。 |
• |
由 FOR XML AUTO 生成的 XML 文档可能会公开数据库表名和列信息,导致无意中泄漏信息。这种情况可以通过为表和列指定别名加以避免。 |
使用 FOR XML 和 OPENXML 的示例
以下示例使用 SQL Server 2005 附带的 AdventureWorks 数据库。此示例使用 FOR XML 为指定范围内的客户从 [Sales.Customer]、[Sales.SalesOrderHeader]、[Production.Product] 和 [Sales.SalesOrderDetail] 表中获得客户、订单以及订单详细信息等信息。
示例:使用 FOR XML
SELECT Cust.CustomerID, OrderHeader.CustomerID, OrderHeader.SalesOrderID, Detail.SalesOrderID, Detail.LineNumber,Detail.ProductID, Product.Name, Detail.OrderQty FROM Sales.Customer Cust, Sales.SalesOrderHeader OrderHeader, Sales.SalesOrderDetail Detail, Production.Product Product WHERE Cust.CustomerID = OrderHeader.CustomerID AND OrderHeader.SalesOrderID = Detail.SalesOrderID AND Detail.ProductID = Product.ProductID AND (Cust.CustomerID BETWEEN 44 AND 46) ORDER BY OrderHeader.CustomerID, OrderHeader.SalesOrderID FOR XML AUTO
查询结果如下:
<Cust CustomerID="44"> <OrderHeader CustomerID="44" SalesOrderID="53575"> <Detail SalesOrderID="53575" LineNumber="2" ProductID="952" OrderQty="2"> <Product Name="Chain" /> </Detail> <Detail SalesOrderID="53575" LineNumber="1" ProductID="969" OrderQty="1"> <Product Name="Touring-1000 Blue, 60" /> </Detail> <Detail SalesOrderID="53575" LineNumber="3" ProductID="972" OrderQty="1"> <Product Name="Touring-2000 Blue, 54" /> </Detail> </OrderHeader> <OrderHeader CustomerID="44" SalesOrderID="59024"> <Detail SalesOrderID="59024" LineNumber="1" ProductID="972" OrderQty="3"> <Product Name="Touring-2000 Blue, 54" /> </Detail> <Detail SalesOrderID="59024" LineNumber="2" ProductID="957" OrderQty="2"> <Product Name="Touring-1000 Yellow, 60" /> </Detail> </OrderHeader> </Cust> <Cust CustomerID="46"> <OrderHeader CustomerID="46" SalesOrderID="48354"> <Detail SalesOrderID="48354" LineNumber="1" ProductID="730" OrderQty="1"> <Product Name="LL Road Frame - Red, 62" /> </Detail> </OrderHeader> </Cust>
以下示例使用 OPENXML 和 XPath 表达式提取 XML 文档中指定的订单详细信息。
示例:使用 OPENXML
DECLARE @XmlDocumentHandle int DECLARE @XmlDocument nvarchar(max) SET @XmlDocument = N'<ROOT> <Cust CustomerID="44"> <OrderHeader CustomerID="44" SalesOrderID="53575"> <Detail SalesOrderID="53575" LineNumber="2" ProductID="952" OrderQty="2"> <Product Name="Chain" /> </Detail> <Detail SalesOrderID="53575" LineNumber="1" ProductID="969" OrderQty="1"> <Product Name="Touring-1000 Blue, 60" /> </Detail> <Detail SalesOrderID="53575" LineNumber="3" ProductID="972" OrderQty="1"> <Product Name="Touring-2000 Blue, 54" /> </Detail> </OrderHeader> <OrderHeader CustomerID="44" SalesOrderID="59024"> <Detail SalesOrderID="59024" LineNumber="1" ProductID="972" OrderQty="3"> <Product Name="Touring-2000 Blue, 54" /> </Detail> <Detail SalesOrderID="59024" LineNumber="2" ProductID="957" OrderQty="2"> <Product Name="Touring-1000 Yellow, 60" /> </Detail> </OrderHeader> </Cust> <Cust CustomerID="46"> <OrderHeader CustomerID="46" SalesOrderID="48354"> <Detail SalesOrderID="48354" LineNumber="1" ProductID="730" OrderQty="1"> <Product Name="LL Road Frame - Red, 62" /> </Detail> </OrderHeader> </Cust> </ROOT>'
-- 创建 XML 文档的内部表示。
EXEC sp_xml_preparedocument @XmlDocumentHandle OUTPUT, @XmlDocument -- 使用 OPENXML 行集合提供程序执行 SELECT 语句。 SELECT * FROM OPENXML (@XmlDocumentHandle, '/ROOT/Cust/OrderHeader/Detail',2) WITH (CustomerID varchar(10) '../@CustomerID', OrderID int '../@SalesOrderID', LineNumber int '@LineNumber', ProductID int '@ProductID', Quantity int '@OrderQty')
-- 删除内部表示。
EXEC sp_xml_removedocument @XmlDocumentHandle
查询结果如下:
-------------------------------------------------------- CustomerID OrderID LineNumber ProductID Quantity -------------------------------------------------------- 44 53575 2 952 2 44 53575 1 969 1 44 53575 3 972 1 44 59024 1 972 3 44 59024 2 957 2 46 48354 1 730 1 --------------------------------------------------------六、SQL Server 2005 中的 XML 数据类型
XML 数据的层次结构特性使其很难被建模成关系数据,因为这会使数据结构变得非常复杂(如层次的深度将增加等)。而且,当 XML 数据映射到关系数据时,也无法保留 XML 实例中的元素顺序,因此从分解得到的关系数据编写原始的 XML 文档代价相当高。由于关系模型对存储 XML 数据有诸多限制,因此在内部存储 XML 实例是一种较理想的方法。内部 XML 实例不会受关系模型的限制,并能提供多种功能,例如能够处理层次结构或嵌套数据、能够保留元素顺序、能够直接存储和检索 XML 数据以及能够灵活地支持多种方案等。
Microsoft SQL Server 2005 为 XML 数据处理提供了广泛支持。使用 SQL Server 2005,XML 值可存储在内部的 XML 数据类型列中,可以根据 XML 方案的集合设置其类型,也可以不设置类型。还可以通过 XQuery 和 XML DML 支持深入的数据处理,后者是针对数据修改的扩展。此外,还可以为 XML 列创建索引,以提高查询性能。
有类型的 XML
有类型的 XML 非常适用于拥有描述 XML 数据的 XML 方案的情况。在这些情况下,可以将 XML 方案的集合与 XML 列关联,以生成有类型的 XML。对 XML 类型列的验证是根据与该列关联的 XML 方案集合进行的。另外,包含有类型的 XML 数据的查询比包含无类型的 XML 数据的查询的性能要好,因为它不需要在运行时转换节点值。
无类型的 XML
无类型的 XML 适用于有方案但不想让服务器验证数据的情况,或者没有方案的情况。在下列情况下,即使有方案,可能也要存储无类型的 XML:
• |
没有固定的方案 |
• |
在服务器端存储数据之前,已在客户端进行验证 |
• |
临时存储根据方案确定无效的 XML 数据 |
• |
使用服务器端不支持的方案组件(如 key/keyref) |
即使无类型的 XML 文档不与任何方案关联,系统仍要对其进行检查,以确保其格式良好。值得注意的是,由于无类型的 XML 需要在运行时转换节点值,其性能会受到一定的影响,因为节点值是作为 Unicode 字符串内部存储的。
XML 数据类型的使用方案
使用 SQL Server 2005 中的新 XML 数据类型,可以实现以下功能:
• |
创建既包含关系列,又包含有 XML 类型列的表。 |
• |
通过与 XML 方案集合关联,创建有类型的 XML 列类型。 |
• |
对涉及其他 XML 或非 XML 类型列的 XML 列进行限制,以强制实现业务规则。 |
• |
创建可用于存储 XML 数据类型实例的 XML 类型的变量。 |
• |
为存储过程或用户定义的函数创建 XML 类型的参数。 |
• |
从用户定义的函数中返回 XML 类型值。 |
• |
将使用新的 TYPE 指令获得的 FOR XML 查询结果分配给 XML 类型的变量。 |
• |
运行 XQuery 子集,在 XML 结构内查询并转换 XML 数据。 |
• |
根据 XML 类型的列创建计算列。 |
• |
为 XML 类型的列创建 XML 索引,以提高查询性能。 |
• |
使用 XML DML 对 XML 实例进行元素级的插入、删除和更新操作。 |
• |
将 XML 类型数据的实例传递到 sp_xml_preparedocument,以准备 XML 文档的内存中表示。 |
• |
使用 XQuery 和 XML DML 编写包含关系和 XML 列的跨域查询。 |
• |
使用 CAST 和 CONVERT,分别将 XML 类型转换为 varchar 或 nvarchar 类型。 |
• |
使用 CAST 或 CONVERT,将字符串数据类型(如 [n]varchar、[n]text、varbinary 和 image)转换为 XML 类型。 |
XML 数据类型方法和 XML DML
可以通过五种方法对 XML 数据类型列进行查询和处理。使用 XML 数据类型的 query() 方法,可以提取 XML 文档的片段;query() 方法接受 XQuery 表达式作为参数,并返回无类型的 XML 实例;使用 value() 方法,通过指定 XQuery 表达式以及需要返回的 SQL 类型,可以从 XML 实例中提取标量值;使用 exist() 方法,可以检查 XML 实例是否存在;使用 nodes() 方法,可以方便地将 XML 文档分解为关系数据。
使用 modify() 方法,可以对 XML 实例进行数据处理。通过 XQuery 中添加的 insert、delete 和 update 关键字提供了对 XML DML 的支持。使用 insert、delete 和 update 关键字可以分别插入、删除和更新一个或多个节点。
XML 索引
假如 XML 实例非常大,那么在对 XML 数据类型列执行查询处理操作时所进行的分析和分解可能要持续相当长的时间。在这些列上创建索引将提高对 XML 数据类型的查询性能。XML 数据的大小和使用方案对确定所需的索引类型非常重要。SQL Server 支持两种类型的索引 - 主 XML 索引和次 XML 索引;没有前者,后者就不能存在。
在 XML 列上创建主 XML 索引,将分解 XML BLOB 并将这些值存储在一个内部表中。这样,由于运行时不必进行分解,就可以提高运行时的查询性能。根据使用方案的不同,还可以创建次 XML 索引,进一步提高查询性能。可以创建三种类型的次 XML 索引 - PATH、PROPERTY 和 VALUE,分别提高路径、属性和值的查询性能。有关为 XML 类型列选择适当的次索引的方法,请参见“Performance Optimizations for the XML Data Type”白皮书。
将 XML 文档存储为 XML 数据类型非常适用于以下情况:
• |
应用程序需要保留 XML 实例的信息集内容。XML 文档的信息集内容包括文档层次结构、元素顺序、元素值及属性等。应用程序不保留属性顺序、命名空间前缀、无关紧要的空格以及 XML 声明等信息。 |
• |
应用程序需要对 XML 文档进行元素级的修改和查询操作。 |
• |
应用程序需要 XML 数据类型列的索引,以加快查询处理。 |
• |
XML 数据不一定有方案。 |
• |
应用程序使用各种结构的 XML 文档,或者 XML 文档要符合难以映射到关系结构的各种不同或复杂方案。 |
分析方案
分析方案:内容管理系统
现在,让我们来分析一下 XML 使用方案中介绍的内容管理系统。出版公司要处理文本、图像、音频、视频等各种形式的信息。可独立使用的信息块是从各种来源收集并在数据库中进行维护的。这些信息块被称为“组件”。将各个组件组装起来即可创建文档。文档中要包括哪些组件取决于用户的需要。这些文档将通过各种渠道发送给预订的用户。内容管理系统通常需要具有高性能、高可缩放性地存储、检索、搜索和更新内容的能力。
XML 作为一种统一标准的数据模型提供了一种极好的方法,可以在同一个文档中同时存储 XML 数据和 XML 内容。XML 还具有将显示方式与数据本身隔离的能力,这一点非常重要,因为相同的信息用于不同用户的显示方式可能不同。SQL Server 2005 提供的内置 XML 数据类型可以满足这种内容管理系统的需要。通过 XML 数据类型,可以存储 XML 文档,使用 XML DML 在元素级修改 XML 文档,以及使用 XQuery 对 XML 文档进行查询。
分析方案:客户调查
在客户调查 XML 使用方案中,主要要求是存储具有多种方案的调查信息。不能使用一个关系表为没有固定方案的数据建模。包含 XML 列的关系表是存储此类信息的最佳选择。还可以为关系表另外添加一列,用于存储调查类型。通过使用调查类型列提取该调查类型的所有记录,可以对与某种类型调查相关的信息进行分析。在一般的调查中,用户通常不会回答所有问题。因此,与其创建多个列(每列对应调查中的一个问题)并在数据库中为未回答的问题存储 NULL 值,倒不如将每个客户的调查信息作为 XML 文档存储在一列中。将客户的调查信息作为 XML 类型列存储更适合此方案,因为:
• |
使用 XML 类型列,不同方案的调查信息可以存储在一个 XML 类型列中。将 XML 类型列与 XML 方案集合关联,这样用户就可以为多个调查类型存储数据。 |
• |
调查信息可以在用户界面上进行验证,而不必将此信息作为有类型的 XML 数据类型列进行存储,从而实现数据库级的验证。 |
• |
XQuery 可用于执行数据分析。 |
优点
在 XML 数据类型列中存储 XML 数据的优点总结如下:
• |
XML 数据类型提供了一种在服务器上存储 XML 数据的简单直观的方法,同时还保留了文档顺序和文档结构。这一点特别适用于文档顺序和文档结构非常重要的文档。假定一个简单的方案:应用程序从某个数据源获得了一个 XML 文档,并且要存储该文档。将其存储在 nvarchar 或 text 列都不能保证 XML 的格式良好,而且不能轻易访问其内容。在这种情况下,最好将传入的 XML 文档存储在内部的 XML 列中。 |
• |
XML 数据类型能够对 XML 数据进行深入的查询和修改操作。在 SQL Server 2005 之前,没有办法在数据库内部存储 XML。因此,假如要修改或查询 XML 数据,必须从 nvarchar 或 text 列中加载数据,由字符串创建 XML 文档,然后才能进行修改。将修改过的数据写回数据库的步骤与此类似。而现在使用 XML 数据类型,这些步骤就变得非常简单。 |
• |
使用 XML 数据类型,可以为 XML 数据类型列创建索引,从而加快查询处理。 |
• |
使用 XML 数据类型时,可以对 XML 数据使用 XML 方案集合和限制,从而强制实现业务规则。XML 方案可用于验证数据,添加基于类型的操作语义,在编写查询和数据修改语句过程中进行比无类型的 XML 更精确的类型检查,并且可以优化存储和查询处理。 |
• |
由于 XML 类型的数据存储在数据库中,因此它还具有很多数据库的功能,如备份和恢复、SQL Server 安全性、事务、日志等。 |
限制
以下是使用新的 XML 数据类型时必须了解的一些限制:
• |
不能存储精确的数据副本。不能保留无关紧要的空格、命名空间前缀、属性顺序和 XML 声明。 | ||||
• |
XML 文档的层次最多为 128 层。 | ||||
• |
XML 文档的内部二进制表示最大为 2GB。 | ||||
• |
XML 实例不能进行比较。因此:
| ||||
• |
XML 不能转换为 text、ntext 和 image 数据类型,因为在 SQL Server 2005 中不能使用这些类型。但是,XML 数据类型可以转换为 [n]varchar 和 [n]varbinary 类型。 |
使用 XML 数据类型的示例
示例应用程序使用 AdventureWorks 数据库中的 Sales.Store 表。Sales.Store 表包含 CustomerID 作为主键,包含 Demographics 作为 XML 列。Demographics 列包含存储调查信息。存储调查中存储的信息是可选的。这意味着,Demographics 列不一定包含所有元素。假如相同的信息已存储为关系格式,则需要将这些元素创建为表的列。由于大多数存储调查信息是可选的,因此对于大多数数据,这些列中将包含 NULL 值。这将导致表空间的浪费。为了避免这种浪费,将 Demographics 列中的存储调查信息存储为 XML 格式。Demographics 列包含每个客户的销售信息,即年销售额、年收入及银行名称等。这些字段作为 XML 元素存储在数据中。
示例应用程序具有以下功能:
• |
显示所有客户及其调查信息的列表。 |
• |
显示特定客户的调查信息。 |
• |
向 Sales.Store 表中插入新客户及其调查信息。 |
• |
修改给定客户的调查信息的某些元素(如年销售额、年收入等)。 |
• |
删除给定客户的调查信息。 |
应用程序使用 System.Data.SqlTypes.SqlXml 类从 XML 列中检索数据。SQLXML 类是对 XML 列的直接映射。
使用 SQLXML 类,可以直接从 XML 列检索数据,而不需要任何映射或转换。
下面,让我们看一个如何为客户 ID 12 检索年收入元素的示例。以下代码示例展示了上述的第二种功能。
Public void RetreiveAnnualRevenue () { SqlConnection conn = new SqlConnection(); conn.ConnectionString = @"Server=localhost; Database=AdventureWorks; integrated security=SSPI"; conn.Open(); SqlCommand command = conn.CreateCommand(); command.CommandText = @"select Demographics.query( 'declare namespace SS="http://schemas.microsoft.com/sqlserver/2004/07/adventure-works/StoreSurvey" <StoreInfo> <AnnualRevenue> {data(/SS:StoreSurvey/SS:AnnualRevenue)} </AnnualRevenue> </StoreInfo>') as Result from Sales.Store where CustomerID=12"; SqlDataReader datareader = command.ExecuteReader(); System.Text.StringBuilder builder = new System.Text.StringBuilder(); While (datareader.Read()) { SqlXml sqlxml = datareader.GetSqlXml(0); builder.Append(sqlxml.Value); } //注意:xml1 是一个 XML Web 控件 this.xml1.DocumentContent = builder.ToString(); this.xml1.TransformSource = @"StoreInfo.xslt"; }
上述方法在 AdventureWorks 数据库中创建了一个 SqlConnection 对象实例。命令文本属性包含为客户 ID 12 检索年收入字段值的查询。
这是一个 XQuery,将在 XML 列上直接执行。查询的返回值将是一个 XML 片段,该片段将映射到 SQLXML 类。然后,可以使用 SQLXML 类的 Value 属性检索该 XML 片段。
检索到的 XML 片段将使用 XML Web 服务器控件显示在客户端应用程序中。
七、不同方法的比较功能 | .NET Framework 中的 XML 类 | FOR XML / OPENXML | SQLXML | XML 数据类型 |
代码的复杂性 |
高。没有类可以在 XML 数据和关系数据之间直接进行映射。 |
中。使用 FOR XML EXPLICIT 编写查询非常困难。 |
低。SQLXML 类提供了一种将关系数据作为 XML 数据进行处理的机制,而且更新程序还可以方便地更新记录。 |
低。由于 XML 数据同样也存储在列中,因此复杂性很低。另外,Visual Studio 2005 还提供了处理 XML 数据类型的类。XML DML 可用于修改 XML 数据。 |
可维护性 |
复杂。对表中字段或 XML 的更改需要更改代码。 |
困难。对表中字段或 XML 的更改需要更改查询。 |
容易。在大多数情况下,修改映射 XSD 文件即可适应更改。 |
容易。XQuery 提供了在数据库中查询 XML 列的简单语法。 |
安装 |
除 .NET Framework 外,不需要进行其他安装。 |
不需要进行其他安装。 |
需要在客户端计算机上安装 SQLXML 库。 |
不需要进行其他安装。 |
安全性 |
在很大程度上是安全的,因为通常情况下,数据类型和格式不会在客户端公开。 |
假如适当注意避免泄漏表名和列名,则是安全的。 |
假如映射 XSD 文件存储在客户端而不是在中间层,则在设计时应考虑安全因素。 |
安全 |
支持 .Net Compact Framework |
有限的支持。Microsoft .NET Compact Framework 不支持 XmlDataDocument。 |
支持 |
不支持 |
不支持。假如 SQL Server 中的 XML 数据类型列与 SQL Server Mobile 同步,则它将转换为 ntext。 |
数据验证 |
可由客户端和服务器实施。 |
可由服务器实施。 |
可由客户端实施。 |
可由服务器使用 XML 方案实施。 |
数据存储 |
[n]varchar(max)、XML 或 [n]varbinary(max) |
关系表(可将 XML 作为字段使用)。 |
关系表(可将 XML 作为字段使用)。 |
XML 数据类型 |
保真度 |
高保真(保留字节级的 XML 数据) |
关系保真(保留数据的层次结构,但忽略元素的顺序) |
关系保真 |
信息集保真(保留 XML 数据的信息集内容) |
存储数据的访问和更新 |
支持文档级更新。 |
支持深入的数据访问和更新。 |
支持深入的数据访问和更新。 |
支持深入的数据访问和更新。 |
本文为用户提供了在 SQL Server 2005 中处理 XML 的各种选项,并对 System.Xml 命名空间、SQLXML 和 XML 数据类型进行了讨论,介绍了其相关的优点和局限性,并给出了示例方案。根据本文介绍的理想方案的性能,用户可以为自己的应用程序选择适当的 XML 选项。