介绍
Microsoft SQL Server 2005 对XML数据处理提供了广泛的支持。XML值可以以原生的形式存储在具有XML数据类型的列中,它们可以是有类型的XML(typed XML),或者是无类型的XML(untyped XML)。你可以对XML列添加索引。此外,还可以使用XQuery和XML DML来精细地操作数据,后者已成为数据修改功能的扩展。
Microsoft SQL Server 2000和SQLXML web版提供了强大的XML数据管理能力。他们主要体现在关系型数据和XML数据的映射上。关系型数据的XML视图可以用带注解的XSD(AXSD)定义,来提供一个以XML为中心的方法用以支持数据的大量装载,查询,以及对xml数据的改写能力。T-SQL扩展提供了一个以SQL为中心的方法来实现关系型的查询结果映射成XML(使用FOR XML子句),以及从XML来生成关系型视图(用OpenXML)。这些支持将在SQL Server 2005中得到更多的扩展。加上最新增加的原生XML支持功能,SQL Server 2005提供了一个强大的平台用来开发和管理半结构化和非结构化数据应用。
本文档提供了一些SQL Server 2005中XML数据建模和使用的指导。它分为以下两个主题:
·数据建模
XML数据可以以多种形式储存在SQL Server 2005中,例如,可以将原生XML数据类型或者是一个XML文档数据的一部分存储在表中。这个主题将指导大家如何正确的数据建模,还将涉及到如何对XML数据进行索引,属性提升(property promotion,),XML实例的类型化。
·用法
这个主题将讨论XML数据使用方面的内容,诸如装载XML数据到server中,输入推论在查询编辑中(and type inference in query compilation);解释和区分相近的特性;对这些特性的建议用法。还用例子来做个演示。
如果想更好的阅读本文档,建议先对SQL Server中的XML特性有个基本的了解。可以查看XML Support in Microsoft SQL Server 2005.
数据建模
这一部分简要的介绍了在SQL Server 2005中使用XML的原因,并对如何选择原生XML存储或XML视图技术提供了一些指导,和数据建模方面的建议。
关系型还是XML数据模型
如果数据具有高度的结构化和熟悉的架构,那么采用关系型模型存储数据更合适。微软SQL Server为此提供了必要的功能和相应的工具。另一方面,如果数据结构很灵活(半结构化或无结构化)或者结构未知,那你就必须为这些数据考虑合适的数据模型。
XML是个好的选择,如果你还希望这个数据模型是平*立的,数据能够在结构化和语义标记间保持灵活的话。此外,如果考虑到下面几个因素的话,就更合适了:
·数据稀疏,或者数据结构未知,或者是数据结构将来会有较大的改变。
·数据需要表现出层次性(并非实体之间的引用)且可能有递归。
·数据内含顺序特性。
·你希望根据数据的结构来查询或更改部分数据。
如果你不需要满足上面这些条件,那么你应该选择关系型数据模型。例如,如果你的数据是XML格式的,但应用程序仅仅是存储和检索它,那么一个[n]varchar(max)列足够了。将数据存储在XML列有额外的优点:存储引擎会检查数据是否具有良好的格式、合法性,且支持对XML数据的精细的查询和修改。
在SQL Server 2005存储XML数据的原因
下面是选择在SQL Server 2005中存储XML数据而非在文件系统中管理XML数据的原因:
·你希望能利用数据库服务器的管理特性来管理XML数据(如,备份、恢复、复制)。
·你希望能够以有效的、交易化的方式来共享、查询和修改XML数据。对你的应用程序而言,精细的数据访问很重要。例如,你可能希望提取XML文档中的某一部分,或者是希望插入一个新的部分而不是替换整个文档。
·你已有关系型的数据和应用程序,并且你希望在你的应用程序中关系型数据和XML数据间能互操作,你需要一个能够在整个应用程序中查询和修改数据的语言。
·你希望服务器能够保证数据的良好结构性,且能根据XML架构验证数据。
·你希望能够对XML数据添加索引来获得更有效的查询处理和更好的扩展性。
·你希望能够用SOAP、ADO.NET、OLE DB来访问XML数据。
如果不存在上面这些情况,把数据存储为非XML的,大对象类型的如[n]varchar(max) 或 varbinary(max)更为合适。不然,你还是应该采用XML来存储数据。
XML存储选项
SQL Server 2005中XML数据存储选项如下:
·作为XML数据类型来存储:
这种存储方式将保留XML数据的内容,如层次性、文档顺序、元素和属性值等。特别是XML数据的InfoSet内容被保留(更多InfoSet信息,请参见http://www.w3.org/TR/xml-infoset)。它不是XML文本的原样拷贝,它忽略了一些信息:无意义的空格、属性的顺序、命名空间前缀、XML声明。
对有类型的XML数据类型(也就是说XML数据类型和XML架构相关联),post-schema validation Infoset (PSVI,它将类型信息加入Infoset)被编码到XML数据的内部表示中,这将显著提高语法分析速度。(更多信息,请查阅W
·把XML数据映射成关系型数据来存储:
通过AXSD,XML数据被分解到一个或多个表的列中,在关系级别上忠实的保留了数据——层次结构被保留,然而元素的顺序被忽略了。架构不可被回归,操作不可逆。
·大对象存储([n]varchar(max) 和 varbinary(max)):
这种方式是XML数据的原样拷贝。这种方法对于一些特定的应用很有用,如法律文本的保存。大多数应用并不需要原样拷贝,忠实的Infoset XML内容就足够了。
一般来说,可能需要把这几种方式结合起来使用。例如,你可能需要把XML数据存储在XML数据类型列中,并且要把其属性提升到关系型的列中。反过来说,你也可能希望使用映射技术来将非递归的部分存储在非XML的列中,在XML数据类型列中只存储递归部分。
XML技术的选择
在选择原生XML还是XML视图时,一般需要考虑下面几个因素:
·存储选项:
你的XML数据可能适合采用大对象存储(如,产品手册),也可能适合存储在关系型的数据列中(如行项目转化为XML数据)。每种存储选项对文档忠实度的保留程度也不一样。
·查询能力:
由于你的查询XML数据的需要,你可能会觉得某种存储方式比另外的更适合。例如,对XML节点的谓词评估,在不同的存储选项中对此支持的程度也不同。
·XML数据索引:
你可能希望索引XML数据以提高XML查询性能。不同的存储选项有着不同的索引方法,你需要选择一个合适的方法来优化应用。
·数据修改能力:
某些应用需要对XML数据能够精细的修改(如,在一个XML文档中增加一个新的部分), 有的则不需要(如web内容管理)。语言对数据修改能力的支持可能对你的应用程序很重要。
·架构支持:
你的XML数据可能需要用架构来描述,它可能是一个XML架构文档,也可能不是。不同的XML技术对架构绑定的XML支持也不同。
很明显,不同的选择会导致系统有不同的性能表现。
原生XML存储
你可以存储XML数据在数据库服务器的XML数据类型列中,这种存储方式适用于:
·你希望有种直截了当的方法把XML数据存储在服务器中,同时保留XML文档的顺序和结构。
·你的XML数据可能有架构与其相关联。
·你希望能够查询和修改XML数据。
·你希望能够对XML数据加索引以提高查询速度。
·你的应用程序需要系统编目视图来管理XML数据和XML架构。
如果你的XML文档结构很大,或者XML文档需要和不同的XML架构、复杂的XML架构
相符合,很难被映射为关系型结构,这时原生XML存储会很有用。
例:用XML数据类型对XML数据建模
想象一下这个场景:产品手册采用XML格式,每个主题有各自的章节,每章中有多个段落(section)。段落还包含子段落,因此<section>是个递归的元素(element)。产品手册还包含大量的混合内容、图表、技术资料等,数据是半结构化的。用户希望能够对感兴趣的主题执行语义查找(如”indexing”章节中的”clustered index”段落),这里,对XML文档的合适的存储模型是XML数据类型列。这将保留XML数据的Infoset内容,并且可以对XML加索引提高查询速度。
例:保持XML数据的原样拷贝
假如*规章(XML文档格式)要求完全的原样拷贝(如,签署的文档、法律文档、股票交易单等),你可能需要存储该文档在[n]varchar(max)列中。
对查询而言,SQL Server将在运行是转化数据为XML数据,并且对其执行Xquery操作。运行时的转化可能非常消耗资源,特别是当文档很大时。如果你需要频繁的查询,你应该额外存储文档在一个XML数据列中,并对其加索引,仅在需要原样文档时才从[n]varchar(max)列中取得。
XML列可以是基于[n]varchar(max)列的计算列。你不能在XML计算列上创建XML索引,然而,你也不能在[n]varchar(max) 或 varbinary(max) 列上创建XML索引。
XML视图技术
借助于在XML架构和数据库的表之间定义映射,你可以为永久数据(不被改写的)创建“XML视图”。XML大容量装载功能可以被用来往使用XML视图的基础表上装填数据。你可以使用XPath 1.0 查询XML视图,该查询被翻译成施加在表上的SQL查询。同样的,改写也是这么做的。
这项技术适用于下面几种场合:
·你希望你的编程模型以XML为中心,这样你可以使用XML视图在已存在的关系型表上。
·你的XML数据有架构对应(XSD, XDR),该架构是你的外部合作伙伴所提供的。
·XML数据的顺序不重要,或者是需查询的数据不是递归的,或者是将来的最大递归深度已知。
·你希望使用XPath 1.0 来通过XML视图查询和修改数据。
·你希望能大容量装载XML数据并且能把它分解到使用XML视图的基表中。
关系型的数据被暴露为XML数据用于数据交换和web service,带固定架构的XML数据。更多信息,请参见the SQLXML Developer Center.
例:使用带注解的XML架构(AXSD)对数据建模
假设你已有关系型数据(诸如客户信息,订单信息),你希望把它作为XML数据来操作。你可以在关系型数据上使用AXSD来定义一个XML视图。这个XML视图允许你大量导入XML数据到表中,并且能够借助它查询和修改关系型数据。如果你需要和其他应用程序交换XML数据,同时不影响SQL应用程序的正常运行,采用这种建模非常有用。
混合模型
常见的数据建模情况是结合了关系型和XML数据类型的混合模型。XML数据中的一些值可以存储在关系型的数据列中,剩余的、也可以是全部的XML值被存储在XML列中。这会导致较好的性能(如,你可以完全控制关系型列上的索引)和锁的特性。然而,你必须对管理数据的存储付出更多的劳动。
怎样把值存储在关系型列中依赖于你的实际情况。例如,如果你基于路径表达式/Customer/@CustId检索整个XML值,接着提升(promoting)CustId属性值到关系型列中,对该列加索引,将会得到很快的查询速度。另一方面, 如果你的XML数据被彻底的且不带冗余的分解到关系型的列中,重新拼装的代价可能会很高。
对于那些有着高度结构化的XML数据(如,表的内容可以被转换成XML数据),你可以使用XML视图技术映射所有的值到关系型的列中。
使用XML数据类型的数据建模
这一部分将讨论原生XML存储方式下的数据建模。包括:索引XML数据、属性提升(property promotion)、有类型(typed)的XML数据类型。
同一个表中或不同表
XML数据类型列可以关系型列在一个表*存,也可以单独在另一张表中,通过主外键的方式和主表相联。
当满足下面条件时,可以创建XML数据类型列和关系型列在同一张表中:
·应用程序从XML数据列上检索数据,但并不要求其上有XML索引
或者
·你希望在XML数据类型列上构建索引,主表的主键就是表的聚集索引。
当满足下面条件时,在另一张表中创建XML数据类型列:
·你希望在XML数据类型列上构建XML索引,但主表的主键不是聚集索引,或者主表没有主键,或者主表没有聚集索引。
·你不希望由于表中XML列的原因导致表扫描速度下降,不管它是in-row存储还是out-of-row存储。
XML数据粒度
存储在XML列中的XML数据粒度对锁和改写特性至关重要。SQL Server 采用同样的锁机制对XML数据和非XML数据,因此,行级锁将造成一行中的所有XML实例被锁。当粒度较大时,更大的XML实例被锁将导致在多用户环境下的吞吐量下降。另一方面,过度的分解(粒度较小)将导致对象封装的丢失和重新拼装代价的提高。
在数据建模需求和锁、改写特性之间的平衡,对一个好的设计是非常重要的。
无类型、有类型和受约束的XML数据类型
SQL Server 2005 XML数据类型实现了ISO SQL-2003所定义的 标准XML数据类型。同样,它可以存储良好结构的XML 1.0 文档,以及所谓的带文本节点和任意数量的*元素的XML内容片段在无类型的XML列中。系统会检查数据是否是良好结构的,并不要求该列被绑定到XML架构上,同时在引伸意义(extended sense)拒绝不具备良好结构的数据。这对无类型的XML变量和参数也一样。
如果你有XML架构来描述XML数据,你将能够把架构和XML列关联起来产生有类型(typed)XML。相对于无类型的XML而言,在查询和数据修改语句的编译时,XML架构被用来校验数据,执行更精确的类型检查,此外,它还能优化查询和存储处理。
在下面情况下使用无类型的XML数据类型:
·你的XML数据没有架构
·你有XML架构,但你不希望服务器校验数据。有时有这种情况:应用程序在存储数据到服务器之前在客户端校验数据,或者是临时地根据架构存储非法的XML数据,或者是服务器不支持这种架构成分(如key/keyref)。
在下面情况下使用有类型的XML数据类型:
·你的XML数据有XML架构,同时你还希望服务器能根据XML架构校验XML数据。
·你希望能够根据类型信息在存储和查询方面的得到好处
·你希望在编译查询时得到
有类型的XML列,参数和变量都能够存储XML文档或者内容,只要你在声明时分别的指定标记(DOCUMENT 或 CONTENT),此外,你还可以提供XML架构集。如果每个XML实例都有完整的一个*元素,那么指定DOCUMENT,否则指定CONTENT。查询编译器在编译时使用DOCUMENT标记进行类型检查,推断出单独的*元素。
除了类型化一个XML列之外,你还可以使用关系型的约束(列约束或者是行约束)在有类型或者是无类型的XML数据类型列上,通常使用在下面的场合:
·你的商业规则不能被表示为XML架构。例如下面这个商业规则,花店的送货地址必须在花店的
·你的约束包含表中的其他XML列或非XML列,一个例子是客户ID的强制性(/Customer/@CustId),它被建立在一个XML实例上,用来匹配关系型CustomerID列的值。
文档类型定义(DTD)
XML数据类型列,变量和参数可以用XML架构来类型化,但不能用DTD来做。然而,行内(inline)的DTD可以被用来对有类型和无类型的XML提供默认值,和替换实体引用。
你可以使用第三方工具转换DTDs为XML架构文档,再把它加载到数据库中。
索引XML数据类型列
可以在XML数据类型列上创建XML索引。它将索引列中XML实例的所有标签(tags)、值和路径,并且提高了查询性能。在下面的场合中,应用程序将从XML索引中受益:
·你的工作常要查询XML列。在数据被修改时的XML索引维护成本必须要考虑进去。
·XML值比较大同时要检索的部分比较小,建立索引将避免解析整个数据,从而提高查询效率。
在XML列上所建的第一个索引被称为主XML索引。通过它,可以创建三种类型的二级XML索引提高查询速度,如下所述:
主XML索引
它将在XML列上索引XML实例的所有标签(tags)、值和路径。基表(也就是有XML列的表)的主键必须是聚集索引。主键被用来关联索引行和基表中的行。可以从XML列中检索完整的XML实例(举例来说,SELECT *)。使用主XML索引的查询返回标量值或XML子树。
例:创建主XML索引
我们将在大多数例子中以表T(pk INT PRIMARY KEY, xCol XML)为例,它有一个无类型的XML列,通过简单的方法可以被扩展为有类型的XML。为了更容易理解,被查询的XML实例如下:
<book genre="security" publicationdate="2002" ISBN="0-7356-1588-2">
<title>Writing Secure Code</title>
<author>
<first-name>Michael</first-name>
<last-name>Howard</last-name>
</author>
<author>
<first-name>David</first-name>
<last-name>LeBlanc</last-name>
</author>
<price>39.99</price>
</book>
下面的语句在表T的XML列xCol上创建了XML索引idx_xCol:
CREATE PRIMARY XML INDEX idx_xCol on T (xCol)
二级XML索引
一旦主XML索引被建立,你便可以创建二级XML索引来针对不同查询提高速度,有三种类型的二级索引:路径、属性、值分别能提高基于路径的查询、定制属性管理、基于值的查询速度。路径索引针对列中所有XML实例按照每个XML节点在文档中的顺序构建一个B+-树(路径,值)。属性索引创建一个聚集的B+-数在(主键,路径,值)上,值索引和路径索引类似,但其创建的B+-树是在(值,路径)上的。
下面是一些创建二级索引的指南:
·如果你经常在XML列上使用路径表达式,路径二级索引将会很有用,最常用的案例是在T-SQL的where子句中对XML列使用exist()方法。
·如果你需要使用路径表达式从单个XML实例中检索多个值,属性二级索引中的聚集路径将会对此有帮助,通常在属性包场景中有这种情况,某个对象的属性被提取,而它的主键值是已知的。
·如果你的工作需要查询XML实例中的值,但你并不知道包含值的元素或是属性名称,你可能想为此创建值索引。这种情况通常发生在有后代(descendant axes)的查找上,诸如//author[last-name="Howard"],这里<author>元素可以是层次的在任何一个级别上。它也可能用在有通配符的查询里,诸如/book [@* = "novel"],该查询将为<book>元素查找值为"novel"的属性。
例:基于路径的查询
假设有如下查询:
SELECT pk, xCol
FROM T
WHERE xCol.exist ('/book[@genre = "novel"]') = 1
路径表达式 /book/@genre 和值"novel"对应路径索引的关键字,因此,一个二级的XML路径索引将会有用:
CREATE XML INDEX idx_xCol_Path on T (xCol)
USING XML INDEX idx_xCol FOR PATH
例:提取对象属性
考虑下面这个查询,它从表中每一行检索属性"genre," "title," 和ISBN:
SELECT xCol.value ('(/book/@genre)[1]', 'varchar(50)'),
xCol.value ('(/book/title)[1]', 'varchar(50)'),
xCol.value ('(/book/@ISBN)[1]', 'varchar(50)')
FROM T
下面创建的属性索引对这个例子很有用:
CREATE XML INDEX idx_xCol_Property on T (xCol)
USING XML INDEX idx_xCol FOR PROPERTY
例:基于值的查询
在下面的查询里,后代或自身斜杠(//)指定了一个部分路径,因此基于ISBN的值的查询将会用到值索引:
SELECT xCol
FROM T
WHERE xCol.exist ('//book[@ISBN = "1-8610-0157-6"]') = 1
创建如下值索引:
CREATE XML INDEX idx_xCol_Value on T (xCol)
USING XML INDEX idx_xCol FOR VALUE
在XML列上的全文索引
你可以在XML列上创建全文索引,它将索引XML的值而忽略XML标记。属性值是不能被全文索引的(因为它们被看作标记的一部份),元素标记被用做边界符。你可以把XML索引和全文索引结合起来用在这些场合:
·首先,使用SQL 全文索引过滤出感兴趣的XML值。
·接着,使用XML索引查询这些XML值。
例:结合XML查询的全文搜索
一旦全文索引被创建在XML列上,下面的查询将检查书的标题中的XML值是否包含"custom"词:
SELECT *
FROM T
WHERE CONTAINS(xCol,'custom')
AND xCol.exist('/book/title/text()[contains(.,"custom")]') =1
CONTAINS()方法将使用全文索引找出XML文档中包含"custom"词的XML值的子集,接着,exist()子句确保"custom"词是在书的标题中出现而不是别处。
全文索引使用的CONTAINS()和XQuery的contains()有着不同的语法。后者用于子串匹配,而前者使用单词多形态(word stemming)来进行记号(token)匹配。例如,如果搜索标题中的字符串"run",那么,"run", "runs," 和 "running"都匹配,因为全文索引的CONTAINS()和XQuery的contains()都被满足。然而,如果标题中有"customizable"词,那么上面的查询则不匹配(全文索引的CONTAINS()失败,但是XQuery的contains()成功)。一般来说,对于纯粹的子串匹配,应该将全文索引的CONTAINS()去掉。
此外,全文搜索使用单词多形态,而XQuery的contains()是文字匹配。下面的例子说明了它们之间的区别。
例:使用多形态全文搜索XML值
上个例子中的XQuery的contains()一般来说不能别去掉,考虑下面的查询:
SELECT *
FROM T
WHERE CONTAINS(xCol,'run')
文档中"ran"这个词因为满足多形态的关系匹配搜索条件。此外,该查询也没有用XQuery来检查搜索文本。
当使用AXSD分解XML到有全文索引的关系型列上,对XML视图的XPath查询并不在基表上执行全文搜索。
Property Promotion
如果查询主要用于查找较少的元素和属性的值(如,基于customer id查找客户,/Customer/@CustId的值已被指定),你可以提升(promote)这些值到关系型的列中,这对于那些从整个XML实例中检索小部分XML数据的查询有一定帮助。此时创建XML索引显得牛刀杀鸡,相反,仅需对提升的列加索引即可。查询必须被写成使用提升列才行(也就是,查询优化器并不能把XML列上的查询重定向到提升列上)。
提升列可以是在同一个表的计算列或另一个表的用户维护的列,这对于从每个XML实例中提升单个值是足够了(也就是说单值属性),然而,对于多值属性,你必须为其创建一个单独的表,如下所示:
基于XML数据类型的计算列
计算列可以用含XML数据类型方法的用户自定义函数来创建。这种类型的计算列可以是任何SQL类型,包括XML类型。如下所示:
例:基于XML数据类型方法的计算列
首先为书的ISBN创建用户自定义函数:
CREATE FUNCTION udf_get_book_ISBN (@xData xml)
RETURNS varchar(20)
BEGIN
DECLARE @ISBN varchar(20)
SELECT @ISBN = @xData.value('/book[1]/@ISBN', 'varchar(20)')
RETURN @ISBN
END
接着在表中为ISBN增加一个计算列:
ALTER TABLE T
ADD
ISBN
AS
dbo.udf_get_book_ISBN(xCol)
计算列可以用常规方式来添加索引。
例:针对该计算列的查询
为了查找出ISBN为0-7356-1588-2的<book>,查询如下:
SELECT xCol
FROM T
WHERE xCol.exist ('/book[@ISBN = "0-7356-1588-2"]') = 1
如果使用计算列,查询如下:
SELECT xCol
FROM T
WHERE ISBN = '0-7356-1588-2'
你可以创建用户自定义函数来返回XML数据类型,也可以使用UDF返回计算列,但你不能在计算列上创建XML索引。
创建属性表
你可能希望能够提升一些XML数据中的多值属性到一个或多个表中,为其创建索引,重定向查询来使用它们。典型的应用场景是一些小数量的属性查找却耗费了大量的查询资源。你可以这么做:
·创建一个或多个表来存放多值属性。你可能会发现每个表存放一个属性会更便利,在每个属性表中复制基表的主键以便和基表相连。
·如果你希望维护属性的顺序,你需要为其引进一个单独的列。
·可以在XML列上创建触发器来维护属性表,在触发器中,实现下面功能:
·使用XML数据类型方法,诸如nodes() 和 value(),来向属性表插入和删除记录。
·在CLR中创建流表值(streaming table-valued)函数来向属性表插入和删除记录。
·针对属性表采用SQL访问方式,针对基表XML列使用XML访问方式,用主键来连接它们。
例:创建一个属性表
假设你希望提升authors的first name,书会有一个或多个作者,这样first name是个多值属性。每个first name被存在一个属性表的不同行中。基表的主键被复制到属性表中用以向后连接。
create table tblPropAuthor (propPK int, propAuthor varchar(max))
例:创建用户自定义函数来从XML实例中生成记录集
下面的表值函数udf_XML2Table接受一个主键值和一个XML实例,它检索<book>元素所有作者的first name,并返回一个(主键,first name)记录集。
create function udf_XML2Table (@pk int, @xCol xml)
returns @ret_Table table (propPK int, propAuthor varchar(max))
with schemabinding
as
begin
insert into @ret_Table
select @pk, nref.value('.', 'varchar(max)')
from @xCol.nodes('/book/author/first-name') R(nref)
return
end
例:创建触发器来填充属性表
Inserted触发器:向属性表中插入记录
create trigger trg_docs_INS on T for insert
as
declare @wantedXML xml
declare @FK int
select @wantedXML = xCol from inserted
select @FK = PK from inserted
insert into tblPropAuthor
select * from dbo.udf_XML2Table(@FK, @wantedXML)
Delete触发器:基于基表中要删除行的主键值来删除属性表中的行
create trigger trg_docs_DEL on T for delete
as
declare @FK int
select @FK = PK from deleted
delete tblPropAuthor where propPK = @FK
Update触发器:根据被改写的XML实例删除属性表中存在的记录,再插入新的记录到属性表中
create trigger trg_docs_UPD
on T
for update
as
if update(xCol) or update(pk)
begin
declare @FK int
declare @wantedXML xml
select @FK = PK from deleted
delete tblPropAuthor where propPK = @FK
select @wantedXML = xCol from inserted
select @FK = pk from inserted
insert into tblPropAuthor
select * from dbo.udf_XML2Table(@FK, @wantedXML)
end
例:查找XML实例中first name为"David"的作者
查询可以在XML列上操作,可供选择的另一种方法是,可以在属性列上查找,然后和基表相关联返回一个XML实例,如:
SELECT xCol
FROM T JOIN tblPropAuthor ON T.pk = tblPropAuthor.propPK
WHERE tblPropAuthor.propAuthor = 'David'
例:使用CLR流表值(streaming table-valued)函数
这种方法包括下面几步:(a)定义一个CLR类SqlReaderBase,它实现ISqlReader,并且通过在XML实例上应用路径表达式生成流表值的输出;(b)创建一个装配件和T-SQL用户自定义函数来调用CLR类;(c)定义insert, update, 和 delete触发器维护属性表。
首先,我们来创建流CLR函数,它的主干如下所示。XML数据类型被暴露为ADO.NET中的一个受托管类,该类支持CreateReader()方法,返回一个XmlReader。
public class c_streaming_xml_tvf {
public static ISqlReader streaming_xml_tvf
(SqlXml xmlDoc, string pathExpression) {
return (new TestSqlReaderBase (xmlDoc, pathExpression));
}
}
// Class that implements ISqlReader
public class TestSqlReaderBase : ISqlReader {
XPathNodeIterator m_iterator;
public SqlChars FirstName;
// Metadata for current resultset
private SqlMetaData[] m_rgSqlMetaData;
public TestSqlReaderBase (SqlXml xmlDoc, string pathExpression) {
// Variables for XPath navigation
XPathDocument xDoc;
XPathNavigator xNav;
XPathExpression xPath;
// Set sql meta data
m_rgSqlMetaData = new SqlMetaData[1];
m_rgSqlMetaData[0] = new SqlMetaData ("FirstName",
SqlDbType.NVarChar,50);
//Set up the Navigator
if (!xmlDoc.IsNull)
xDoc = new XPathDocument (xmlDoc.CreateReader());
else
xDoc = new XPathDocument ();
xNav = xDoc.CreateNavigator();
xPath = xNav.Compile (pathExpression);
m_iterator = xNav.Select(xPath);
}
public bool Read() {
bool moreRows = true;
if (moreRows = m_iterator.MoveNext())
FirstName = new SqlChars (m_iterator.Current.Value);
return moreRows;
}
}
接下来,创建一个装配件和T-SQL用户自定义函数SQL_streaming_xml_tvf和CLR函数streaming_xml_tvf相对应。用户自定义函数被用来定义表值函数CLR_udf_XML2Table用以生成记录集:
create function CLR_udf_XML2Table (@pk int, @xCol xml)
returns @ret_Table table (FK int, FirstName varchar(max))
with schemabinding
as
begin
insert into @ret_Table
select @pk, FirstName
FROM SQL_streaming_xml_tvf (@xCol, '/book/author/first-name')
return
end
最后,定义触发器,用函数CLR_udf_XML2Table替代udf_XML2Table,这样,insert触发器如下:
create trigger CLR_trg_docs_INS on T for insert
as
declare @wantedXML xml
declare @FK int
select @wantedXML = xCol from inserted
select @FK = PK from inserted
insert into tblPropAuthor
select *
from dbo.CLR_udf_XML2Table(@FK, @wantedXML)
删除触发器无需改变,改写触发器仅仅需要把函数udf_XML2Table()替换为CLR_udf_XML2Table()即可。
XML架构集
一个XML架构集是一个元数据的实体,包含一个或多个相关的(例如通过<xs:import>)、也可以是不相关的XML架构。一个XML架构集中的单个XML架构通过其命名空间来识别。
可以用CREATE XML SCHEMA COLLECTION语法来创建XML架构集和提供一个或多个XML架构。可以向一个已有的XML架构中添加XML架构组件(component),也可以通过ALTER XML SCHEMA COLLECTION来向已有的XML架构集中添加架构。在SQL Server 2005中,XML架构集可以向任何SQL对象那样使用安全模型来确保安全。
多类型列
一个XML架构集C根据多个XML架构类型化一个XML列xCol,加之标记DOCUMENT或CONTENT可以分别指定了是XML树还是片断被存储在列xCol中。
对DOCUMENT来讲,每个XML实例根据验证和类型指定了它的*元素的目标命名空间,另一方面,对CONTENT,每个*元素可以指定在C中的任何一个目标命名空间。XML实例依照其中的所有目标命名空间来验证和类型化。
架构进化
XML架构集被用来类型化列、变量和参数,它提供了一种XML架构进化的机制。假设你要增加一个目标命名空间为BOOK-V1 XML架构到XML架构集C。一个使用C来类型化的XML列xCol便可以存储和BOOK-V1 架构相一致的XML数据。
假设一个应用程序希望用新的XML组件扩展XML架构,诸如复杂的类型定义和*元素声明,这些新的架构组件可以被添加到BOOK-V1 架构中,并不要求重新验证xCol列中已存在的XML数据。
假设在稍后时候,这个应用程序希望提供一个XML架构的新版本,目标命名空间为BOOK-V2,这个XML架构可以被添加到C,XML列可以同时存储BOOK-V1和BOOK-V2,XML列上的查询和数据修改要和这些命名空间相一致。
用法
装载xml数据
从SQL Server 2000传输XML数据到SQL Server 2005
你可以用多种方法传输XML数据到SQL Server 2005,下面这一段落我们将作些讨论。
·如果在你的SQL Server 2000数据库中数据是在[n]text 或 image列,你可以使用DTS把表导入SQL Server 2005数据库中,再使用ALTER TABLE命令把列的类型改为XML。
·你可以使用bcp out从SQL Server 2000中大容量拷贝数据出来,再用bcp in大容量插入到SQL Server 2005数据库中。
·如果数据在SQL Server 2000中的关系型列中,那么创建一个带ntext列的新表,也可以为该表增加一个主键列用于行标识。使用客户端程序检索由服务器通过FOR XML生成的XML数据,再把它写到ntext列中,接着可以使用上面提到的技术传输数据到SQL Server 2005中。你也可以选择直接写XML数据到SQL Server 2005的XML列中。
例:把列类型改为XML
假设你希望把表R的XYZ列的类型由[n]text 或image改为无类型的XML,你可以用下面的命令:
ALTER TABLE R ALTER COLUMN XYZ XML
如果需要,你可以指定一个XML架构集把类型改为有类型的XML。
大容量装载(bulk load)XML数据
你可以使用SQL Server中大容量装载功能把XML数据大容量装载到服务器中,诸如使用bcp命令。OPENROWSET命令允许你从文件中装载数据到XML列中,下面是一些相关的例子:
例:从文件装载XML
这个例子说明了如何往表T中插入一行,该行的值来自文件C:/yukon/xmlfile.xml,假设整型列的值为10:
INSERT INTO T
SELECT 10, xCol
FROM (SELECT *
FROM OPENROWSET (BULK 'C:/Yukon/xmlfile.xml', SINGLE_CLOB)
AS xCol) AS R(xCol)
文本编码
SQL Server 2005以Unicode(UTF-16)类型存储XML数据,从服务器被检索的XML数据被编码为UTF-16。如果你希望不同的编码,你需要对检索数据执行必要的转换。有时,你的XML数据可能有不同的编码,那么在数据装载时要小心:
·如果你的文本XML是Unicode (UCS-2, UTF-16)编码,可以直接分配给XML列,变量,参数,不会有问题。
·如果编码不是Unicode,并且是隐含的(和源代码页有关),数据库中的字符串代码页应该和你希望装载的代码(如有必要,使用COLLATE)相同或兼容,如果不存在这样的服务器代码页,你应该用适当的编码添加一个显式的XML声明。
·使用显式的编码,或者使用varbinary()类型(它和代码页无关),或者使用适当的代码页的字符串类型,再分配数据到XML列,变量和参数。
例:显式地指定编码
假设你有一个XML文档(vcdoc)被作为varchar(max)存储,并没有一个显式的XML声明。下面的语句用编码"iso8859-1"增加一个 XML声明,连接XML文档,转换结果为varbinary(max),以便字节表示被保留,最终被转换为XML。这将使XML处理器根据指定的编码"iso8859-1"解析数据,并为其生成对应的UTF-16表示值。
SELECT CAST(
CAST (('<?xml version="1.0" encoding="iso8859-1"?>'+ vcdoc)
AS VARBINARY (MAX))
AS XML)
Xquery和类型推论(type inference)
Xquery(http://www.w3.org/TR/xquery/)被嵌入T-SQL语言中以支持对XML数据类型的查询。该语言正由W
错误模型
Xquery表达式和XML DML语句的语法错误将返回编译错误。编译阶段检查XQuery表达式和DML语句的静态类型正确性,并且对有类型XML使用XML架构用于类型推论。如果一个表达式由于类型安全违反而导致运行时失败,它将引发静态类型错误。一个这样的例子是:增加字符串值到整型数据列中,或者查询类型数据中的一个不存在的节点。
和W
显式转换到正确的类型将使用户避免静态错误,虽然运行错误将被转换为空序列。
下面的子段落将详细的讨论类型检查。
单态(singleton)检查
如果编译器不能确定在运行时是否能保证一个单态,那么诸如位置步骤、函数参数、操作符(例如eq)这些要求单态的都回返回一个错误。这种问题经常是由无类型的数据而引起。例如,查询一个attribute要求一个单态双亲元素,一个双亲节点的顺序选择就够了。将node()-value()结合以提取attribute值可能并不要求顺序的指定,就像下面的例子所示。
例:一个已知的单态
在这个例子中,nodes()方法为每个<book>元素生成分离的行。value()方法对<book>节点求值,提取@genre的值,而@genre作为一个属性,是一个单态。
SELECT nref.value('@genre', 'varchar(max)') LastName
FROM T CROSS APPLY xCol.nodes('//book') AS R(nref)
XML架构对有类型XML的类型检查有用。如果一个节点被指定作为XML架构中的单态,编译器将获悉这个信息,不报错,否则,将需要依次的挑选一个单独的节点。特别地,后代轴或者自身轴(//),如/book//title,对<title>元素遗失了单态集推论,即使对其指定了XML架构。可将它重写为(/book//title)[1]。
要注意,在类型检查时,确保//first-name[1]和(//first-name)[1]的区别非常重要。前者返回一个<first-name>节点的序列,每个节点都是它同胞节点的最左边<first-name>节点,后者返回XML实例中的在文档顺序里的第一个单态<first-name>节点。
例:使用value()
下面在一个无类型的XML列上的查询将导致一个静态、编译错误,因为value()期望一个单态节点作为第一个参数,编译器不能决定是否只有一个<last-name> 节点在运行中发生:
SELECT xCol.value('//author/last-name', 'nvarchar(50)') LastName
FROM T
下面这个修正看起来不错:
SELECT xCol.value('//author/last-name[1]', 'nvarchar(50)') LastName
FROM T
然而,因为在每一个XML实例中可能会有多个<author>节点,其实它并没有修正错误。下面的代码是正确的:
SELECT xCol.value('(//author/last-name)[1]', 'nvarchar(50)') LastName
FROM T
该查询返回每个XML实例中第一个<last-name>元素的值
双亲轴
如果一个节点的类型不能被确定,它便成为anyType,它不能被隐式的转换到任何别的类型。这在使用双亲轴导航时最有可能发生(如xCol.query('/book/@genre/../price')),双亲节点类型被确定为anyType。一个元素也可能被定义为anyType。在这些情况中,更多精确类型信息的遗失常导致静态类型错误,并要求显式的转换原子值到它们的明确的类型。
Data(),Text(), and String() Accessors
XQuery有函数fn:data()可以从节点中提取标量的,类型化的值,对节点用text()返回文本节点,函数fn:string()返回节点的字符串值。它们的用法有时会造成混淆,SQL Server 2005中对它们的使用指南如下,让我们考虑有XML实例<age>12</age>:
·无类型XML:路径表达式/age/text()返回文本节点”
12”
。函数fn:data(/age)返回字符串值”
12”
,就像fn:string(/age)一样。
·有类型XML:对任何简单的有类型<age>元素,表达式/age/text()返回静态错误。在另一方面,fn:data(/age)返回整型12,而fn:string(/age)返回字符串”
12”
。
联合类型的函数和操作符
由于类型检查的原因,联合类型需要仔细的处理,下面的例子演示了两个问题。
例:联合类型的函数
考虑一个联合类型<r>的元素定义
<xs:element name="r">
<xs:simpleType>
<xs:union memberTypes="xs:int xs:float xs:double"/>
</xs:simpleType>
</xs:element>
在XQuery上下文中,"average"函数fn:avg (//r)返回一个静态错误,因为XQuery编译不能对<r>元素的不同的类型求和(xs:int, xs:float, or xs:double),为了解决它,重写函数fn:avg(for $r in //r return $r cast as xs:double ?)。
例:联合类型的操作符
加操作符'+'要求精确的操作数类型,所以表达式(//r)[1] + 1将返回一个静态错误,重写它来解决这个问题:(//r)[1] cast as xs:int? +1,这里"?"预示是0或者1。SQL Server 2005要求"cast as" 带"?",因为由于运行时错误,任何类型转换都能导致空序列。
Value(), Nodes() 和 OpenXML()
你可以在一个SELECT子句中对XML数据类型使用多个value()方法,从而提取值生成一个行集。nodes()方法呢,它可以生成一个对每个被选择的节点的内部引用,这个可以被将来的查询所用。将nodes()和value()结合起来可以更有效的生成行集,尤其是在有多列,且可能使用了路径表达式的情况下。
nodes()方法生成一个指定的XML数据类型的多个实例,它们中的每一个都有自己的上下文设置到不同的被选择的节点,象这样的一个XML实例支持query(), value(), nodes(), 和 exist()方法,并可被用于count(*)聚集,所有别的用法将会出错。
例:nodes()的使用
假设你希望提取作者的first name 和last name,且first name不是“David”,行集是由firstname和lastname两列构成,通过使用nodes() and value()方法,你可以实现它,如下所示:
SELECT nref.value('first-name[1]', 'nvarchar(50)') FirstName,
nref.value('last-name[1]', 'nvarchar(50)') LastName
FROM T CROSS APPLY xCol.nodes('//author') AS R(nref)
WHERE nref.exist('.[first-name != "David"]') = 1
在这个例子中,对每一个XML实例,nodes('//author')生成一个引用<author>元素的行集,作者的first和last名字通过value()方法关系到这些引用来获得。
SQL Server 2000 提供了一个方法用于从一个XML实例中生成行集:OpenXml()。你可以为行集指定一个关系型的架构,这样XML实例中的值被映射到行集中的列上。
例:对XML数据类型使用OpenXml()
我们可以用OpenXml()重写上面那个查询,通过创建一个游标,将每个XML实例读到XML变量中,再对其应用OpenXml()。
DECLARE name_cursor CURSOR
FOR
SELECT xCol
FROM T
OPEN name_cursor
DECLARE @xmlVal XML
DECLARE @idoc int
FETCH NEXT FROM name_cursor INTO @xmlVal
WHILE (@@FETCH_STATUS = 0)
BEGIN
EXEC sp_xml_preparedocument @idoc OUTPUT, @xmlVal
SELECT *
FROM OPENXML (@idoc, '//author')
WITH (FirstName varchar(50) 'first-name',
LastName varchar(50) 'last-name') R
WHERE R.FirstName != 'David'
EXEC sp_xml_removedocument @idoc
FETCH NEXT FROM name_cursor INTO @xmlVal
END
CLOSE name_cursor
DEALLOCATE name_cursor
OpenXml()创建了一个内存中的表述,并使用工作表来代替查询处理器。它MSXML 3.0的XPath 1.0而不是XQuery引擎。工作表并不在对OpenXml()的多个调用间共享,即便是在同一个XML实例上,这样限制了它的可扩展性。OpenXml()允许你访问边缘表格式的XML数据(在不指定WITH子句时),它还允许你使用在一个分离的,"overflow"列中的剩余的XML值。
将nodes() 和 value()功能结合起来能更有效的使用XML索引。这样,这种结合要比OpenXml具有更大的扩展性。
从行集中使用 FOR XML生成XML
你可以通过使用带TYPE指示的FOR XML从行集中生成一个XML数据类型实例。
结果可以被分配给一个XM数据类型列,变量或者是参数,而且,FOR XML还能够嵌套使用生成任何层次结构。这使得嵌套的FOR XML比FOR XML EXPLICIT要更方便书写,但是它可能对深度层次性能不是很好。FOR XML也引入了一个新的PATH节点,该节点指出了列的值在XML树中的路径。
新的FOR XML TYPE指示可以被用来定义在关系型数据上的只读的XML视图,该视图可以用SQL 语句和嵌入的XQuery来查询,如下面的例子所示。例如,你可以在存储过程中采用这样的视图。
例:返回XML数据类型的SQL视图
下面的SQL视图定义创建了一个XML视图,它基于一个关系型列(pk)和从一个XML列中检索的作者:
CREATE VIEW V (xmlVal) AS
SELECT pk, xCol.query('/book/author')
FROM T
FOR XML AUTO, TYPE
视图V包含一个单XML类型列xmlVal,它可以象规则的XML数据类型实例那样被查询,例如,下面的查询返回first name为“David”的作者:
SELECT xmlVal.query('//author[first-name = "David"]')
FROM V
SQL视图定义有点类似于采用带注解的架构创建的XML视图,然而,它们之间存在重大的差别。SQL视图定义是只读的,并且必须用嵌入式XQuery来操作,这点和采用带注解的架构创建的XML视图差别很大,此外,SQL视图在应用XQuery表达式之前物化(materializes)XML结果,而在XML视图上的XPath查询则估计为基表上的SQL查询。
增加商业逻辑
可以用下面几种方法增加商业逻辑到XML数据中:
·你可以通过写行、列约束来实现在插入和修改XML数据时的指定域的约束。
·你可以在XML列上写触发器,该触发器在你插入和修改列值时激活,触发器中可以包含指定域的验证规则或填充属性表。
·你可以在你传递XML值的托管代码中写SQLCLR函数,并使用System.Xml命名空间提供的XML处理能力。例如,可以对XML数据应用XSL转换,象下面例子所示。可供选择的方法还有反序列化XML到一个或多个托管类中,并且使用托管代码操作它。
·你可以根据商业需要写T-SQL存储过程和函数来调用对XML列的处理操作。
例:XSL转换的应用
考虑一下这个CLR函数TransformXml(),它接受一个XML数据类型实例和一个存储在文件中的的XSL转换,应用这个转换到XML数据,并且返回被转换过的XML,该函数的主干如下(使用C#编写):
public static SqlXml TransformXml (SqlXml XmlData, string xslPath) {
// Load XSL transformation
XslTransform xform = new XslTransform();
XPathDocument xslDoc = new XPathDocument (xslPath);
xform.Load (xslDoc.CreateNavigator(),null);
// Load XML data
XPathDocument xDoc = new XPathDocument (XmlData.CreateReader());
XPathNavigator nav = xDoc.CreateNavigator ();
// Return the transformed value
SqlXml retSqlXml = new SqlXml (xform.Transform(nav, null));
return (retSqlXml);
}
一旦这个装配件被注册,再创建一个和TransformXml()相应的用户自定义T-SQL函数SqlXslTransform(),那么我们可以从T-SQL中调用该函数:
SELECT SqlXslTransform (xCol, 'C:/yukon/xsltransform.xsl')
FROM T
WHERE xCol.exist('/book/title/text()[contains(.,"custom")]') =1
这个查询结果包含一个被转换的XML的行集。
SQLCLR给我们提供了全新的方法,它可以被用来分解XML数据到表或用于属性提升,也可以使用在System.Xml命名空间中的托管类查询XML数据。更多信息可以在SQL Server 2005和Microsoft Visual Studio “Whidbey”联机帮助中发现。
交叉域查询
当你的数据既有关系型列也有XML数据类型列时,你可能想写的查询既能处理关系型也能处理XML数据。例如,你可以使用FOR XML转换关系型数据和XML列数据到一个XML数据类型实例,再使用XQuery查询它。或者反过来,你可以从XML值生成行集,再用T-SQL查询它。
一个更方便有效的方法是写交叉域查询,它在XQuery或者XML DML表达式中使用SQL 变量或列的值:
·你可以用sql:variable()在你的XQuery或XML DML表达式中使用SQL 变量的值。
·你可以用sql:column()在你的XQuery或XML DML表达式中使用关系型列的值。
· 这种方法允许应用程序参数化查询,如下面例子所示。然而,sql:variable()和sql:column()并不允许XML和用户自定义类型。
例:使用sql:variable()的交叉域查询
下面的查询是例子:Example: queries on a computed column based on XML data type methods.的修改版,在这个版本中,所感兴趣的ISBN通过SQL变量@isbn来传递,用sql:variable()来替换常量,该查询可以被用来查找任何ISBN,而不仅仅是ISBN: 0-7356-1588-2。
DECLARE @isbn varchar(20)
SET @isbn = '0-7356-1588-2'
SELECT xCol
FROM T
WHERE xCol.exist ('/book[@ISBN = sql:variable("@isbn")]') = 1
可以用类似的方法使用Sql:column(),且能提供额外的好处:基于代价的查询优化器决定让列上的索引使查询更有效,此外,如在A computed column based on an XML data type.讨论的那样,计算列可以存储提升的属性。
原生XML支持的编目视图
编目视图用于提供关于XML使用的元数据信息,下面将讨论它的一些特性。
XML索引
在编目视图sys.indexes中的XML索引项的”type”列为3,”name”列则包含了XML索引的名字。
XML索引也记录在编目视图sys.xml_indexes中,它包含所有的sys.indexes列,并有一些专门用于XML索引的意义,在列”secondary_type”值NULL指明这是个主XML索引,值”P”,”R”,”V”分别代表二级索引的PATH,PROPERTY,VALUE。
XML索引的空间占用可以用表值函数sys.fn_indexinfo( )查看,它提供了一些信息诸如:磁盘空间占用量,平均每行占多少字节,记录数,和一些所有索引类型都有的其他信息,包括XML索引。这些信息对每个数据库分区都可用。XML索引和基表使用同样的分区架构和分区功能。
例:XML索引的空间使用
SELECT sum(Pages)
FROM sys.fn_indexinfo ('T', 'idx_xCol_Path' , DEFAULT, 'DETAILED')
它将生成表T上的XML索引index idx_xCol_Path在所有分区的磁盘占用数,如果不用sum(),将返回每个分区的磁盘占用情况。
检索XML架构集
XML架构集被存储在编目视图sys.xml_schema_collections中,XML架构集”sys”由系统定义,它包含预定义的命名空间可被用于所有用户自定义的XML架构集中,无须显式的装载它们。它包含的命名空间有:xml, xs, xsi, fn, xdt。另外两个值得提及的编目视图是:sys.xml_schema_namespaces,它列举了每个XML架构集的所有命名空间;sys.xml_components,它列举了每个XML架构的组件。
系统内建的函数XML_SCHEMA_NAMESPACE (schemaName, XmlSchemacollectionName, namespace-uri)生成一个XML数据类型实例,它包含XML架构集中的XML架构片断,除了预定义的XML架构。
你可以列出用下面的方法列出XML架构集的内容:
·对合时的XML架构集的编目视图写T-SQL查询。
·使用内建的函数XML_SCHEMA_NAMESPACE()。你可以在这个函数的输出上应用XML数据类型方法,但你不能修改基XML架构。
·下面有一些示例:
例:列出一个XML架构集中的命名空间
对XML架构集”myCollection”使用下面的查询:
SELECT XSN.name
FROM sys.xml_schema_collections XSC JOIN sys.xml_schema_namespaces XSN
ON (XSC.xml_collection_id = XSN.xml_collection_id)
WHERE XSC.name = 'myCollection'
例:列出一个XML架构集的内容
下面的语句列出和架构dbo有关的XML架构集”myCollection”的内容
SELECT XML_SCHEMA_NAMESPACE (N'dbo', N'myCollection') 1
通过指定目标命名空间给XML_SCHEMA_NAMESPACE()的第三个参数,架构集中的单个XML架构可以被作为XML数据类型实例来获得,如下所示:
例:从一个XML架构集中输出指定的架构
下面的语句从XML架构集"myCollection"中输出目标命名空间为“http://www.microsoft.com/books”的XML架构。
SELECT XML_SCHEMA_NAMESPACE (N'dbo', N'myCollection',
N'http://www.microsoft.com/books')
查询XML架构
如果你希望查询装载进XML架构集的XML架构,你可以通过以下的方法:
·为XML架构命名空间在编目视图上写T-SQL查询。
·创建一个包含XML数据类型列的表来存储你的XML架构,除此之外,还可以装载它们到XML类型系统。你可以使用XML数据类型方法查询XML列,此外,你还可以在该列上建XML索引。你可以将维护存储在XML列的XML架构和XML类型系统一致性的工作留给应用程序来处理,然而,如果你从XML类型系统中删除了XML架构命名空间,你还需要从表中删除它来保持一致性。