[Flex]flex选修课之第二课:与servlet服务端交互

mikel阅读(896)

flex4 lesson 2 : dynamic hello world interacting with server

flex选修课
基于flex4技术从零开发flex博客系统
sban 2009-4-19

第二课:与servlet服务端交互

上一节课,我们讲了如何配置开发环境,包括客房端flex开发环境,以及服务端java开发环境,并且编写了一个客房端示例程序 helloworld,但遗憾的是,目前这一个helloworld不是动态的。如果客户端不能与服务端进行数据交互,那么我感觉我并没有真正入门。

我审视了一下eclipse为我创建的gapp_flexblog项目,它包括以下目录结构:

Guestbook/
  src/
    …Java source code…
    META-INF/
      …other configuration…
  war/
    …JSPs, images, data files…
    WEB-INF/
      …app configuration…
      lib/
        …JARs for libraries…
      classes/
        …compiled classes…

src为java源码目录,war为程序布署目录,包括了编译之后的所有文件(注:Google App Engine采用标准的war格式组织web程序目录)。与flex中的bin-release目录相当。flex默认把编译之后的文件放在bin- Debug目录下,这是在Debug模式下,如果是发布模式,则放在bin-release目录下。

我在sban.flexblog.server下添加了一个HelloWorldServlet.java文件:

package sban.flexblog.server;

 

import java.io.IOException;
import javax.servlet.http.*;

public class HelloWorldServlet extends HttpServlet {
        public void doGet(HttpServletRequest req, HttpServletResponse resp)
            throws IOException 
        {
                String[] name = req.getParameterValues("name");
                
                resp.setContentType("text/plain");
                resp.getWriter().println("Hi " + name[0] + ",Hello, world.");
        }
}

Google App Engine采用java servlet处理客户端与服务端的数据交互。该类继承于HttpServlet,doGet用于处理客户端的get指求。(注:如果是post请求,将报错)

那么Google App Engine是如何处理客户端请求的呢,或者说对于url是怎么处理的?在war/WEB-INF目录下,有一个web.xml文件。当服务端接收到一个 url时,web.xml负责把特定的url地址映射到特定的servlet类。我希望把/gapp_flexblog/hello请求映射到刚才添加的 类HelloWorldServlet上,我添加了如下内容:

<servlet>
    <servlet-name>helloWorld</servlet-name>
    <servlet-class>sban.flexblog.server.HelloWorldServlet</servlet-class>
  </servlet>
  …
  <servlet-mapping>
    <servlet-name>helloWorld</servlet-name>
    <url-pattern>/gapp_flexblog/hello</url-pattern>
  </servlet-mapping>

它可以帮我把/gapp_flexblog/hello请求交给HelloWorldServlet处理。

点击运行,eclipse打开Google Web Toolkit Hosted mode窗口,弹出浏览器运行窗口,表示服务端程序已经到位,客户端可以发出请求了。
google web tookit server

google-app-browser

如果你安装了curl,可以就可以用它测试服务端代码是否工作正常,打开cmd,输入:

curl http://localhost:8080/gapp_flexblog/hello?name=sban

curl hello

返回正确。我修改了客户端的静态的helloworld程序,如下:

<?xml version="1.0" encoding="utf-8"?>
<FxApplication xmlns="http://ns.adobe.com/mxml/2009">

 

        <Script>
                <![CDATA[
                        import mx.controls.Alert;
                        import flash.net.URLLoader;
                        import flash.net.URLRequest;
                        
                        private function greet() : void
                        {
                                new URLLoader( newURLRequest("http://localhost:8080/gapp_flexblog/hello?name=" + vNameTxt.text) )
                                        .addEventListener(Event.COMPLETE
                                                function(event : Event) : void
                                                {
                                                        Alert.show(event.currentTarget.data);
                                                        vSendBtn.enabled = true;
                                                }
                                        );
                                vSendBtn.enabled = false;
                        }
                ]]>
        </Script>
        
        <HGroup>
                <FxTextInput id="vNameTxt" text="sban" />
                <FxButton id="vSendBtn" label="greet" click="greet()" />
        </HGroup>

</FxApplication>

我用URLLoader及URLRequest向服务端发起一个http get请求,参数为name。我对URLLoader注册了一个事件监听,监测其complete事件,该事件发生在请求返回数据之后,而 URLloader的data属性便记录了返回结果。因为URLLoader在实例化如果有URLRequest,它会自动调用load方法,故而无须再 显式调用load方法。

input your name:

result

本课最终源码:source.zip

 

http://blog.sban.com.cn/

[SQL]数据库系统的维护与优化(二)

mikel阅读(1379)

二、数据库系统的性能监视与优化

1.、性能监视:

       1)增长

       测量并预测增长,需要收集四种主要的信息:处理器,网络,存储,内存。

      

对像类型

 

要收集的值

Processor

 

使用率百分比

Network

 

流量,总字节数

Storage

 

总传输数,以操作或块为单位

Memory

 

使用中的MB数或GB

Database

 

每个数据库的大小

 

       如果没有以前管理人员留下来的数据库性能基准线,则可以自己制做自己的数据库性能监视基准线。

       方法:

       以使用一个较小的时间间隔(5-10)进行一周,每天24小时,在之后24小时的测量改为每周一次,持续一个月,最后每月一次,并持续两个月。

有了这些数字之后,首先检查每天的范围。按照时间排列这些数字并为它们创建一个图表。这些数字将显示一种趋势,如果数字显示出一个增长趋势的

可预测模式,则要注意周测试的最后一次测量,并根据它预测后三个星期的增长模式。如果预测正确,它们应该与你实际采集的周数据接近。采用相同

的方法对月度数据进行测试。如果度量值初始显示出一个平稳的曲线,你应当进行更长时间的测试,每周一次,至少进行三个月以上。如果测量值呈下

降趋势,则延迟一周后再进行每天测量。

 

2、活动和性能

      

对像类型

 

要收集的值

Processor

 

使用率百分比,特别是应用程序相关的进程

Network

 

读和写的字节数

Storage

 

读和写操作

Memory

 

SQL server需要使用的值,以及SQL server正在使用的值

Database

 

数据库活动,连接和锁

       得到这些值之后,按时间顺序组织它们,计算出最小值,最大值和平均值。如果使得平均有意义,还要计算出集合的标准差,标准差越接近0,平均值越可信。

 

3、性能监视

对像

计数器

含义

说明

Processor

%Processor Time

显示在监视时间内处理器的使用百分比。

平均低于75%(低于50%更佳)

 

Memory

Available Mbytes

显示还剩多少内存。从总内存数量中减去这个值就可以算出正在使用的数量

应该保持在50MB

 

 

Page/sec 

 

平均低于20(低于15更佳)

 

LogicalDisk   

%Disk Time

显示在监视时间内的读,写百分比

 

Network Interface

Bytes Total/sec

显示发送和接收的字节数

用于规划网络带宽的大小

 

SQL Server:Databases

Active Transactions

       显示实例范围内数据库的所有活动事务

 

 

SQLServer:General      

User Connections      

显示被统计的连接到服务器的用户数量                        

用于规划内存

 

                                                                                                                                                                                                                                                                  

       我通常会使用系统监视器的日志功能,并把结果发送一个制表符分隔的文件。如果只监视一天,我会把收集时间间隔设置为5秒一次,如果要监视更长的时间

,则设置为5分钟一次。

       然后我创建了一个三张表的Excel文件:监视,评估,建议。我会把系统监视器的结果读到“监视”表中,然后更改列的格式,例如去掉服务器名称。对于处理

器内存,磁盘,网络和Sql server计数器,我会使用不同的颜色标记。

       在“评估”表中我会创建引用“监视”表中数据的公式,这些公式会计算出最大值,最小值,平均值,标准偏差等。然后我会对这些列中的数据进行评估并使用

不同的颜色标注,例如红色表示非常正常,绿色表示好,蓝色表示差。在这些数字下面我会解释为什么觉得它好或是不好,以及其他影响它的因素。我还会对测试环

境进行简单描述。

       在“建议”表中我会解释使用什么方法来解决问题。例如如果连接数以某种频率增长,并且同时内存使用量多于服务器上安装的数量,那么服务器正在进行换页

操作,那么它需要更多内存。

 

 

       磁盘增长方式

线性增长:

       未来使用率=当前使用率+(增长数量*周期个数)

       例如:如果数据库当前每分钟处理40个事务,并且每年每分钟增长10个事务,就可以通过这些数值放置到公式中计算出未来3年内数据库使用的情况

       未来3年内使用率=40+(10*3)

 

几何增长:

       未来使用率=当前使用率+(+增长率)^周期个数

       例如:如果数据库当前是600GB,并且增长率每个月为2%,则未来三年内数据库大小=600(1+0.02)^36

 

4、磁盘系统的优化

 

 

对于磁盘子系统规划监视

对像

计数器

含义

说明

System

Processor Queue      

每个处理器平均低于2

例如,在一个双处理器的机器上,应该保持在低于4的状态

Physical Disk

%Disk Time  

显示在监视时间内的读,写百分比   

 

平均低于50%

 

Avg.Disk QueueLength       

平均每个磁盘应该低于2

例如,对于一个5磁盘的阵列,此值应该低于10

 

      

Avg.Disk Reads/sec

用户规划磁盘和CPU

该低于磁盘容量的85%

 

 

vg.Disk Writes/sec

用户规划磁盘和CPU

应该低于磁盘容量的85%

SQL Server:Buffer Manager

Buffer Cache HitrATIO

应该超过90%(理想状态下接近99%          

 

      

Page Life Expectancy    

用于规划内存(应该保持在300秒以上)

                    

用于规划内存

 

 

Transactions/sec

用户规划磁盘和CPU       

 

 

 

Data Files Size KB

用于规划磁盘子系统

 

 

 

Percent Log Used

用于规划磁盘子系统

 

 

 

 

 

 

 

 假设:数据库的工作负载在峰值时段内为每秒600个读事务,200个写事务, 每个磁盘的标准是300IOPS及最大255 IOPS

 1.使用RAID1R的配置中,写事务的数量是双倍,因此它将此吞吐量的级别调整为600个读事务,400个写事务.或者1000 IOPS.如果这些I/O负载分摊到两个磁盘,

那么每个磁盘就是500IOPS,远远超出了每个磁盘的标准值.

所以RAID1不满足这个数据库的负载.

 

 2.RAID 5配置中,由于增加了4倍的写事务个数,所以提高了总体的I/O.由此I/O吞吐量级别上升到了每秒1400(600+200*4).不像RAID1,RAID5配置固定磁盘个数,

所以在RAID5 配置中,你可以使用多个磁盘来分担吞吐量,以符合每个磁盘最大255IOPS的负载.

       计算出磁盘数量:1400/255=5.49.由于5.49不是一个整数值,所以应该将其上升到下一个整数值,所以应该是6.

 3.RAID 10配置中,RAID1配置一样的写事务的数量翻一倍.

       计算出磁盘数量:1000/255=3.9 .所以最少要4个物理磁盘才能满足.

 

4.内存:

 并发是指300秒持续时间内的执行数量.

 1.连接上下文指的是需要支持用户连接的数据库结构.连接到sql server的每个用户大约需要500KB.确定并发用户的最大数量之后,500*最大并发连接数,就可以确定用户的内在需要.

 2.存储过程缓冲内存:

    1) 首先收集在数据库应用程序中包含所有的查询类型.然后计算每个查询文本在内在中需要的存储空间数量.请记住,执行此计算时,文本中每个字符都是一个字节.

 :

 如果一个特定的存储过程中包含了4000个字符,则此查询的文本在内存中大概占据4KB.

     2) 对每个查询评估最大的并发执行数量.

     :

       你可能估算出相同的存储过程的并发执行的最大的数量为500.则需要的缓存为4*501 (一个用于共享的查询计划,500用于执行上下文)2004KB.

 3.缓冲区内存🙁这个是最大的内存用户)

    :

       查询A:30个用户并发执行,每个查询有400KB数据输出, 120MB

       查询B:20个用户并发执行,每个查询有300KB数据输出,60MB

       查询C: 50个用户并发执行,每个查询有100KB数据输出,50MB

       三个种查询总共需要230MB内存,做为缓冲区.

[ASP.NET]提高ASP.net性能的十种方法

mikel阅读(687)

今天无意中看了一篇关于提高ASP.NET性能的文章,个人感觉还不可以,整理了一下,分享给大家。抛砖引玉,也希望高手们留下自己对提高性能上的高见!如果感觉有什么不对的观点还望见谅,多多指教。

 

一、返回多个数据集

  检查你的访问数据库的代码,看是否存在着要返回多次的请求。每次往返降低了你的应用程序的每秒能够响应请求的次数。通过在单个数据库请求中返回多个结果集,可以减少与数据库通信的时间,使你的系统具有扩展性,也可以减少数据库服务器响应请求的工作量。

  如果用动态的SQL语句来返回多个数据集,那用存储过程来替代动态的SQL语句会更好些。是否把业务逻辑写到存储过程中,这个有点争议。但是我认为,把业务逻辑写到存储过程里面可以限制返回结果集的大小,减小网络数据的流量,在逻辑层也不用在过滤数据,这是一个好事情。

  用SQLCommand对象的ExecuteReader方法返回一个强类型的业务对象,再调用NextResult方法来移动数据集指针来定位数据集。返回多个ArrayList强类型对象。只从数据库中返回你需要的数据可以大大的减小你的服务器所耗用的内存。

二、对数据进行分页

  ASP.NETDataGrid有一个非常有用的功能:分页。如果DataGrid允许分页,在某一时刻它只下载某一页的数据,另外,它有一个数据分页的济览导航栏,它让你可以选择浏览某一页,而且每次只下载一页的数据。

  但是它有一个小小的缺点,就是你必须把所有的数据都绑定到DataGrid中。也就是说,你的数据层必须返回所有的数据,然后DataGrid再根据当前页过滤出当前页所需要的数据显示出来。如果有一个一万条记录的结果集要用DataGrid进行分页,假设DataGrid每页只显示25条数据,那就意味着每次请求都有9975条数据都是要丢弃的。每次请求都要返回这么大的数据集,对应用程序的性能影响是非常大的。

  一个好的解决方案是写一个分页的存储过程,例如对Northwind数据库orders表的分页存储过程。你只需要传当前页码,每页显示的条数两个参数进来,存储过程会返回相应的结果。

  在服务器端,我专门写了一个分页的控件来处理数据的分页,在这里,我用了第一个方法,在一个存储过程里面返回了两个结果集:数据记录总数和要求的结果集。

  返回的记录总数取决于要执行查询,例如,一个where条件可以限制返回的结果集的大小。因为在分页界面中必须要根据数据集记录的大小来计算总的页数,所以必须要返回结果集的记录数。例如,如果一共有1000000条记录,如果用where条件就可以过滤成只返回1000条记录,存储过程的分页逻辑应该知道返回那些需要显示的数据。

三、连接池

  用TCP来连接你的应用程序与数据库是一件昂贵的事情(很费时的事情),微软的开发者可以通过用连接池来反复的使用数据库的连接。比起每次请求都用TCP来连一次数据库,连接池只有在不存在有效的连接时才新建一个TCP连接。当关闭一个连接的时候,它会被放到池中,它仍然会保持与数据库的连接,这样就可以减少与数据库的TCP连接次数。

  当然,你要注意那些忘记关的连接,你应在每次用完连接后马上关闭它。我要强调的是:无论什么人说.net framework中的GC(垃圾收集器)总会在你用完连接对象后调用连接对象的Close或者Dispose方法显式的关闭你的连接。不要期望CLR会在你想象的时间内关掉连接,虽然CLR最终都要销毁对象和关闭边接,但是我们并不能确定它到底会在什么时候做这些事情。  

  要用连接池优化,有两条规则,第一,打开连接,处理数据,然后关闭连接。如果 你必须在每次请求中多次打开或关闭连接,这好过一直打开一个边接,然后把它传到各个方法中。第二,用相同的连接字符串(或者用相同的用户标识,当你用集成 认证的时候)。如果你没有用相同的连接字符串,如你用基于登录用户的连接字符串,这将不能利用连接池的优化功能。如果你用的是集成的论证,因为用户很多, 所以你也不能充分利用连接池的优化功能。.NET CLR提供了一个数据性能计数器,它在我们需要跟踪程序性能特性的时候非常有用,当然也包括连接池的跟踪了。     

  无论你的应用程序什么时候要连在另一台机子的资源,如数据库,你都应该重点优化你连资源所花的时间,接收和发送数据的时间,以及往返回之间的次数。优化你的应用程序中的每一个处理点(process hop),它是提高你的应用的性能的出发点。  

  应用程序层包含与数据层连接,传送数据到相应的类的实例以及业务处理的逻辑。例如,在Community Server中,要组装一个Forums或者Threads集合,然后应用业务逻辑,如授权,更重要的,这里要完成缓存逻辑。

四、ASP.NET缓存API

   在写应用程序之前,你要做的第一件事是让应用程序最大化的利用ASP.NET的缓存功能。

  如果你的组件是要在Asp.net应用程序中运行,你只要把System.Web.dll引用到你的项目中就可以了。然后用HttpRuntime.Cache属性就可访问Cache了(也可以通过Page.CacheHttpContext.Cache访问)。  

  有以下几条缓存数据的规则。第一,数据可能会被频繁的被使用,这种数据可以缓存。第二,数据的访问频率非常高,或者一个数据的访问频率不高,但是它的生存周期很长,这样的数据最好也缓存起来。第三是一个常常被忽略的问题,有时候我们缓存了太多数据,通常在一台X86的机子上,如果你要缓存的数据超过800M的话,就会出现内存溢出的错误。所以说缓存是有限的。换名话说,你应该估计缓存集的大小,把缓存集的大小限制在10以内,否则它可能会出问题。在Asp.net中,如果缓存过大的话也会报内存溢出错误,特别是如果缓存大的DataSet对象的时候。

这里有几个你必须了解的重要的缓存机制。首先是缓存实现了“最近使用”原则( a least-recently-used algorithm),当缓存少的时候,它会自动的强制清除那些无用的缓存。其次 “条件依赖”强制清除原则(expiration dependencies),条件可以是时间,关键字和文件。以时间作为条件是最常用的。在asp.net2.0中增加一更强的条件,就是数据库条件。当数据库中的数据发生变化时,就会强制清除缓存  

五、预请求缓存

  在前面,我们只对某些地方作了一个小小的性能改进也可以获得大的性能提升,用预请求缓存来提升程序的性能是很不错的。

  虽然Cache API设计成用来保存某段时间的数据,而预请求缓存只是保存某个时期的某个请求的内容。如果某个请求的访问频率高,而且这个请求只需要提取,应用,修改或者更新数据一次。那么就可以预缓存该请求。我们举个例子来说明。

  在BS的论坛应用程序中,每一个页面的服务器控件都要求得到用于决定它的皮肤(skin)的自定义的数据,以决定用哪个样式表及其它的一些个性化的东西。这里面的某些数据可能要长时间的保存,有些时间则不然,如控件的skin数据,它只需要应用一次,而后就可以一直使用。

  要实现预请求缓存,用Asp.net HttpContext类,HttpContext类的实例在每一个请求中创建,在请求期间的任何地方都可以通过HttpContext.Current属性访问。HttpContext类有一个Items集合属性,在请求期间所有的对象和数据都被添加到这个集合中缓存起来。和你用Cache缓存访问频率高数据一样,你可以用HttpContext.Items缓存那些每个请求都要用到的基础数据。它背后的逻辑很简单:我们向HttpContext.Items中添加一个数据,然后再从它里面读出数据。

六、后台处理

  通过上面的方法你的应用程序应该运行得很快了,是不是?但是在某些时候,程序中的一次请求中可能要执行一个非常耗时的任务。如发送邮件或者是检查提交的数据的正确性等。

  当我们把asp.net Forums 1.0集成在CS中的时侯,发现提交一个新的帖子的时候会非常的慢。每次新增一个帖子的时侯,应用程序首先要检查这个帖子是不是重复提的,然后用“badword”过滤器来过滤,检查图片附加码,作帖子的索引,把它添加到合适的队列中,验证它的附件,最后,发邮件到它的订阅者邮件箱中。显然,这个工作量很大。

  结果是它把大量的时间都花在做索引和发送邮件中了。做帖子的索引是一项很耗时的操作,而发邮件给订阅都需要连接到SMTP服务,然后给每一个订阅者都发一封邮件,随着订阅用户的增加,发送邮件的时间会更长。

  索引和发邮件并不需要在每次请求时触发,理想状态下,我们想要批量的处理这些操作,每次只发25封邮件或者每隔5分钟把所有的要发的新邮件发一次。我们决定使用与数据库原型缓存一样的代码,但是失败了,所以又不得不回到VS.NET 2005

  我们在System.Threading命名空间下找到了Timer类,这个类非常有用,但却很少有人知道,Web开发人员则更少有人知道了。一旦他建了该类的实例,每隔一个指定的时间,Timer类就会从线程池中的一个线程中调用指定的回调函数。这意味着你的asp.net应用程序可以在没有请求的时候也可以运行。这就是后以处理的解决方案。你就可以让做索引和发邮件工作在后台运行,而不是在每次请求的时候必须执行。

  后台运行的技术有两个问题,第一是,当你的应用程序域卸载后,Timer类实例就会停止运行了。也就是不会调用回调方法了。另外,因为CLR的每个进程中都有许多的线程在运行,你将很难让Timer获得一个线程来执行它,或者能执行它,但会延时。Asp.net层要尽量少的使用这种技术,以减少进程中线程的数量,或者只让请求用一小部分的线程。当然如果你有大量的异步工作的话,那就只能用它了。

七、页面输出缓存和代理服务

  Asp.net是你的界面层(或者说应该是),它包含页面,用户控件,服务器控件(HttpHandlers HttpModules)以及它们生成的内容。如果你有一个Asp.net页面用来输出htmlxml,imgae或者是其它的数据,对每一个请求你都用代码来生成相同的输出内容,你就很有必要考虑用页面输出缓存了。

只要简单的把下面的这一行代码复制到你的页面中就可以实现了:

<%@ PageOutputCache VaryByParams=none Duration=60 %>

就可以有效的利用第一次请求里生成的页面输出缓存内容,60秒后重新生成一道页面内容。这种技术其实也是运用一些低层的Cache API来实现。用页面输出缓存有几个参数可以配置,如上面所说的VaryByParams参数,该参数表示什么时候触发重输出的条件,也可以指定在Http GetHttp Post 请求模式下缓存输出。例如当我们设置该参数为VaryByParams=Report”的时候,default.aspx?Report=1或者default.aspx?Report=2请求的输出都会被缓存起来。参数的值可以是多个用分号隔开参数。

  许多人都没有意识到当用页面输出缓存的时候,asp.net也会生成HTTP头集(HTTP Header)保存在下游的缓存服务器中,这些信息可以用于Microsoft Internet安全性中以及加速服务器的响应速度。当HTTP缓存的头被重置时,请求的内容会被缓在网络资源中,当客户端再次请求该内容时,就不会再从源服务器上获得内容了,而直接从缓存中获得内容。

  虽然用页面输出缓存不提高你的应用程序性能,但是它能减少了从的服务器中加载已缓存页面内容的次数。当然,这仅限于缓存匿名用户可以访问的页面。因为一旦页面被缓存后,就不能再执行授权操作了。

八、 IIS6.0Kernel Caching

  如果你的应用程序没用运行在IIS6.0(windows server 2003)中,那么你就失去了一些很好的提高应用程序性能的方法。在第七个方法中,我讲了用页面输出缓存提高应用程序的性能的方法。在IIS5.0中,当一个请求到来到IIS后,IIS会把它转给asp.net,当应用了页面输出缓存时,ASP.NET中的HttpHandler会接到该请求,HttpHandler从缓存中把内容取出来并返回。

  如果你用的是IIS6.0,它有一个非常好的功能就是Kernel Caching,而且你不必修改asp.net程序中任何代码。当asp.net接到一个已缓存的请求,IISKernel Cache会从缓存中得到它的一份拷贝。当从网络中传来一个请求的时,Kernel层会得到该请求,如果该请求被缓存起来了,就直接把缓存的数据返回,这样就完工了。这就意味着当你用IISKernel Caching来缓存页面输出时,你将获得不可置信的性能提升。在开发VS.NET 2005 asp.net时有一点,我是专门负asp.net性能的程序经理,我的程序员用了这个方法,我看了所有日报表数据,发现用kernel model caching的结果总是最快的。它们的一个共同的特征就是网络的请求和响应量很大,IIS只占用了5%CPU资源。这是令人惊奇的。有许多让你使用用IIS6.0的理由,但kernel cashing是最好的一个。

九、 Gzip压缩数据

  除非你的CPU占用率太高了,才有必要用提升服务器性能的技巧。用gzip压缩数据的方法可以减少你发送到服务端的数据量,也可以提高页面的运行速度,同时也减少了网络的流量。怎么样更好的压缩数据取决于你要发送的数据,还有就是客户端的浏览器支不支持(IIS把用gzip压缩后的数据发送到客户端,客户端要支持gzip才能解析,IE6.0Firefox都支持)。这样你的服务器每秒能多响应一些请求,同样,你也减少了发送响应的数据量,也就能多发送一些请求了。

  好消息,gzip压缩已经被集成在IIS6.0中了,它比IIS5.0gzip更好。不幸的是,在IIS6.0中启用gzip压缩,你不能在IIS6.0的属性对话中设置。IIS开发团队把gzip压缩功能开发出来了,但他们却忘了在管理员窗口中让管理员能很方便的启用它。要启用gzip压缩,你只能深入IIS6.0xml配置文件中修改它的配置。

  除了阅读本文以外看看Brad Wilson写的 IIS6 压缩一文:http://www.dotnetdevs.com/articles/IIS6compression.aspx;另外还有一篇介绍aspx压缩基础知识的文章,Enable ASPX Compression in IIS。但是要注意,在IIS6中动态压缩和kernel cashing是互斥的。

十、 服务器控件的ViewState

  ViewStateasp.net中的一个特性,它用于把生成页面要用的一状态值保存在一个隐藏域中。当页面被回传到服务器时,服务器要解析,校验和应用ViewState中的数据以还原页面的控件树。ViewState是一个非常有用的特性,它能持久化客户端的状态而不用cookie或者服务器的内存。大部分的服务器控件都是用ViewState来持久化那些在页面中与用户交互的元素的状态值。例如,用以保存用于分页的当前页的页码。

  用ViewState会带来一些负面的影响。首先,它加大的服务器的响应和请求的时间。其次,每次回传时都增加了序列化和反序列化数据的时间。最后,它还消耗了服务器更多的内存。

  许多的服务器控件很趋于使用ViewState,如DataGrid,而有时候是没有必须使用的。默认情况下是允许使用ViewState的,如果你不想使用ViewState的话,你可以在控件或页面级别把关闭它。在控件中,你只要把EnableViewState属性设为False就可以了;你也可以在页面中设置,使它的范围扩展到整个页面中: <%@ Page EnableViewState=false %> 如果页面无需回传或者每次请求页面只是呈现控件。你就应该在页面级别中把ViewState关掉。

[Flex]第一课:flex4开发环境配置与第一个应用程序hello world

mikel阅读(791)

第一课:flex4开发环境配置与第一个应用程序hello world

我原本是学教育学的,因为对flex技术略感兴趣,所以业余就想研究一下。我认为学习flex技术,最好的方法莫过于阅读官方文档,其次便是做项目。在我最初学习的时候,我在想,如果有一个人能够把他的学习的过程晒出来,大家沿着他的学习轨迹学习,一定会比较好学一点。

对 于初学者,学习内容若太难了,一下子难于理解。太容易了,又丧失了学习的兴趣与动力。CookBook居说很好,例子很丰富,但CookBook只是单个 例子的集合,针对单个例子没有问题,但正因为如此,才容易给初学者产生误导。写程序,不只是解决问题,对于一个项目,整体的架构与设计很重要,而这些在 CookBook中是看不到的。CookBook有时候为了说明一个问题,并不讲究编码方法与规范,这也往往误导初学者,让初学者以为,这样写就是对的, 就是最好的,其实不然。教程一方面可以帮助初学者理解问题,也可能是在误导。

如果不看CookBook,而读官方livedoc文档,又录如何?也有问题,因为大多了,往往不知从何看起。往往看过后面的,就忘记前面的。昨天 看了一个类的用法,今天再看,觉得还是有一些不明白。对于Flex SDK,它有一个整体的架构,它为什么这么设计,对于一个组件或一个类,它为什么要有这个属性,为什么要有这个方法?这在livedoc中是没有办法直接 学不到的。所以从livedoc学习,是枯燥的,是不高效的,就好像想通过背诵字典来提高文化素养一样。

开源程序员喜欢写一些开源程序open给大家,供大家免费使用,以及学习交流。阅读开源代码,也是学习flex技术的一个好途径。但是,越是完善的 开源框架,越难看得明白。一个开源程序,只所以会如此如此设计,肯定有它的历史演化原因,这些原因i湮没在历史的版本中,很难从最终版本中窥斑见豹。而如 果从svn中究极它的所有版本,再一一消化细细揣磨加以对照,这种精神也不是我等凡人所能具备的。网上有人会对一些开源程序的框架写一些分析评论,但恨往 往只是只言片言,很少有人有耐心把它从头到脚剖析完备的。所以阅读开源代码只能做为学习flex技术的备选项,而非上上策。

看书居说是很不错的选择,但可惜国内目前关于flex方面的好书还是不多,抛去翻译国外的,为赚稿费搪瑟字数的,照搬livedoc的,就廖廖无几 了。当然如果你的英语够好,不妨去读英文原著。最后我便想到第一段中提到的学习方法,即做项目,迭代式的做项目,从一点一滴做起,从零做起,在做的过程中 发现问题,解决问题。所思所想涉及项目的架构与设计,设计模式,与服务端的交互等。我不是flex高手,是一名地地道道的flex初学者,爱好者,我将用 我诚实的笔忠诚的记录我的详细学习过程,希望对读者有益。

我将用flex4技术,以及相关的技术或软件如Flash CS4等,开发一个flex博客系统,它具有常用的博客的一些功能。今天是第一天,首选配置我flex4开发环境,以及编写第一个应用程序hello world。

一, 需要安装的软件

jdk 1.6eclipse 3.4.2Adobe Flex Builder 3.0.2 Professional Eclipse Plug-in,在eclipse中安装jdt及Google Plugin for Eclipse

上述软件的安装比较简单,不在废话。之所以要使用eclipse作为开发环境,是因为同时编写java与flex代码方便,因为我想用java做为 server端的开发语言。安装Google Plugin for Eclipse,是因为我要使用免费的Google App Engine作为web测试服务器。

因为我要使用flex4技术,所以我下载了一个Max Preview版本,即4.0.0.4021版本,安装配置不再详言。

打开Eclipse,新建一个Web Application Project项目,名为gapp_flexblog,默认命名空间为sban.flexblog,用于写server代码。再新建一个Flex Project,名为gapp_flexblog_client,用于编写客户端逻辑。

撰写我的第一个flex4页面:

<?xml version="1.0" encoding="utf-8"?>
<FxApplication xmlns="http://ns.adobe.com/mxml/2009">

 

        <TextGraphic text="hello, world!" />
        
</FxApplication>

flex4的默认命名空间为http://ns.adobe.com/mxml/2009,且默认没有缀,为空,这样编写反而更方便了一些。ok,运行一下,没有问题:
hello world

我创建的Google App Engine app id为flex-blog,站点为http://flex-blog.appspot.com/。首先我把默认创建的Web Applicaton Project布置到Google App Engine。布置方法很简要,点击工具栏中的Deploy按纽。布署完成之后,查看http://flex-blog.appspot.com,没有问 题:
default deploy page

下一步要做的事件是让hello world有动态内容:
1,把flex发布的代码布署到app engine上去
2,flex从server端获取简要数据,实现数据交互

本课最终源码:lesson1.zip

 

http://blog.sban.com.cn/

[安全]Web安全实践(14)嗅探,arp欺骗,会话劫持与重放攻击(下)

mikel阅读(826)

本系列导航http://www.cnblogs.com/xuanhun/archive/2008/10/25/1319523.html

安全技术区http://space.cnblogs.com/group/group_detail.aspx?gid=100566 

前言

QQ群:77986379,我的QQ717532978. 博客园内的请加入技术安全区。

距离上篇文章已经很长时间了,但是不能留下这个缺憾,所以今天抽时间写了点内容。还是希望更多的高手和朋友参与进web安全的讨论中来。这篇文章重点放在重放攻击的原理解说上。

正文

14.1 什么是重放攻击

所谓重放攻击就是攻击者发送一个目的主机已接收过的包,来达到欺骗系统的目的,主要用于身份认证过程。
攻击者利用网络监听或者其他方式盗取认证凭据,之后再把它重新发给认证服务器。从这个解释上理解,密可以有效防止会话劫持,但是却防止不了重放攻击。重放攻击任何网络通讯过程中都可能发生。

14.2重放攻击的类型

 

l

据消息的来源:
协议轮内攻击:一个协议轮内消息重放
协议轮外攻击:一个协议不同轮次消息重放
根据消息的去向:
偏转攻击:改变消息的去向
直接攻击:将消息发送给意定接收方
其中偏转攻击分为:
反射攻击:将消息返回给发送者
第三方攻击:将消息发给协议合法通信双方之外的任一方

 

典型例子

 

 

14.3重放攻击与cookie

我们监听http数据传输的截获的敏感数据大多数就是存放在cookie中的数据。其实在web安全中的通过其他方式(非网络监听)盗取cookie与提交cookie也是一种重放攻击。我们有时候可以轻松的复制别人的cookie直接获得相应的权限。关于cookie,我想应该用单独的一篇文章来说明,在这里就不重复了。

14.4重放攻击的防御方案

(1)时间戳

“时戳”──代表当前时刻的数
基本思想──A接收一个消息当且仅当其包含一个对A而言足够接近当前时刻的时戳
原理──重放的时戳将相对远离当前时刻
时钟要求──通信各方的计算机时钟保持同步
处理方式──设置大小适当的时间窗(间隔),越大越能包容网络传输延时,越小越能防重放攻击
适用性──用于非连接性的对话 (在连接情形下双方时钟若偶然出现不同步,则正确的信息可能会被误判为重放信息而丢弃,而错误的重放信息可能会当作最新信息而接收)

(2)序号

通信双方通过消息中的序列号来判断消息的新鲜性
要求通信双方必须事先协商一个初始序列号,并协商递增方法

(3)提问——应答

“现时”──与当前事件有关的一次性随机数N(互不重复即可)
基本做法──期望从B获得消息的A 事先发给B一个现时N,并要求B应答的消息中包含N或f(N),f是A、B预先约定的简单函数
原理──A通过B回复的N或f(N)与自己发出是否一致来判定本次消息是不是重放的
时钟要求──无
适用性──用于连接性的对话

14.5重放攻击演示

本来是做了个演示的,但受到了相关人士的阻拦,这里就不发布了。如果你有相关的实例,可以贡献出来做个补充。

真的佩服博客园里常写教程的高手哦,我真是力不从心,呵呵。

[C#]Web开发学习心得5——Asp.net的设计思想

mikel阅读(959)

自从有了html与http,就有了浏览器与Web服务器,并有了Web应用,最初的交互模式是这样的:

      该模式很好地运行了很多年。然而,随着计算机应用的发展,人们越来越不满足于只有静态内容的页面,而由某种机制动态产生html等代码的需求越来越迫切,于是,很多技术就应运而生,ASP.NET就是这样一种技术。从本质上讲,ASP.NET就是一种服务器端动态产生html、css、JavaScript等浏览器认识的代码的技术。ASP.NET的交互模式如下:

 

      由该图可知,Asp.net必须解决两大问题,一是如何与Web服务器(一般就是指IIS)进行交互,二是如何根据不同请求产生不同的html等代码。

 

      第一个问题,根据IIS版本(5.*,6.0,7.0)的不同,Asp.net具有不同的进程模式与不同的交互模式,该问题不是本篇要 讲述的。一般来说,大家不必关心该问题,而且要了解该问题,又必须清楚IIS各个版本的模型,而各个版本又各有各的不同,因此,我基本不准备讲述这个问 题,大家有兴趣的话,可以自行搜索相关资料。

      我们来讨论第二个问题,这里首先要说明的是,因为IIS7.0进程模式的变化比较大,我也没去了解IIS7.0的模型,因此,以下讲述 及以后讲述将只针对IIS5.*与IIS6.0。我们有理由认为,针对IIS5.*与IIS6.0的讲述一般同样适用于IIS7.0。

      先且按下该问题不表,我们来看一段请求玉帝把大象放到冰箱里的代码(为什么不是上帝?因为我中华不归上帝管),请大家先跟着我的思路来,别急,别急。

Code1

 

      请大家先认真读懂该代码,该代码不会有问题吧,如果有问题,那么还请你一定先读懂该代码再往下看。

      后来,玉帝累了,他想啊,我堂堂玉帝,居然亲自干这种没含量的活,但是顾客是玉帝的玉帝,又不能不干。于是,玉帝找了两个手下(把门大神、神象星宿)来帮他干活。

Code2

 

      观测该代码有哪些变化?玉帝增加了一个Init方法,该方法召集了那些干活的手下,并交待手下你们要干的事情(初始化)。所有手下都继 承自IEmperorModule接口,该接口有一个Init方法,以初始化该手下能干的活。玉帝的ProcessRequest方法不再亲自干活了,而 仅仅是在那边喊,该开门了,该把大象放进去了,该关门了。

      (来点题外话,上面代码应用了模板方法模式,或者说是模板方法的变种。什么,不是,没有抽象类,没有抽象方法,没有子类?请问,模板方 法一定要有抽象类吗?一定要有抽象方法吗?一定要有子类吗?我想,模板方法的精髓,在于它规定了特定的步骤算法,而这些步骤在必要的时候可以委托给其它方 法实现。在传统的模板方法中,是委托给子类实现,而在我们拥有委托的情况下,难道我们不能委托给其它类实现吗?其实,我想,学习设计模式,不要拘泥于形 式,而要掌握其精髓,并灵活运用。就Gof的23种设计模式来说,我觉得其精髓几乎全部可由一句话概括。)

      天有不测风云,人有旦夕祸福,某一天,这神象星宿病了,他赶不动超过1000斤的大象了,怎么办?玉帝于是又召集了超级神象星宿,可但凡有点能耐的都有点脾气,超级神象星宿表示,只有其它人赶不动的大象他才来赶。

Code3

      仔细琢磨该代码,请问你发现了什么?如果你发现的是灵活的可扩展性和可配置性,以及各手下之间的交互,那么恭喜你,你悟性很高。如果你 还发现了职责链模式,那么恭喜你,你悟性非常高。(该代码中两个神象星宿的交互完全可以通过在Context中增加一个bool值属性来实现,之所有这里 用Items字典,是为了演示如果你不具有修改Context的源代码的时候,该如何处理。)

     好了,Asp.net的设计思想讲完了。什么,还没开始?你没发现我们的Program就是HttpRuntime、Emperor就是 HttpApplication、Context就是HttpContext、IEmperorModule就是IHttpModule吗?什么,没发 现?那么,让我们来看看Asp.net的源代码吧。(本想把整个方法的源代码都贴上来,但是显得过于冗长,因此删掉非关键代码,并以//……省略……代 替。)

 

      如此,你明白了吗?

[新闻]Google公开测试Analytics API,桌面版Google Analytics即将成

mikel阅读(899)

在经过长达一年的非公开测试之后,Google 终于公开 Google Analytics API 的测试版。去年10月以来,随着 Google Analytics 众多重要升级的 进行,Google 曾透露他们即将推出该服务的 API。Google Analytics API 使开发者可以在自己的客户端访问 Google Analytics 数据,并基于这些数据设计自己的流量分析报告,同时桌面版 Google Analytics 也将成为现实。

Analytics API 是 Google 的数据 API,和 Google Calendar,Finance 以及 Webmaster 工具属于同一种 API。对于 JavaScript 以及 Java 编程语言,Google 提供一些客户库作为辅助,他们还会提供更多编程语言的支持,目前,不管你使用何种编程语言,你可以使用 HTTP 请求获取 XML 格式的数据。


Polaris 是率先使用该 API 创建的客户端工具之一,它由 Desktop Reporting 创建,Desktop Reporting 旨在将 Google Analytics 服务转向桌面。其完整套件,一个基于 Adobe AIR 的叫 Dopac 的程序,还没有完全发布。不过 Polaris 已经实现将部分报告数据通过跨平台的 widget 搬到桌面。目前有8种报告,这个程序对第一个站点是免费的,它的目标市场是那些希望从桌面对多个站点的流量进行追踪的人。
该工具的开发者是来自比利时的 Nicolas Lierman,他一直尝试将 Google Analytics 转移到桌面,除了前面提到的工具,他还有两个其它产品等待发布。另外一个值得一看的是 Actual Metrics 的 Android 版 Google Analytics


Google 还提供了一些示例,另外,在 Google Analytics API Notify email group 邮件组还可以获取关于该 API 的最新进展。
本文来源: Google Analytics API Now In Public Beta, Desktop Reporting Takes Stats Offline
中文翻译来源:COMSHARP CMS 官方网站

[C#]C#类和接口、虚方法和抽象方法及值类型和引用类型的区别

mikel阅读(842)

1.C#类和接口的区别
接口是负责功能的定义,项目中通过接口来规范类,操作类以及抽象类的概念!
而类是负责功能的具体实现!
在类中也有抽象类的定义,抽象类与接口的区别在于:
抽象类是一个不完全的类,类里面有抽象的方法,属性,也可以有具体的方法和属性,需要进一步的专业化。
但接口是一个行为的规范,里面的所有东西都是抽象的!
一个类只可以继承一个基类也就是父类,但可以实现多个接口
PS: 接口除了规范一个行为之外,在具体项目中的实际作用也是十分重要的,在面向对象的设计原则以及设计模式的使用中,无不体现作为一个接口的使用好处,最直接 的就是设计原则中OCP(开放封闭原则),我们使用接口,而不需要关心他的具体实现,具体实现的细节变化也无关客户端(使用接口的类)的使用,对与扩展是 开放的,我们可以另写一个接口的实现来扩展当前程序,而不影响上层的使用,但对修改是封闭的,即我们不能够再去修改接口的定义,当然这个“不能够”是指在 规范原则上不应该这么做!  
2.抽象类和接口的区别
答:
抽象类(abstract class)可以包含功能定义和实现,接口(interface)只能包含功能定义
抽象类是从一系列相关对象中抽象出来的概念, 因此反映的是事物的内部共性;接口是为了满足外部调用而定义的一个功能约定, 因此反映的是事物的外部特性
分析对象,提炼内部共性形成抽象类,用以表示对象本质,即“是什么”
为外部提供调用或功能需要扩充时优先使用接口
3. C#语言中,值类型和引用类型有何不同?
解答
  值类型和引用类型的区别在于,值类型的变量直接存放实际的数据,而引用类型的变量存放的则是数据的地址,即对象的引用。
   值类型变量直接把变量的值保存在堆栈中,引用类型的变量把实际数据的地址保存在堆栈中,而实际数据则保存在堆中。注意,堆和堆栈是两个不同的概念,在内 存中的存储位置也不相同,堆一般用于存储可变长度的数据,如字符串类型;而堆栈则用于存储固定长度的数据,如整型类型的数据int(每个int变量占用四 个字节)。由数据存储的位置可以得知,当把一个值变量赋给另一个值变量时,会在堆栈中保存两个完全相同的值;而把一个引用变量赋给另一个引用变量,则会在 堆栈中保存对同一个堆位置的两个引用,即在堆栈中保存的是同一个堆的地址。在进行数据操作时,对于值类型,由于每个变量都有自己的值,因此对一个变量的操 作不会影响到其它变量;对于引用类型的变量,对一个变量的数据进行操作就是对这个变量在堆中的数据进行操作,如果两个引用类型的变量引用同一个对象,实际 含义就是它们在堆栈中保存的堆的地址相同,因此对一个变量的操作就会影响到引用同一个对象的另一个变量。
4.结构和类的区别
解答
  1) 结构是一个值类型,保存在栈上,而类是一个引用类型,保存在受管制的堆上。
  2) 对结构中的数据进行操作比对类或对象中的数据进行操作速度要快。
  3) 一般用结构存储多种类型的数据,当创建一个很多类或对象共用的小型对象时,使用结构效率更高。
4.抽象方法和虚方法的区别
抽象方法
使用abstract关键字 public abstract bool Withdraw(…);
抽象方法是必须被派生类覆写的方法。
抽象方法是可以看成是没有实现体的虚方法
如果类中包含抽象方法,那么类就必须定义为抽象类,不论是否还包含其它一般方法
虚方法
使用virtual关键字 public virtual bool Withdraw(…);
调用虚方法,运行时将确定调用对象是什么类的实例,并调用适当的覆写的方法。
虚方法可以有实现体
———————————————————————————————

虚拟方法和抽象方法有什么区别?
抽象方法只有声明没有实现,需要在子类中实现;虚拟方法有声明和实现,并且可以在子类中覆盖,也可以不覆盖使用父类的默认实现
虚拟方法有实现代码
抽象方法则没有,
并且抽象类不能被实例化,只能实例化实现了全部抽象方法的派生类
抽象方法是虚拟方法的一种
抽象方法没有实现,它的存在只是为派生类统一接口;派生类应该实现这个方法
如果编写一个基类,它永远不会被实现,那么就应该将这个类中的一个或多个方法定义为
抽象方法。
抽象方法只有声明没有实现,需要在子类中实现;虚拟方法有声明和实现,并且可以在子类中覆盖,也可以不覆盖使用父类的默认实现
补充一点
只允许在抽象类中使用抽象方法声明
学习
其 实大家首先要搞清楚,虚方法与多态性关系密切,虚方法允许派生类完全或部分重写该类的方法,需写方法体。抽象类中可以包含抽象方法与一般的方法,抽象类不 可以new,抽象方法只是一个定义,没有方法体,也就是没有{},也不要在里面写内容。它们两个相像的一点是都用override重写

[SQL]Microsoft SQL Server 2005 中的 XML 支持

mikel阅读(934)

Microsoft SQL Server 2005 中的 XML 支持

发布日期 : 6/22/2004 | 更新日期 : 6/22/2004

Shankar Pal、Mark Fussell 和 Irwin Dolobowsky

Microsoft Corporation

适用于:Microsoft SQL Server

摘要:本文探讨 SQL Server 2005 中内置的 XML 支持。描述了这种支持如何与 .NET 框架 V2.0 和本机代码(例如 OLEDB 和 SQLXML)均支持的客户端编程相集成。

本页内容

简介
推动 XML 存储方案
SQL Server 2005 中的服务器端 XML 处理
SQL Server 2005 中的客户端 XML 处理
小节

简介

可扩展标记语言 (XML) 作为一种与平台无关的数据表示形式已被广泛采用。它对于在松散耦合且完全不同的系统,以及各种企业到企业 (B2B) 应用和工作流范畴内交换信息是很有用的。数据交换已成为 XML 技术的主要驱动力之一。.

XML 在企业应用程序中的使用正日益广泛,它主要用于对半结构化和非结构化数据进行建模。文档管理就是这样的一种应用程序。像电子邮件这样的文档是半结构化性质 的。如果文档以 XML 的形式存储在数据库服务器中,就可以开发功能强大的应用程序来根据文档内容检索文档、查询部分内容(例如查找标题包含单词“背景”的部分),以及查询文档 聚合。如果存在能够生成和使用 XML 的应用程序,则这样的方案就变得可行了。例如,Microsoft Office 2003 系统允许用户以 XML 标记的形式生成 Word、Excel、Visio 和 Infopath 文档。

为什么使用关系数据库来存储 XML 数据?

  • 将 XML 数据存储在关系数据库中会给数据管理和查询处理带来好处。SQL Server 提供了强大的查询和修改关系数据的能力,而且已经扩展到查询和修改 XML 数据。这使得可以利用在过去的版本上所进行的投资,就如同基于成本的优化和数据存储领域一样。例如,关系数据库中的索引技术已经广为人知,而且已经扩展到 用于索引 XML 数据,这样就可以使用基于成本的决策来优化查询。

  • XML 数据可以与现有的关系数据和 SQL 应用程序进行互操作,这样就可以在需要进行数据建模而又不破坏现有的应用程序的系统中引入 XML。数据库服务器还提供了管理功能来管理 XML 数据(例如备份、恢复和复制)。

  • 这些功能促进了对 SQL Server 2005 中的原生 XML 支持的需求,从而解决了不断增加的 XML 使用的问题。SQL Server 2005 中的 XML 支持将给企业应用程序开发带来好处。

  • 下面几部分将概述 SQL Server 2000 和 2005 中的 XML 支持,描述一些推动 XML 使用的方案,并且详细讨论服务器端和客户端的 XML 功能集。

SQL Server 2000 中的 XML 支持

这一部分简要概述了 SQL Server 2000 中的 XML 支持,以及随后发布的 SQLXML 客户端编程平台 Web 版,它提供了丰富的支持来将关系数据映射到 XML 数据或将 XML 数据映射到关系数据。

服务器端支持

在服务器上,XML 数据可以从表生成,并通过在 Select 语句中使用 FOR XML 子句来查询结果。这对于数据交换和 Web 服务应用程序是很理想的。FOR XML 的逆函数是一个名为 OpenXML 的关系行集合生成器函数;它通过求 XPath 1.0 表达式的值来从 XML 数据提取值,并将其放到行集合的列中。应用程序使用 OpenXML 来“切碎”传入 XML 数据,并将其存放到表中,或者用于通过 T-SQL 语言进行的查询。

客户端支持

SQL Server 2000 对客户端编程的支持称为 SQLXML。 这项技术的核心是 XML 视图,它是 XML 架构和关系表之间的双向映射。SQL Server 2000 只支持 XDR 架构的映射,尽管在后续的 Web 版中增加了对 XSD 的支持。XML 视图允许使用 XPath 1.0 的一个子集来进行查询,其中,可以使用映射将路径表达式转换成底层表中的 SQL 查询,并且将查询结果打包成 XML 结果。

SQLXML 还支持您创建 XML 模板,这使得您可以创建带有动态部分的 XML 文档。在 XML 文档中,您可以嵌入 FOR XML 查询和/或映射查询之上的 XPath 1.0 表达式。在执行 XML 模板时,可以用查询的结果来替换查询块。通过这种方式,您可以创建带有某些静态内容和某些数据驱动的动态内容的 XML 文档。

在SQL Server 2000 中,有两种访问 SQLXML 功能的主要方法:

  • SQLXMLOLEDB Provider。SQLXMLOLEDB Provider 是一个 OLE DB 提供程序,它通过 ADO 公开 Microsoft SQLXML 功能。

  • HTTP 访问。SQL Server 2000 中的 SQLXML 功能也可以使用 SQLXML ISAPI 过滤器通过 HTTP 进行访问。通过使用我们的配置工具,您可以建立网站来检索传入请求,从而通过 HTTP 执行 XML 模板、XML 视图之上的 FOR XML 和 XPath 1.0 语句。

XML 支持的局限性

服务器和客户端编程平台为基于表格和 XML 数据之间的映射生成和使用 XML 数据提供了丰富的支持。这能够相当好地处理结构化 XML 数据。在 SQLXML 中,查询语言是 XPath 1.0 的一个子集,并且有一些局限性。例如,不支持 descendant-or-self 轴 (//)。因此,在开发某些解决方案时会存在一定的限制。例如,不保存 XML 文档顺序,而这对于像文档管理这样的应用程序来说是非常关键的。此外,还不支持递归的 XML 架构。尽管存在这样一些局限性,但是客户端 SQLXML 和服务器 XML 功能还是在应用程序开发中得到了广泛的使用。SQL Server 2005 解决了许多这样的限制,扩展了关系 XML 交换功能,并且还提供了原生 XML 支持。

SQL Server 2005 中的 XML 支持概述

这一部分简要概述了 SQL Server 2005 中增加的新的 XML 支持,它是通过.NET 框架 V2.0 中的支持和本机客户端数据访问(如 OLE DB)进行补充的。

XML 数据类型

XML 数据模型具有一些特性,这些特性使得映射到关系数据模型非常困难,如果不是完全不可能的话。XML 数据具有可以递归的层次结构;关系数据库提供对层次数据(建模为外键关系)的弱支持。文档顺序是 XML 实例的固有属性,并且必须保存在查询结果中。这与关系数据形成了对比,关系数据是无序的,必须通过附加的排序列来强制进行排序。在查询时重新组合结果是很 费力的,因为实际的 XML 架构将 XML 数据分解到大量的表中。

SQL Server 2005 引入了一种称为 XML 的本机数据类型。用户可以创建这样的表,它在关系列之外还有一个或多个 XML 类型的列;此外,还允许带有变量和参数。为了更好地支持 XML 模型特征(例如文档顺序和递归结构),XML 值以内部格式存储为大型二进制对象 (BLOB)。

SQL Server 2005 提供了 XML 架构集合,可以将其作为一种方法来以元数据的形式管理 W3C XML 架构。XML 数据类型可以与 XML 架构集合相关联,以便对 XML 实例强加架构限制。当 XML 数据与 XML 架构集合相关联时,它称为类型化的 XML;否则,就称为非类型化的 XML。在一个框架中可以同时容纳类型化的 XML 和非类型化的 XML,保留 XML 数据模型,并且强制采用 XML 语义进行查询处理。底层关系基础结构被广泛用于这一目的。它支持关系数据和 XML 数据之间的互操作,这为更广泛地采用 XML 功能开辟了道路。

XML 数据类型查询和数据修改

可以使用 T-SQL Select 语句来检索 XML 实例。在 XML 数据类型中提供了五种内置的方法来查询和修改 XML 实例。

XML 数据类型的方法接受 XQuery,它是一种新出现的 W3C 标准语言(目前处于 Last Call(最后请求)状态),并且包括导航语言 XPath 2.0。也可以使用一种语言来修改 XML 数据,比如添加或删除子树和更新标量值。与一大组函数一起,嵌入式的 XQuery 和数据修改语言为处理 XML 数据提供了丰富的支持。

XQuery 类型系统与 W3C XML 架构类型是一致的。大多数 SQL 类型与 XQuery 类型系统是兼容的(例如,小数)。少数类型(例如,xs:duration)是以内部格式存储的,并且可以通过适当的解释来与 XQuery 类型系统兼容。

编译阶段检查 XQuery 表达式和数据修改语句的静态类型的正确性,并且在类型化 XML 的情况下使用 XML 架构来进行类型推理。如果表达式由于类型安全冲突而在运行时失败,则会产生静态类型错误。

XML 索引

查询执行在运行时处理每个 XML 实例;如果 XML 值比较大或需要在表的许多行中对查询进行求值,则查询执行就会变得非常费时。因此提供了一种索引 XML 列的机制来加速查询。

B+ 树广泛用于建立关系数据的索引。XML 列的主 XML 索引在该列中 XML 实例的所有标记、值和路径上都创建一个 B+ 树索引。通过这种方式,可以有效地对 XML 数据中的查询进行求值,并且在保留文档顺序和文档结构的同时从 B+ 树重新组合 XML 结果。

可以在 XML 列中创建次 XML 索引来加速不同类别的常见查询:PATH 索引(用于基于路径的查询)、PROPERTY 索引(用于属性包的情况)和 VALUE 索引(用于基于值的查询)。

XML 架构处理

XML 列、变量和参数可以根据 XML 架构的集合(它可能与另一个集合相关(例如,通过使用 )或不相关)有选择地进行类型化。每个类型化的 XML 实例都从它遵循的 XML 架构集合指定目标命名空间。数据库引擎在数据分配和修改时根据 XML 架构来验证实例的有效性。

XML 架构信息用于存储和查询优化。类型化的 XML 实例在内部的二进制表示中包含类型化的值,这与 XML 索引中的一样。通过这种方式,可以有效地处理类型化 XML 数据。

关系数据和 XML 数据的集成

用户可以在同一个数据库中存储关系数据和 XML 数据。简而言之,数据库引擎除了知道如何遵循关系数据模型之外,还知道如何遵循 XML 数据模型。在升级到 SQL Server 2005 之后,关系数据和 SQL 应用程序仍然能够正常工作。在服务器上,可以将驻留在文件和文本或图像列中的 XML 数据移到 XML 数据类型的列中。通过使用 XML 数据类型的方法,可以建立 XML 列的索引,并对其进行查询和修改。

数据库利用现有的关系基础结构和引擎组件(例如存储引擎和查询处理器)来进行 XML 处理。例如,XML 索引可以创建 B+ 树,而且可以在 Showplan 输出中查看查询计划。通过集成到关系框架中,数据管理功能(如备份/恢复和复制)可用于 XML 数据。同样地,新的数据管理功能(如数据库镜像和快照隔离)可以处理 XML 数据类型,以提供无缝的用户体验。

结构化数据应该存储在表和关系列中。当应用程序需要执行细粒度查询和数据修改时,对于使用 XML 的半结构化数据和标记数据,XML 数据类型是比较合适的选择。

FOR XML 和 OpenXML 增强

现有的 FOR XML 功能已经在几个方面得到了增强。它能够处理 XML 数据类型的实例和其他新的 SQL 类型,例如 [n]varchar(max)。

新的 TYPE 指令生成的 XML 数据类型实例可以分配给 XML 列、变量或参数,也可以使用 XML 数据类型的方法进行查询。这允许嵌套 Select …FOR XML TYPE 语句。

PATH 模式允许用户指定出现列值的 XML 树中的路径,并且与上述嵌套一起使用时比 FOR XML EXPLICIT 更易于编写。

XSINIL 指令与 ELEMENTS 一起使用,可以将 NULL 映射到带有属性 xsi:nil="true" 的元素。另外,新的 ROOT 指令还允许在所有的 FOR XML 模式下指定根节点。新的 XMLSCHEMA 指令生成 XSD 内联架构。

OpenXML 的功能增强包括在 sp_preparedocument 中接受 XML 数据类型以及在行集中生成 XML 和新的 SQL 类型的列。

对 XML 数据类型的客户端访问

客户端可以通过几种方式访问服务器中的 XML 数据。使用 ODBC 和 OLE DB 的本机 SQL 客户端访问以 Unicode 字符串的形式传递 XML 数据类型。OLE DB 还提供对流 Unicode 数据的 XML 数据类型的 ISequentialStream 访问。

托管访问通过 .NET 框架 V2.0 中的 ADO.NET 将 XML 数据作为一个名为 SqlXml 的新类进行传递。它支持一个名为 CreateReader() 的方法,该方法返回 XmlReader 实例来读取返回的 XML。同样地,数据集能够将 XML 数据类型的实例加载到中间层的列,中间层可以作为 XML 文档进行编辑,并且重新保存到 SQL Server。这两者都支持对服务器发出 SQL 查询,以检索在中间层操作的 XML 列。

在 SQL Server 2005 中,可以使用直接对 HTTP 终结点进行的SOAP 访问来查询、检索和修改 XML 数据。

本机和托管客户端技术都提供了新的接口来检索类型化 XML 列的 XML 架构集合。

带有 XQuery 的客户端 XML 支持

除了使用 ADO.NET 来检索表并将结果存放到关系数据集中之外,您还可以使用 XQueryCommand 类将关系数据直接加载到中间层的 XML 文档。该类提供了一个中间层 XQuery 处理器,它能够将 T-SQL 语句作为 XQuery 语言的一部分嵌入。通过这种方式,您可以查询本地 XML 文件,也可以从 SQL Server 检索数据(直接从表或者通过数据库存储程序),再将数据构造成 XML 格式,然后将其加载到中间层 XML 文档。这大大简化了 SQL Server 中的查询,将查询结果构造成特定的 XML 格式,并使其流向业务合作伙伴,而不需要将数据加载到数据集来执行数据转换。

推动 XML 存储方案

XML 数据变得越来越普遍.它可以表示客户数据,带有或不带有描述数据的 XML 架构均可。XML 数据和 XML 架构需要在一起进行管理。实际应用程序的 XML 架构通常比较复杂,因而将这样的 XML 架构映射到表和列是一项复杂的任务。当 XML 架构改变或新的架构加入系统时,长时间维护这样的映射是一件很麻烦的事情。通常,XML 数据存储在文件系统或数据库服务器的文本列中。文本列有数据管理方面的好处(例如,复制和备份/恢复),但是不提供任何基于数据的 XML 结构的查询支持。在具有原生 XML 支持的情况下,使用 XML 进行应用程序开发变得更快。

自定义属性管理

一些应用程序(例如用户界面软件)允许用户从一组固定的属性中进行选择。而其他应用程序则允许用户定义他们自己感兴趣的属性。如果这样的自定义属性是以 XML 的格式存储的,它们就可以很好地进行管理。应用程序可以支持标量属性以外的属性:

  • 可以支持对象中的多值属性,例如,多个电话号码。

  • 可以支持复杂属性,例如,一个文档的作者属性可能是该作者的联系信息。

可以将对象属性存储在 XML 数据类型的列中并建立索引,从而提高查询处理的效率。

数据交换和工作流

XML 允许采用平台无关的方式在应用程序之间交换数据。可以使用 XML 标记将这样的数据建模为消息。代替不断地分割和生成 XML 消息,以 XML 的格式存储消息是明智的。这正是数据流所需要的。到达工作流阶段的 XML 消息携带着当前的状态。每个消息都需要进行处理,处理的进展记录在 XML 内容中(如状态改变),然后将 XML 数据转发到工作流处理的下一个阶段。消息可能是不同类型的,甚至可能是半结构化的,并且有不同的 XML 架构与它们相关联,因此将它们映射到表并不总是一件轻而易举的事。

针对不同垂直领域(如金融数据和地理空间数据)的基于 XML 的标准正在形成。这些标准根据可以查询和更新的实例数据来描述数据的结构。通常,实际数据采用的是二进制形式,而 XML 数据提供关于它们的元数据信息。

举一个简单的例子,为了将一个输入参数表传送到存储程序或函数,应用程序首先将数据转换成 XML,然后将其作为一个 XML 数据类型参数进行传递。在存储过程或函数内,从 XML 参数重新生成行集。

文档管理

假设有一个呼叫中心,它采用 XML 文档来维护患者记录和谈话。当患者发起呼叫时,呼叫中心希望恢复前面的谈话以设置传入呼叫的情景。这可以通过查询 XML 标记实现,从而给应用程序带来好处。此外,还可以方便地检索以前谈话的细节并记录当前的谈话。

像电子邮件之类的文档本质上是半结构化的。带有 XML 标记的文档正变得越来越容易创建,例如,使用 Office 2003。可以将这些 XML 文档存储在 XML 列中,并建立索引,进行查询和更新。因此,通过利用原生 XML 支持,开发人员可以做更多的事情。

SQL Server 2005 中的服务器端 XML 处理

SQL Server 2005 支持包括提供一个数据库,在这个数据库中,您可以存储关系数据和 XML 数据。

XML 数据类型

您可以使用普通的 Create TABLE 语句来创建带有 XML 列的表。然后就可以采用一种特别的方式来建立 XML 的索引。

非类型化的 XML

SQL Server 2005 XML 数据类型实现了 ISO SQL-2003 标准 XML 数据类型。因此,它不仅可以存储格式良好的 XML 1.0 文档,而且可以存储所谓的 XML 内容片段(带有文本节点和任意数目的顶层元素)。在对数据进行格式良好性检查时,并不要求将 XML 数据类型绑定到 XML 架构,但是格式不规范的数据将被拒绝。

当架构不是已知先验的 并且因此而导致基于映射的解决方案不可能实现时,就可以使用非类型化的 XML。如果架构是已知的,但映射到关系数据模型非常复杂并且难于维护,或者存在多个架构而且这些架构是后来根据外部要求绑定到数据的,也可以使用非类型化的 XML。

例:表中非类型化的 XML 列

下面的语句创建一个名为“docs”的表,该表带有整型主键“pk”和非类型化的 XML 列“xCol”:

Create TABLE docs (pk INT PRIMARY KEY, xCol XML not null)

也可以创建一个包含多个 XML 列或关系列、带主键或不带主键的表。

类型化的 XML

如果 XML 架构集合中有描述 XML 数据的 XML 架构,就可以将 XML 架构集合与产生类型化 XML 的 XML 列相关联。可以使用 XML 架构来验证数据的有效性,在编译查询和数据修改语句时执行比非类型化的 XML 更精确的类型检查,以及优化存储和查询处理。

类型化的 XML 列、参数和变量可以存储 XML 文档或内容,可以在声明时将其指定为一个选项(分别为DOCUMENT 或 CONTENT,默认为 CONTENT)。此外,还必须提供 XML 架构集合。如果每个 XML 实例都正好有一个顶层元素,则指定 DOCUMENT;否则,使用 CONTENT。查询编译器在类型检查中使用 DOCUMENT 标记来推理 singleton 顶层元素。

例:表中的类型化 XML 列

XML 列、变量和参数可以绑定到一个 XML 架构集合(请参阅本文后面的“XML 架构处理”一节以获得更多的详细信息和示例)。假定 myCollection 代表这样一个集合。下面的语句创建一个表 XmlCatalog,带有使用 myCollection 进行类型化的 XML 列文档。类型化的 XML 列还被指定为接受 XML 片段,而不只是 XML 文档。

Create TABLE XmlCatalog (
ID INT PRIMARY KEY,
Document XML(CONTENT myCollection))
约束 XML 数据类型的列
  • 除了类型化一个 XML 列之外,还可以在类型化和非类型化的 XML 数据类型的列中使用关系(列或行)约束。大部分 SQL 约束同样可应用于 XML 列,值得注意的唯一例外是主键和外键约束,因为 XML 数据类型的实例是不兼容的。因此,可以指定 XML 列可为空或不可为空,提供默认值,并且在列中定义 CHECK 约束。例如,非类型化的 XML 列可以有 CHECK 约束来验证存储的 XML 实例是否符合 XML 架构。

在下列条件下使用约束:

  • 业务规则不能用 XML 架构表示。例如,花店的交付地址必须在其业务场所 50 英里的范围内,这可以编写成 XML 列中的一个约束条件。该约束条件可以包括 XML 数据类型的方法。

  • 约束条件涉及表中其他的 XML 或 非 XML 列。这样的一个例子就是,使 XML 实例中存在的 Customer (/Customer/@CustId) 的id 与整型 CustomerID 列中的值相匹配。

例:约束 XML 列

要确定<book>的<author>的<last-name> 不同于<author>的<first-name> ,可以指定下列 CHECK 约束:

Create TABLE docs (pk INT PRIMARY KEY, xCol XML not nullCONSTRAINT CK_name CHECK (xCol.exist('/book/author[first-name = last-name]') = 0))

文本编码

SQL Server 2005 将 XML 数据存储为 Unicode (UTF-16)。从服务器检索的XML 数据,其结果也是 UTF-16 编码的。如果想要采用一种不同的编码方式,就需要在检索数据之后进行必要的转换,转换的方法有两种,一种是通过强制类型转换,另一种是在中间层执行转换。 例如,可以在服务器上将 XML 数据强制转换成 varchar 类型,在这种情况下,数据库引擎会通过 varchar 类型的排序所确定的编码方式来序列化 XML。

存储 XML 数据

可以通过多种方式为 XML 列、参数或变量提供 XML 值。

  • 作为隐式转换到 XML 数据类型的字符或二进制 SQL 类型。

  • 作为文件的内容。

  • 作为 XML 发布机制 FOR XML 的输出(带有生成 XML 数据类型实例的 TYPE 指令)

对提供的值进行格式良好性检查,并且允许存储 XML 文档和 XML 片段。如果数据没有通过格式良好性检查,则拒绝它,并发出一个适当的错误消息。

对于类型化的 XML,需要检查提供的值是否符合已注册到类型化 XML 列的 XML 架构集合的 XML 架构。如果该 XML 实例没有通过这种有效性验证,则拒绝它。此外,仅当 CONTENT 允许提供 XML 文档和内容时,类型化的 XML 中的 DOCUMENT 标记才将所接受的值限制为 XML 文档。

例:将数据插入非类型化的 XML 列

下列语句在表 docs 中新插入一行,其中在整型的 pk 列插入的值为 1,而在 XML 列插入的是 实例。 数据(作为字符串提供)被隐式地转换为 XML 数据类型,并且在插入的过程中进行格式良好性检查。

Insert INTO docs VALUES (1,
'<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>')

例:将来自文件的数据插入非类型化的 XML 列

如下所示的 Insert 语句使用 OPENROWSET 读取文件 C:\temp\xmlfile.xml 的内容作为 BLOB。在表 docs 中新插入一行,值 10 作为主键,而 BLOB 作为 XML 列 xCol。格式良好性检查出现在文件内容分配到 XML 列时。

Insert INTO docs
Select 10, xCol
FROM    (Select * FROM OPENROWSET
(BULK 'C:\temp\xmlfile.xml',
SINGLE_BLOB) AS xCol) AS R(xCol)

例:将数据插入类型化的 XML 列

类型化的 XML 列需要 XML 实例数据指定用于对其进行类型化的 XML 架构的目标命名空间(该命名空间可以为空)。在下面的示例中,这是通过命名空间声明 xmlns=http://myDVD 来实现的。

Insert XmlCatalog VALUES(2,
'<?xml version="1.0"?>
<dvdstore xmlns="http://myDVD">
<dvd genre="Comedy" releasedate="2003">
<title>My Big Fat Greek Wedding</title>
<price>19.99</price>
</dvd>
</dvdstore>')

例:存储使用带有 TYPE 指令的 FOR XML 生成的 XML 数据

通过 TYPE 指令增强的 FOR XML 可以生成像 XML 数据类型实例这样的结果。结果 XML 可以分配到 XML 列、变量或参数。在下面的语句中,使用 FOR XML TYPE 生成的 XML 实例被分配给 XML 数据类型的变量 @xVar。可以使用 XML 数据类型的方法来查询变量。

DECLARE @xVar XML
SET     @xVar = (Select * FROM docs FOR XML AUTO,TYPE)
存储表示

XML 数据类型的实例存储在内部的二进制表示中,该表示是可流化的,并且经过了优化,从而可以更有效地进行解析。标记映射到整型值,而映射的值存储在内部表示中。这也产生了一些数据的压缩。

对于非类型化的 XML,节点值存储为 Unicode (UTF-16) 字符串,因此需要进行运行时类型转换才能执行操作。例如,为了求谓词 /book/price > 9.99 的值,必须将该书的价格值转换为小数。而对于类型化的 XML,值的编码类型为在 XML 架构中指定的类型。这使得数据的解析更加有效,并且还不必进行运行时转换。

存储的二进制形式被限制为每 XML 实例 2 GB,这可以适应大部分的 XML 数据。此外,XML 层次的深度还被限制为 128 层。

XML 数据的信息集内容被保留。但是,它不可能是与文本 XML 一模一样的副本,因为下列信息没有保留:无关紧要的空白、属性的顺序、命名空间前缀和 XML 声明。

数据建模考虑事项

通常,关系数据类型和 XML 数据类型的列的组合比较适合数据建模。可以将 XML 数据中的一些值存储在关系列中,而将其余的值或全部的 XML 值存储在 XML 列中。这可以产生更好的性能和锁定特性。

对于 singleton 值(即单值属性),XML 数据中的值可以提升为同一个表中的计算列。多值属性需要单独的属性表,该表必须使用触发器进行填充和维护。查询需要直接针对属性表进行编写。

对于锁定和更新特性,存储在 XML 列中的 XML 数据的粒度是至关重要的。SQL Server 将相同的锁定机制用于 XML 和非 XML 数据。如果粒度比较大,则在多用户的情况下,为了更新而锁定大的 XML 实例会引起吞吐量的下降。而另一方面,严格的分解会失去对象的封装性并增加重新组合的成本。

查询和修改 XML 数据

查询存储在 XML 列中的 XML 实例需要解析列中的二进制 XML 数据。与解析文本形式的 XML 数据相比,解析二进制的 XML 要快得多。XML 索引避免了重新解析,这将在“建立 XML 数据的索引”一节中进行讨论。

XML 数据类型中的方法

如果感兴趣,可以检索全部 XML 值,也可以检索部分 XML 实例。这可以使用四个 XML 数据类型的方法来实现:query()value()exist()nodes(),它们接受 XQuery 表达式作为参数。第五个方法 modify() 允许修改 XML 数据并接受 XML 数据修改语句作为输入。

query() 方法用于提取 XML 实例的部分。XQuery 表达式求值为一个 XML 节点列表。以这些节点中的每一个为根的子树按照文档顺序返回。结果类型为非类型化的 XML。

value() 方法从 XML 实例提取标量值。它返回 XQuery 表达式所求值的节点的值。该值被转换为 value() 方法的第二个参数所指定的 T-SQL 类型。

exist() 方法用于对 XML 实例进行存在性检查。如果 XQuery 表达式求值为非空节点列表,则返回 1;否则,返回 0。

nodes() 方法产生特定 XML 数据类型的实例,每个实例都将其上下文设置为 XQuery 表达式所求值的不同节点。特定的 XML 数据类型支持 query()value()nodes()exist() 方法,并且可以用于 count(*) 聚合和 NULL 检查。所有其他的使用都会产生错误。

modify() 方法允许修改 XML 实例的某些部分,例如添加或删除子树,或者更新标量值(如将书的价格从 9.99 替换为 39.99)。

例:使用 query() 方法

考虑下面的表 docs 的 XML 列 xCol 中的查询,它提取 元素下的任何位置的 id 为 123 的 元素。该查询也从整型主键列检索值。Select 列表中的 query() 方法会为生成 元素序列的表中的每一行进行求值, 元素及其子树是按照文档顺序进行检索的。对于每个 XML 实例,如果没有 id 为 123 的 元素或者其下没有 元素,则不会返回结果,也就是说,query() 方法的返回值为 NULL。

Select pk, xCol.query('/doc[@id = 123]//section')
FROM   docs

NULL 返回值可以在外部 Select 语句中过滤掉。或者也可以使用 exist() 方法,如下一个示例所示。

例:使用 exist() 方法

考虑下面的查询,它在表 docs 的 XML 列 xCol 中包括 query()exist() 方法。exist() 方法求路径表达式 /doc[@id = 123] 的值,检查是否存在顶层 元素,该元素具有一个称为 id 的属性,且其值为 123。对于每个这样的行,Select 子句中的 query() 方法都会进行求值;在这个示例中,query() 方法在 元素下的任何位置都产生一个 元素序列。从 exist() 方法返回 0 的任何行都会被忽略。

Select xCol.query('/doc[@id = 123]//section')
FROM   docs
Where  xCol.exist ('/doc[@id = 123]') = 1

例:使用 value() 方法

下面的查询使用 value() 方法以 Unicode 字符串的形式提取文档第三部分的标题。结果的 SQL 类型的 nvarchar(max) 被指定为 value() 方法的第二个参数。XQuery 函数 data() 从 节点提取标量值。

Select xCol.value( 'data((/doc//section[@num = 3]/heading)[1])', 'nvarchar(max)') FROM docs

XQuery 语言

众多的 XML 都来源于存储在文件系统、Web 服务或配置文件中的 Office 文档。事实上,以 XML 格式或作为虚拟 XML 文档生成的数据正在不断地增加。为了处理这些数量越来越多的数据,一种强大的查询语言 XQuery 应运而生。在 XQuery 语言规范(位于 http://www.w3.org/TR/xquery)中将选择 XQuery 的理由描述为:

  • 一种巧妙地使用 XML 结构的查询语言,可以跨各种数据表示查询,而不管这些数据是物理存储在 XML 中,还是通过中间件被视为 XML。该规范描述了一种称为 XQuery 的查询语言,它旨在能广泛应用于许多类型的 XML 数据源。

  • XQuery 旨在满足 W3C XML 查询工作组 XML 查询 1.0 要求和 XML 查询用例中的用例所确定的要求。它是一种旨在使查询简洁易懂的语言。它还相当地灵活,可以查询大范围的 XML 信息源,其中包括数据库和文档。

  • XQuery 还可以概括为如下表述:XQuery 语言之于 XML 正如 SQL 语言之于关系数据库。

内嵌于 T-SQL 的 Xquery 子集(位于 http://www.w3.org/TR/xquery/) 是一种支持查询 XML 数据类型的语言。这种语言正在由 Worldwide Web Consortium (W3C) 进行开发(目前处于最后请求状态),所有主要的数据库厂商(包括 Microsoft 在内)都参与其中。我们的实现与 2003 年 11 月发布的 XQuery 草案是一致的。

XQuery 将 XPath 2.0 作为导航语言包括在内。SQL Server 2005 的 XQuery 实现提供了用于遍历节点 (for)、节点检查 (where)、返回值 (return) 和排序 (order by) 的构造。它也提供了用于在查询过程中重新进行数据构形的元素构造。

SQL Server 2005 还提供了用于 XML 数据类型的数据修改 (DML) 的语言构造(请参阅下面的“数据修改”一节以获得更多信息)。下面的示例演示了如何将 XQuery 用于 XML 数据类型。

例:使用 XQuery 中丰富的语言构造

下面的查询显示了如何一起使用几个 XQuery 语言构造。它从 id 为 123 的文档返回区域号为 3 和更高的区域中的标题,并将其包装在新标记 中。

Select pk, xCol.query('
for $s in /doc[@id = 123]//section
where $s/@num >= 3
return <topic>{data($s/heading)}</topic>')
FROM docs

“for”遍历 id 为 123 的<doc> 元素下的所有<section> 元素,并且将每个这样的<section> 元素绑定到变量 $s。“where”确保区域号(<section> 元素的 @num 属性)为 3 或更高。该查询按照文档顺序返回区域<heading> 中的值,并且将其包装在一个称为<topic> 的构造元素中。

查询编译和执行

SQL 语句是由 SQL 解析器解析的。当它遇到 XQuery 表达式时,它就会跳转到 XQuery 编译器,然后编译 XQuery 表达式。这会产生一个查询树,并将其嫁接到整个查询的查询树上。

整个查询树会进行查询优化并产生物理查询计划,该计划是根据基于成本的评估得出的。Showplan 输出显示了大部分关系运算符和一些新的运算符(例如,用于 XML 处理的 UDX)。

查询执行与关系框架中的其余部分一样,都是面向元组的。在表 docs 的每一行上都求 Where 子句的值;这需要在运行时解析 XML BLOB 以求出 XML 数据类型方法的值。如果条件满足,则锁定行,然后在行中求 Select 子句的值。结果以 XML 数据类型的形式产生(用于 query() 方法),并且转换成指定的目标类型(用于 value() 方法)。

而如果该行不满足 Where 子句中的条件,它就会被忽略,执行转到下一行。

XML 数据修改

SQL Server 2005 提供了用于数据修改的构造作为对 XQuery 的一个扩展。子树可以在指定的节点之前或之后插入,或者作为最左边或最右边的子节点插入。此外,子树也可以插入到父节点,在这种情况下,它成为父节点最右 边的子节点。属性、元素和文本节点插入都支持。

支持删除子树。在这种情况下,整个子树就从 XML 实例中被移除。

标量值可以用新的标量值进行替换。

例:将子树插入 XML 实例

这个示例显示了 modify() 方法的使用,它将一个新的<section> 元素插入编号为 1 的 元素的右边。

Update docs SET xCol.modify('
insert
<section num="2">
<heading>Background</heading>
</section>
after (/doc/section[@num=1])[1]')

例:将这本书的价格更新为 $49.99

下面的更新语句将 ISBN 为 1-8610-0311-0 的书的<price> 替换为 $49.99。该 XML 实例是通过 XML 架构 http://myBooksinstance 进行类型化的,因而就有了 XML 数据修改语句中的命名空间声明。

Update XmlCatalog
SET    Document.modify ('
default namespace = "http://myBooks"
replace value of (/bookstore/book[@ISBN=
"1-8610-0311-0"]/price)[1] with 49.99')
类型检查和静态错误

XQuery 引入了类型检查。编译阶段检查 XQuery 表达式和数据修改语句的静态类型正确性,并且将 XML 架构用于类型推理(在类型化 XML 的情况下)。如果表达式在运行时由于类型安全冲突而失败,则会产生静态类型错误。静态错误的例子有:将一个字符串与一个整数相加、在操作需要单值的地方接 收一个值序列,以及查询不存在的节点来查找类型化数据。对于因类型不匹配而导致的静态错误,显式转换到正确的类型是一种变通方法。XQuery 运行时错误被转换成空序列。

如果编译器不能确定在运行时是否保证有 singleton 元素,则需要 singleton 元素的定位步骤、函数参数和运算符(例如,eq)就会返回错误。这种问题常常因非类型化的数据而产生。例如,查找属性需要 singleton 父元素;顺序选择单个的父节点就足够了。

例:value() 方法中的类型检查

下面的查询是在非类型化的 XML 列中进行的,它需要 //author/last-name 中的顺序规范,因为 value() 方法需要 singleton 节点作为第一个参数。如果没有,则编译器就不能确定在运行时是否只出现一个<last-name> 节点。

Select xCol.value('(//author/last-name)[1]', 'nvarchar(50)') LastName
FROM   docs

通过求 node()-value() 组合的值来提取属性值可以不需要顺序规范,如下一个示例所示。

例:已知的 singleton 元素

如下所示的 nodes() 方法为每个<book> 元素生成单独的行。对<book> 节点求值的 value() 方法提取 @genre 的值,@genre 作为一个属性,是 singleton 元素。

Select nref.value('@genre', 'varchar(max)') LastName
FROM   docs CROSS APPLY xCol.nodes('//book') AS R(nref)
跨域查询

如果数据驻留在关系数据类型的列和 XML 数据类型的列的组合中,就可能需要编写查询来组合关系数据处理和 XML 数据处理。通过使用带有 TYPE 指令的 FOR XML,可以将关系列和 XML 列中的数据转换成 XML 数据类型的实例,并使用 XQuery 对其进行查询。相反地,可以从 XML 值生成行集,并且使用 T-SQL 来对其进行查询,如下面的“从 XML 数据生成行集”一节所示。

编写跨域查询的一个更加方便和有效的方法是,使用 SQL 变量的值,或者使用 XQuery 或 XML 数据修改表达式中的列:

  • 使用 sql:variable(),可以在 XQuery 或 XML DML 表达式中应用 SQL 变量的值。

  • 通过 sql:column(),可以在 XQuery 或 XML DML 上下文中使用来自关系列的值。

这种方法允许应用程序参数化查询,如下面的示例所示。Sql:column() 的用法与前者类似,并且还带来其他的好处。正如基于成本的查询优化器所确定的,可以使用列的索引来提高效率。此外,也可以使用计算列。

XML 和用户定义的类型不允许与 sql:variable()sql:column() 一起使用。

例:使用 sql:variable() 的跨域查询

在这种查询中,<book> 元素的 ISBN 是使用 SQL 变量 @isbn 来传入的。代替使用常量,sql:variable() 提供 ISBN 的值,并且该查询可以用于搜索任何 ISBN,而不只是 ISBN 为 0-7356-1588-2 的 元素。

DECLARE @isbn varchar(20)
SET     @isbn = '0-7356-1588-2'
Select  xCol
FROM    docs
Where   xCol.exist ('/book[@ISBN = sql:variable("@isbn")]') = 1
从 XML 数据生成行集

在自定义属性管理和数据交换场景中,应用程序通常将 XML 数据的某些部分映射到行集。例如,为了将输入参数表传送到存储过程或函数,应用程序需要将数据转换成 XML,并且将其作为 XML 数据类型的参数传入。在存储过程或函数中,行集是从 XML 参数生成的。

SQL Server 2000 为此提供了 OpenXml()。它简化了从 XML 实例生成行集的过程,方法是指定行集的关系架构以及将 XML 实例内的值映射到行集中的列的方式。

另外,还可以使用 nodes() 方法来生成 XML 实例中的节点上下文,并且在 value()query()exist()nodes() 方法中使用该节点上下文来生成所期望的行集。nodes() 方法接受 XQuery 表达式,在 XML 列的每个 XML 实例中对其进行求值,并且有效地使用 XML 索引。下一个示例演示如何将 nodes() 方法用于行集生成。

例:从 XML 实例提取属性

假定要提取名字不是“David”的作者的名和姓,将其作为由两列(FirstName 和 LastName)组成的一个行集。通过使用 nodes()value() 方法,您可以达到此目的,如下所示:

Select nref.value('first-name[1]', 'nvarchar(50)') FirstName,
nref.value('last-name[1]', 'nvarchar(50)') LastName
FROM   docs CROSS APPLY xCol.nodes('//author') AS R(nref)
Where  nref.exist('.[first-name != "David"]') = 1

在这个示例中,nodes('//author') 产生一个指向每个 XML 实例的 元素的引用的行集。作者的名和姓是通过对与这些引用有关的 value() 方法求值来获得的。要获得好的性能,需要建立 XML 列的索引,这是下一部分的主题。

建立 XML 数据的索引

XML 数据是以内部二进制形式存储的,存储容量可以达到 2 GB。每个查询在运行时一次或多次地解析表中每一行的 XML BLOB。这会使查询处理的速度变得很慢。如果在工作负荷中常常需要进行查询,则建立 XML 列的索引是有好处的,不过,这样做必须考虑在修改数据的过程中维护 XML 索引的成本。

XML 索引是通过一个新的 DDL 语句在类型化和非类型化的 XML 列中创建的。这为该列中的所有 XML 实例创建了一个 B+ 树。XML 列中的第一个索引是“主 XML 索引”。通过这个索引,可以在 XML 列中支持三种类型的次 XML 索引来加速普通类的查询,如下一节所述。

主 XML 索引

在基表(即定义 XML 列的表)的主键中,主 XML 索引需要聚集索引。它在 XML 节点的信息集项的一个子集中创建一个 B+ 树。B+ 树的列表示标记,例如元素和属性名称、节点值和节点类型。其他的列捕获 XML 数据中的文档顺序和结构,以及从 XML 实例的根节点到每个节点的路径,从而有效地对路径表达式进行求值。基表的主键在主 XML 索引中复制,以使索引行与基表行相关。

XML 架构中给定的标记和类型名称被映射为整数值,而映射值存储在 B+ 树中以优化存储。索引中的路径列按照相反的顺序(即从节点到 XML 实例的根)存储映射值的串联。当路径后缀已知时(在一个路径表达式中,如 //author/last-name),相反的表示使得可以匹配路径值。

如果对基表进行分区,则需要以相同的方式对主 XML 索引进行分区,也就是使用相同的分区函数和分区架构。

全部的 XML 实例都是从 XML 列检索的(Select * FROM docs 或 Select xCol FROM docs)。需要 XML 数据类型方法的查询使用主 XML 索引,从索引本身返回标量值或 XML 子树。

例:创建主 XML 索引

下面的语句在表 docs 的 XML 列 xCol 中创建一个名为 idx_xCol 的 XML 索引。

Create PRIMARY XML INDEX idx_xCol on docs (xCol)
次 XML 索引

一旦主 XML 索引创建完毕,就可以创建次 XML 索引来加速工作负荷中不同类的查询。三种类型的次 XML 索引 — PATH、PROPERTY 和 VALUE — 分别对基于路径的查询、自定义属性管理方案和基于值的查询有利。

  • PATH 索引在主 XML 索引的列 (path, value) 中构建 B+ 树。该路径的值是通过计算路径表达式和节点的值得出的,如果提供了一个路径值,则也可以使用所提供的值。在已知 PATH 索引开始字段的情况下,查找 PATH 索引可以加速路径表达式的求值。最常见的情况是在 Select 语句的 Where 子句中对 XML 列使用 exist() 方法。

  • PROPERTY 索引在主 XML 索引的列 (PK, path, value) 中创建 B+ 树,其中 PK 是基表的主键。此索引对 XML 实例中的属性值查找有利。

  • 最后,VALUE 索引在主 XML 索引的列 (value, path) 中创建一个 B+ 树。此索引对节点的值已知但是其路径没有准确地在查询中指定的查询有利。这通常出现在祖先或自身 (descendant-or-self) 轴查询中,例如,//author[last-name="Howard"],其中, 元素可以出现在层次的任何一层。它还可以出现在“wildcard”查询中,例如 /book [@* = "novel"],其中,查询查找具有“novel”属性值的 元素。此外,VALUE 索引还可用于对类型化的 XML 进行基于值的范围扫描。

可以容纳多达 128 层的 XML 层次;在插入和修改的过程中,如果 XML 实例包含更长的路径,则会被拒绝。

类似地,可以建立一个节点值的前 128 个字节的索引;系统中可以容纳更长的值,但是不会建立索引。

例:基于路径的查找

假定下面的查询在工作负荷中是常见的:

Select xCol
FROM   docs
Where  xCol.exist ('/book[@genre = "novel"]') = 1

路径表达式 /book/@genre 和值“novel”对应于 PATH 索引的键字段。因此,类型 PATH 的次 XML 索引有助于此工作负荷:

Create XML INDEX idx_xCol_Path on docs (xCol)
USING XML INDEX idx_xCol FOR PATH

例:获取对象的属性

考虑下面的查询,它从表 T 的每一行检索书的“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    docs

属性索引可用于这种情况,该索引创建如下:

Create XML INDEX idx_xCol_Property on docs (xCol)
USING XML INDEX idx_xCol FOR PROPERTY

例:基于值的查询

在下面的查询中,祖先或自己轴 (//) 指定了部分路径,这样,基于 ISBN 值的查询就可以从 VALUE 索引的使用中受益:

Select xCol
FROM    docs
Where    xCol.exist ('//book[@ISBN = "1-8610-0157-6"]') = 1

VALUE 索引创建如下:

Create XML INDEX idx_xCol_Value on docs (xCol)
USING XML INDEX idx_xCol FOR VALUE
内容索引

可以在 XML 列中创建全文本索引;这会建立 XML 值内容的索引而忽略 XML 标记。属性值不是全文本索引的(因为它们被认为是标记的一部分),而元素标记被用作标记边界。可以在 XML 列中创建 XML 和全文本索引,并且组合使用全文本搜索和 XML 索引。使用全文本索引作为第一个筛选器来缩小选择范围,接着再应用 XQuery 进一步筛选。

使用 CONTAINS() 和 XQuery contains() 的全文本搜索有不同的语义。后者是子字符串匹配,而前者是使用词根检索的标记匹配。

例:在 XML 列中创建全文本索引

在 XML 列中创建全文本索引的步骤与在其他 SQL 类型列中创建全文本索引的步骤是非常相似的。需要在基表中有一个唯一的键列。DDL 语句如下,其中,PK__docs__7F60ED59 是该表的单列主键索引:

Create FULLTEXT CATALOG ft AS DEFAULT
Create FULLTEXT INDEX ON dbo.docs (xCol) KEY INDEX PK__docs__7F60ED59

例:组合全文搜索和 XML 查询

下面的查询检查 XML 值是否在书的标题中包含单词“Secure”:

Select *
FROM   docs
Where  CONTAINS(xCol,'Secure')
AND    xCol.exist('/book/title/text()[contains(.,"Secure")]') =1

CONTAINS() 方法使用全文本索引来产生一个 XML 值的子集,它包含文档中所有的单词“Secure”。exist() 子句确保单词“Secure”出现在书的标题中。

使用 XML 索引执行查询

XML 索引可以加速查询的执行。查询总是针对 XML 列中的主 XML 索引进行编译(如果存在的话)。为整个查询(包括关系部分和 XML 部分)制订一个查询计划,该计划可以通过数据库引擎基于成本的优化器进行优化。根据查询优化器的成本估算来选择次 XML 索引。

XML 索引的目录视图

目录视图用于提供关于 XML 索引的元数据信息。目录视图 sys.indexes 包含带有索引“type”3 的 XML 索引项。“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 索引使用与基表相同的分区架构和分区函数。

例:XML 索引的空间使用

Select sum(Pages)
FROM    sys.fn_indexinfo ('docs', 'idx_xCol_Path' , DEFAULT, 'DETAILED')

这产生在表 T 的所有分区中 XML 索引 idx_xCol_Path 所占用的磁盘页数。如果没有 sum() 函数,则结果将会返回每个分区的磁盘页使用。

XML 架构处理

在系统中,XML 架构是可选的。如前所述,没有绑定到 XML 架构的 XML 数据类型被认为是非类型化的;XML 节点值将作为 Unicode 字符串存储,而 XML 实例需要进行格式良好性检查。可以建立非类型化 XML 列的索引。

要类型化 XML,可以将 XML 数据类型与已注册到 XML 架构集合的 XML 架构相关联。新的 DDL 语句允许创建一个或多个 XML 架构可以注册到的 XML 架构集合。绑定到 XML 架构集合的 XML 列、参数或变量是根据该集合中的所有 XML 架构进行类型化的。在 XML 架构集合中,类型系统标识每个使用其目标命名空间的 XML 架构。

XML 实例中的每个顶层 XML 元素都必须指定它遵循的可能为空的目标命名空间。在插入和修改数据时,将根据每个顶层元素的目标命名空间对其进行有效性检查。二进制 XML 表示根据相关的 XML 架构信息对类型化值进行编码,并且是完整描述的,因此,与非类型化的 XML 相比,它的再解析效率更高一些。在 XML 索引中,还将对值进行适当的类型化(如果 /book/price 是在像 xs:decimal 这样的命名空间中定义的,则会存储为小数)。

在查询编译的过程中,使用 XML 架构进行类型检查,并且会因类型不匹配而引起静态错误。查询编译器还将 XML 架构用于查询优化。

数据库引擎的元数据子系统包含 XML 类型信息,例如 XML 架构集合和它们所包含的 XML 架构,以及基本 XSD 和关系类型系统之间的映射。几乎所有的 W3C XML 架构 1.0 规范(请参阅 http://www.w3.org/TR/2001/REC-xmlschema-1-20010502/http://www.w3.org/TR/2001/REC-xmlschema-2-20010502/)都支持。XML 架构文档中的注释和注解都没有保留,并且不支持 key/keyref。

XML 架构集合

XML 架构集合是一个元数据实体(通过关系架构定义作用域),并且包含一个或多个相关(例如,使用<xs:import>) )或不相关的 XML 架构。XML 架构集合中单独的 XML 架构是使用其目标命名空间进行标识的。XML 架构集合是安全的实体,大致与表类似。

XML 架构集合是使用 Create XML SCHEMA COLLECTION 语义创建的,提供一个或多个 XML 架构。然后可以使用 XML 架构集合来类型化 XML 列。这种设计产生了灵活的数据模型,其中,按照不同的 XML 架构类型化的 XML 可以存储在相同的列中。当 XML 架构的数量很大时,这尤其方便。此外,这种设计还支持对 XML 架构进行一定程度的扩展。

另外,类型化的 XML 列中的选项 DOCUMENT 或 CONTENT 分别指定了 XML 树或片段是否可以存储在 XML 列中。默认为 CONTENT。对于 DOCUMENT,每个 XML 实例都必须指定其顶层元素的目标命名空间,该 XML 实例就是按照这个命名空间来进行有效性检查和类型化的。而对于 CONTENT,每个顶层元素都可以指定 XML 架构集合中的任何一个目标命名空间。该 XML 实例按照出现在实例中的所有目标命名空间进行有效性检查和类型化。

例:创建 XML 架构集合

假定要使用带有目标命名空间 http://myBooks 的 XML 架构来类型化 XML 实例。可以创建一个 XML 架构集合 myCollection 并将该 XML 架构作为 myCollection 的内容,如下所示:

Create XML SCHEMA COLLECTION myCollection AS
'<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns="http://myBooks"
elementFormDefault="qualified"
targetNamespace="http://myBooks">
<xsd:element name="bookstore" type="bookstoreType" />
<xsd:complexType name="bookstoreType">
<xsd:sequence maxOccurs="unbounded">
<xsd:element name="book" type="bookType" />
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="bookType">
<xsd:sequence>
<xsd:element name="title" type="xsd:string" />
<xsd:element name="author" type="authorName" />
<xsd:element name="price" type="xsd:decimal" />
</xsd:sequence>
<xsd:attribute name="genre" type="xsd:string" />
<xsd:attribute name="publicationdate" type="xsd:string" />
<xsd:attribute name="ISBN" type="xsd:string" />
</xsd:complexType>
<xsd:complexType name="authorName">
<xsd:sequence>
<xsd:element name="first-name" type="xsd:string" />
<xsd:element name="last-name" type="xsd:string" />
</xsd:sequence>
</xsd:complexType>
</xsd:schema>'

同时也为该 XML 架构注册到的 myCollection 创建了一个新的元数据实体。

修改 XML 架构集合

Alter XML SCHEMA COLLECTION 语句支持用新的顶层架构组件来扩展 XML 架构,并且还支持将新的 XML 架构注册到 XML 架构集合。下面的示例对此进行了演示。

例:改变 XML 架构集合

下面的语句显示了如何将具有目标命名空间 http://myDVD 的新 XML 架构添加到 XML 架构集合 myCollection 中:

Alter XML SCHEMA COLLECTION myCollection ADD
'<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns="http://myDVD"
elementFormDefault="qualified"
targetNamespace="http://myDVD">
<xsd:element name="dvdstore" type="dvdstoreType" />
<xsd:complexType name="dvdstoreType">
<xsd:sequence maxOccurs="unbounded">
<xsd:element name="dvd" type="dvdType" />
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="dvdType">
<xsd:sequence>
<xsd:element name="title" type="xsd:string" />
<xsd:element name="price" type="xsd:decimal" />
</xsd:sequence>
<xsd:attribute name="genre" type="xsd:string" />
<xsd:attribute name="releasedate" type="xsd:string" />
</xsd:complexType>
</xsd:schema>'
XML 架构集合的目录视图

XML 架构集合的 SQL 目录视图允许用户重新构造单独的 XML 架构命名空间的内容。XML 架构集合在目录视图 sys.xml_schema_collections 中枚举。XML 架构集合“sys”由系统定义,并且包含一些预定义的命名空间,这些命名空间可用于所有用户定义的 XML 架构集合,而不必显式地加载它们。该列表包含用于 xml、xs、xsi、fn 和 xdt 的命名空间。

值得一提的其他两个目录视图是:sys.xml_schema_namespaces,它枚举每个 XML 架构集合中的所有命名空间;sys.xml_components,它枚举每个 XML 架构中的所有 XML 架构组件。

内置函数 XML_SCHEMA_NAMESPACE() 接受关系架构和 XML 架构集合的名称,以及 XML 架构的目标命名空间(可选)。它返回包含该 XML 架构的 XML 数据类型实例。如果目标命名空间参数被省去,那么该内置函数返回的 XML 实例就会包含 XML 架构集合中除预定义 XML 架构以外的所有 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 架构集合输出指定的 XML 架构

下面的语句从(关系)架构 dbo 中的 XML 架构集合“myCollection”输出带有目标命名空间 http://myBooks 的 XML 架构。

Select XML_SCHEMA_NAMESPACE (N'dbo', N'myCollection',
N'http://myBooks')
对 XML 架构集合的访问控制

通过使用 SQL Server 2005 的安全模型,可以像任何 SQL 对象一样保护 XML 架构集合。您可以授予用户在数据库中创建 XML 架构集合的特权。每个 XML 架构集合都支持 Alter、CONTROL、TAKE OWNERSHIP、REFERENCES、EXECUTE 和 VIEW DEFINITION 等权限。

执行 Alter XML SCHEMA COLLECTION 语句需要 Alter 权限。

如果要通过执行 Alter AUTHORIZATION 语句将 XML 架构集合的所有权从一个用户转移到另一个用户,则需要 TAKE OWNERSHIP 权限。

REFERENCES 权限授权用户在任何需要架构绑定的地方使用 XML 架构,例如,类型化或约束 XML 列和参数。

要检查用户针对 XML 架构集合插入或更新的值的有效性,则需要 EXECUTE 权限。使用 XML 数据类型从类型化的 XML 列、变量和参数查询值也需要此权限。

VIEW DEFINITION 权限允许用户访问目录视图中与 XML 架构集合(所有的 XML 架构都包含其中)对应的行,所有 XML 架构组件都包含在这些 XML 架构中。

CONTROL 权限授予用户执行 XML 架构集合中任何操作的权限,包括使用 Drop XML SCHEMA COLLECTION 语句撤消 XML 架构集合。这意味着具有 XML 架构集合的其他权限。

除了表或 XML 列中的其他权限以外,还需要 XML 架构集合的权限。用户需要权限来创建表,并且还需要 XML 架构集合 C 中的 REFERENCES 权限来创建包含 XML 列 X(按照 C 类型化)的表 T。具有将数据插入列 X 的权限的用户可以这样做,前提是用户具有 XML 架构集合 C 中的 EXECUTE 权限。类似地,要使用 XML 数据类型方法来查询列 X 中的数据,用户需要列 X 中的 Select 权限和列 C 中的 EXECUTE 权限。然而,对于从列 X 检索整个 XML 值,列 X 中的 Select 权限就足够了,例如 Select X FROM T 或 Select * FROM T。

可以从用户撤回权限,还可以通过 SQL Server 2005 的安全模型拒绝用户的权限,正如允许其拥有权限一样。

目录视图的可视化

XML 架构集合中具有 Alter、TAKE OWNERSHIP、REFERENCES、VIEW DEFINITION 或 CONTROL 权限的用户可以在目录视图中访问 XML 架构集合的行及其所包含的 XML 架构和它们的 XML 架构组件。该主体还可以通过使用内置函数 XML_SCHEMA_NAMESPACE() 来访问 XML 模式集合的内容。

如果用户的 VIEW DEFINITION 权限被拒绝,则该用户就不能在目录视图中或通过使用 XML_SCHEMA_NAMESPACE() 访问 XML 架构集合。

对 FOR XML 的增强

可以将 TYPE 指令生成的 XML 数据类型实例分配给 XML 列、变量或参数,或者使用 XML 数据类型方法对其进行查询。这使得可以嵌套 Select …FOR XML TYPE 语句。

PATH 模式允许用户指定需要出现列值的 XML 树中的路径,并且与上述嵌套一起使用时更易于编写(与 FOR XML EXPLICIT 相比),不过,它也不可用于深层次。

与 ELEMENTS 一起使用的指令 XSINIL 可以将 NULL 映射为具有属性 xsi:nil="true" 的元素。新的 ROOT 指令允许在所有的 FOR XML 模式下指定根节点。新的 XMLSCHEMA 指令生成 XSD 内联架构。

要获得更多关于改进和示例的信息,请参阅由 Michael Rys 撰写的“What's New in FOR XML in Microsoft SQL Server”(MSDN 于 2004 年 6 月发布)。

性能指导原则

XML 数据模型比关系数据模型更加丰富,因而也更加复杂。XML 数据模型不仅允许对复杂的数据进行建模,而且还必须保留层次关系和数据中的文档顺序。文档顺序是通过基于 XML 节点标识符的排序维护的;这同时维护了层次关系。所有这些都对更复杂的查询计划产生了影响。

为了获得更好的性能,应该将结构化数据存储在表的关系列中。如果数据是半结构化或非结构化的,则需要选择使用 XML 数据模型来进行建模,并且加入 XML 标记,不过,别指望会有什么好的性能。XML 架构有助于查询优化。

SQL Server CLR 中的 XML 支持

通过使用 SQL Server CLR 支持,可以在托管代码中编写服务器端逻辑来执行业务规则。可以采用多种方法来将这些业务逻辑添加到 XML 数据中:

  • 可以在 XML 值传入的托管代码中编写 SQLCLR 函数,并且使用 System.Xml 命名空间提供的 XML 处理功能。这样的一个例子是将 XSL 转换应用于 XML 数据,如下所述。另外,还可以将 XML 反序列化成一个或多个托管类,并且使用托管代码对它们进行操行。

  • 可以编写 T-SQL 存储过程和函数来调用 XML 列中的处理,以满足您的业务需要。

例:应用 XSL 转换

考虑接受 XML 数据类型实例的 CLR 函数 TransformXml() 和存储在文件中的 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);
}

一旦注册了程序集并创建了用户定义的 T-SQL 函数 SqlXslTransform()(与 TransformXml() 相对应),就可以从 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 和 Visual Studio? 2005 在线书籍中找到。

SQL Server 2005 中的客户端 XML 处理

XML 数据类型的客户端支持

.NET 框架 V2.0 中的 ADO.NET XML 支持

SqlDataReader.GetSqlXml() 方法的 System.Data.SqlTypes 命名空间中,XML 数据类型是作为类 SqlXml 公开的。可以使用 SqlXml.CreateReader() 函数从 SqlXml 对象获得 XmlReader。

类型化 XML 列的 XML 架构集合的名称由三部分组成,可以从 XML 列的元数据中获得(通过使用 SqlDataReader 对象中的 GetSchemaTable() 或 GetSqlMetaData (int)),其三个部分作为三个属性,分别表示数据库 (XmlSchemaCollectionDatabase)、关系架构 (XmlSchemaCollectionOwingSchema) 和 XML 架构集合 (XmlSchemaCollectionName) 的名称。

客户端可以使用新的架构行集 XMLSCHEMA 从服务器检索 XML 架构。XMLSCHEMA 行集包含 XML 架构集合、目标命名空间和 XML 架构内容本身这样三列。

下面的示例显示了用于对 XML 数据类型进行托管访问的主干代码。

例:对 XML 数据类型进行进程内访问

下面的 C# 代码演示了如何从进程内提供程序访问 XML 数据类型。用于 SQL 客户端访问的代码需要适当地改变进程内访问。

using System;
using System.Xml;
using System.Data;
using System.Data.SqlTypes;
using System.Data.SqlServer;
class xmldtADONETReadAccessInProc
{
static void ReadXmlDataType () {
// in-proc connection to server
SqlConnection conn = SqlContext.GetConnection();
// prepare query to select xml data
SqlCommand cmd = conn.CreateCommand();
cmd.CommandText = "Select xCol FROM docs";
// execute query and retrieve incoming data
SqlDataReader r = cmd.ExecuteReader();
r.Read();
// access XML data type field in rowset
SqlXml xml = r.GetSqlXml(0);
new XmlTextWriter(Console.Out).WriteNode(
xml.CreateReader(), true);
}
}

例:使用 SQL 客户端提供程序更新 XML 数据类型列

下面的代码所显示的 WriteXmlDaraType () 方法使用 SQL 客户端程序提供程序来替换 XML 列中的值。用于进程内提供程序的代码是相似的。

using System;
using System.Xml;
using System.Data;
using System.Data.SqlTypes;
using System.Data.SqlClient;
class xmldtADONETUpdateAccess
{
static void WriteXmlDataType () {
// connection to server
SqlConnection conn = new SqlConnection("server=server1;" +
" database=XMLtest; Integrated Security=SSPI");
conn.Open();
// update XML column at the server
SqlCommand cmd = conn.CreateCommand();
cmd.CommandText = "Update docs SET xCol=@x Where id=1";
// set value of XML parameter
SqlParameter p = cmd.Parameters.Add("@x", SqlDbType.Xml);
p.Value = new SqlXml(new XmlTextReader("<hello/>",
XmlNodeType.Document, null));
// execute update and close connection
cmd.ExecuteNonQuery();
conn.Close();
}
}
SQL 本机客户端访问

在新的 SQL 本机访问 (SQLNCLI) 的 OLE DB 提供程序中,可以通过 ISequentialStream 来将 XML 数据类型列作为 Unicode 字符串(DBTYPE_XML、DBTYPE_BSTR、DBTYPE_WSTR 和 DBTYPE_VARIANT)或 Unicode 字符串流进行检索。默认为 DBTYPE_XML。

由三部分组成的 XML 架构集合名称是在由 IDBSchemaRowset::GetRowset() 返回的 COLUMNS 架构行集的三个新列中携带的:SS_XML_CATALOGNAME 给出目录的名称;SS_XML_SCHEMANAME 给出 XML 架构集合所驻留的关系架构的名称;SS_XML_SCHEMACOLLECTIONNAME 给出 XML 架构集合的名称。这些名称都属于 DBTYPE_WSTR 类型。对于非类型化的 XML 列,这些列具有 NULL 值。

可以对 PROCEDURE_PARAMETERS 架构行集和 IColumnRowset:GetColumnRowset() 进行类似的改变。

要检索 XML 架构集合的内容,客户端可以通过在对 XML_SCHEMA_NAMESPACE() 的调用中使用这些名称来单独访问服务器,并且返回 XML 架构作为 XML 数据类型。另外,带有新的 SS_XMLSCHEMA 架构行集的 IDBSchemaRowset 可以返回目录的名称、关系架构的名称、XML 架构集合的名称、目标命名空间和 XML 架构。

要使用 SQLNCLI 进行 ODBC 访问,需要将 XML 数据类型映射到名为 SQL_SS_XML 的 Unicode 可变长度字符数据。由三部分组成的 XML 架构集合的名称是通过用于 XML 列的 SqlColAttribute 作为 CharacterAttributePtr 出现的。这些字段标识符为 SQL_DESC_SS_XML_SCHEMA_CATALOG_NAME、SQL_DESC_SS_XML_SCHEMA_SCHEMA_NAME 和 SQL_DESC_SS_XML_SCHEMA_COLLECTION_NAME,分别用于数据库、关系架构和 XML 架构集合的名称。

用户必须安装 SQL Server 2005 版的数据库服务器或客户端工具才能获取 SQL 本机客户端。在撰写本文时,还不能使用 MDAC 2.8 ADO 对 XML 数据类型进行 OLE DB 访问。

SQLXML — XML 和关系架构之间的映射

可以使用 SQLXML 映射技术来创建关系数据的逻辑 XML 视图。XML 视图(也称为“映射”或“带注解的架构”)是通过将特殊的注解添加到特定的 XSD 架构中创建的。于是,其他的 SQLXML 技术就可以使用这种带注解的架构,将针对逻辑 XML 视图进行的查询和更新转换成针对关系表进行的查询和更新:

  • 当 XML 视图与 XPath 查询组合在一起时,SQLXML 将生成 FOR XML 查询来查找请求数据,并且根据架构的指定对其进行构形。

  • SQLXML Updategrams 表示对 XML 实例进行的更改,当与带注解的架构组合在一起时,将使用开放式并发将这些更改重新保存到关系更改中,以确保更新正确的数据。

  • SQLXML Bulkload 使用 XML 视图将 XML 数据“切碎”并存放到关系表中。

有关这些主题的更多信息,请访问 SQLXML documentation

创建关系表的 XML 视图

要创建数据库的 XML 视图,需要从用于 XML 数据的 XSD 架构开始。数据库表/视图中的行将映射为该架构中复杂类型的元素。数据库中的列值映射为属性或简单类型的元素。

默认情况下,如果没有给出显式注解,SQLXML 就会假定复杂类型的元素将映射到表,而简单类型的元素和属性将映射到列。只有当元素和属性的名称与数据库中表和列的名称完全相同时,这才有效。

如果元素/属性的名称与它映射到的表/视图或列的名称不同,则必须创建显式映射。下面的注解用于指定 XML 文档中的元素或属性间的映射和数据库中的表(视图)或列之间的映射:

  • sql:relation — 将 XML 元素映射到数据库表。

  • sql:field — 将元素或属性映射到数据库列。

通过映射关系创建 XML 视图中的层次

在数据库中,表可以通过外键关系来关联。在 XML 中,这些相同的关系是通过嵌套的元素层次来表示的。为了在映射中构造正确的嵌套,必须指定关联元素的方式。可以使用 sql:relationship 注解在映射架构元素中建立这些关系。在这种注解中,可以指定父表和子表,以及每个表中需要用于执行加入的列。然后,SQLXML 将利用这些信息为映射构造正确的嵌套层次。

使用溢出存储未使用的数据

当 XML 数据有正规的结构时映射是有效的。然而,在 XML 中,可能有一些数据是非结构化的,还可能有一些数据没有映射到特定的列。要存储这种数据并随后检索它,可以使用 sql:overflow 注解。sql:overflow 注解指定存储所有未使用数据的列以及查询时从何处检索数据。

通过溢出列,还可以扩展 XML,而不必将其添加到数据库。可以在任何时候将元素和属性添加到 XML 结构,而不必在数据库中添加列来存储它们。可以将它们简单地存储到溢出字段,并且在适当的时候对其进行检索。

更多信息

有关创建 XML 视图和映射示例的详细信息,请参阅 Creating XML Views by Using Annotated XSD Schemas

使用 XPath 来查询 XML 视图

一旦创建好数据库的 XML 视图,就可以使用 XPath 查询语言来查询视图,就好像它是实际的 XML 文档一样。SQLXML 支持 XPath 1.0 查询语言中的一个子集。当针对映射进行 XPath 查询时,SQLXML 将它们组合在一起,并且创建发送到 SQL Server 的 FOR XML EXPLICIT 语句。然后检索正确的数据,并根据映射对其进行构形。

请参阅 SQLXML 文档以获得关于 XML 视图中支持的 XPath 子集的详细信息。

使用 Updategrams 通过 XML 视图进行更新

可以通过 XML 视图修改(插入、更新或删除)SQL Server 中的数据库,方法是针对数据库的 XML 视图使用 Updategram。

updategram 的结构

updategram 是一个 XML 文档,带有<sync>、<before> 和<after> 元素,这些元素构成了 updategram 的语法。每个<sync> 块包含一个或多个<before> 和<after> 块。<before> 标识记录实例的现有状态(也称为“前状态”)。<after> 标识要更改的数据的新状态。updategram 是删除、插入还是更新记录实例取决于<before> 和<after> 块的内容。

插入操作

当记录实例出现在<after> 块而没有出现在相应的<before> 块中时,updategram 指示插入操作。在这种情况下,updategram 将<after> 块中的记录插入数据库。

删除操作

当记录实例出现在<before> 块而没有相应的记录出现在<after> 块中时,updategram 指示删除操作。在这种情况下,updategram 从数据库中删除<before> 块中的记录。

如果在 updategram 中指定的元素或者匹配表中的多行或者不匹配表中的任何行,则 updategram 会返回错误,并取消整个<sync> 块。updategram 中的元素一次只能删除一条记录。

更新操作

当更新现有的数据时,必须指定<before> 和<after> 块。updategram 使用<before> 块中指定的元素来标识数据库中的现有记录。<after> 块中相应的元素指示在执行更新操作之后记录的外观应该怎样。

<before>块中的元素必须只与数据库中的一个表行相匹配。如果该元素匹配多个表行或不匹配任何表行,则 updategram 都会返回错误,并且取消整个<sync> 块。

更多信息

要获得更多关于通过 XML 视图创建和使用 updategram 修改数据的信息,请参阅 Using Updategrams to Modify Data

通过 XML 视图大量加载 XML 数据

XML Bulk Load 是一个 COM 对象,它允许您将 XML 数据加载到 SQL Server 表中。虽然通过使用 Insert 语句和 OPENXML 函数可以将 XML 数据插入 SQL Server 数据库中,但是当需要插入大量的 XML 数据时,这种大量加载实用程序提供了更高的性能。XML Bulk Load 解释映射架构并标识 XML 数据要插入的表。然后,它将 XML 数据“切碎”并存放到关系表中。

在进行大量加载处理时,由于源 XML 文档可能很大,所以并不将整个文档读入内存。相反,XML Bulk Load 将 XML 数据解释为流,并且边解释它边读取它。当该实用程序读取数据时,它标识数据库表,从 XML 数据源生成适当的记录,然后将记录发送到 SQL Server,以供插入。

要获得更多关于 Bulk Load 如何工作和使用的信息,请参阅 Performing Bulk Load of XML Data

SQLXML 数据访问方法

由于有了 SQL Server 2000,所以又增加了两种访问 SQLXML 功能的新方法:

  • SQLXML 托管类

  • SQLXML Web 服务

另外,还增强了对 SQL Server 的 HTTP 访问,以提供对模板中的 Updategram 的支持。

SQLXML 托管类

SQLXML 托管类公开 Microsoft .NET 框架内的 SQLXML 3.0 的功能。通过 SQLXML 托管类,可以编写 C# 应用程序从 SQL Server 的实例访问 XML 数据,将数据带入 .NET 框架环境,处理数据,并将更新重新发送回 SQL Server 以应用这些更新。

要获得更多关于如何使用 SQLXML 托管类的细节,请参阅 SQLXML .NET Support

SQLXML Web 服务

SQLXML 中的 Web 服务支持将 SQL Server 作为 Web 服务公开,从而为客户端提供 SQL Server 功能。可以将 SOAP HTTP 请求发送到正在运行 SQLXML 的服务器,以执行存储过程、用户定义的函数 (UDF) 和模板。

通过使用所包括的用于 SQL Server 实用程序的 IIS 虚拟目录管理,也可以设置 Web 服务。一旦 Web 服务虚拟目录设置完毕,就可以将存储程序和模板添加到站点。然后,客户端就可以通过 HTTP 之上的 SOAP 协议来访问这些服务。

SQL Server 2005 现在包括对服务器中的 Web 服务的本机支持。然而,只有 SQLXML 支持通过 Web 服务访问 XML 模板。另外,可以在不同于数据库服务器的服务器上设置 SQLXML,创建远离数据库的单独中间层盒子。

要获得更多关于 SQLXML Web 服务的细节,请访问 Web Services (SOAP) Support in SQLXML

.NET 框架 2.0 中的 XQuery

为了在 SQL Server 2005 中补充引入 XQuery,在 Visual Studio 2005 Beta 的 .NET 框架 V2.0 版中,System.Xml.Query 命名空间内置了一个中间层 XQuery 处理器。在这一部分中,我们说明该处理器如何提供查询和聚合完全不同的数据源的能力,例如文件和关系数据存储 SQL Server 数据库。

简单的说,下面的代码示例显示了一个查询,用于选择书店中所有 genre 属性值为 autobiography 的书,并且将这些书的标题作为一个新的书店写出。执行查询的称为“books.xml”的本地文件被加载,而将结果写入另一个称为 “output.xml”的本地文件。

using (XmlWriter writer = XmlWriter.Create("output.xml"))
{
XQueryCommand xq = new XQueryCommand();
string query =
"<bookstore>" +
"{ for $s in /bookstore/book " +
"where $s/@genre='autobiography' " +
"return $s/title }" +
"</bookstore>";
xq.Compile(query);
xq.Execute("books.xml", new XmlUrlResolver(), writer);
}

查询表达式如下:

<bookstore>
{for $s in /bookstore/book
where $s/@genre='autobiography'
return $s/title}
</bookstore>

XQueryCommand 类是 XQuery 处理器。首先由 Compile 方法编译查询表达式,该方法对值进行类型检查,并且生成一个可执行文件。Execute 方法执行接受 books.xml 文档作为输入的查询。

在 SQL Server(具有将 T-SQL 语句内嵌到 XQuery 表达式的功能)中,XQueryCommand 类还能够对关系表中的数据进行查询。事实上,这使得能够将两个不同的查询语言之间的集成运用于不同的域,并且还补充了反向 SQL Server 2005,其中,T-SQL 语句内嵌到 XQuery 语句。下面的代码示例显示了一个查询,用于从样本 Northwind 数据库中选择所有的顾客。

declare namespace sql='http://schemas.microsoft.com/framework/2003/xml/sql';
<Customers>{
$for $customer in sql:query("select * from customers", "conn")/*
return customer
}</Customers>

sql:query 方法使您能够对连接进行 T-SQL 查询,其结果以与 XQuery doc() 函数完全相同的风格作为 XML 文档返回。如果结果是由关系类型组成的,则在客户端作为有效流转换成 XML。如果 SQL 查询的结果已经是 XML,例如 FOR XML 查询或 XML 数据类型的查询,则使用该查询的结果。如果没有返回结果,像在大多数 T-SQL DML 语句的情况下,这解释为空文档,它是有效的。来自此 XQuery 的输出如下所示,它列出了用 Customers 元素包装的顾客表中所有的顾客。在结果中自动为表中的每一行创建行元素。

<Customers>
<row>
<CustomerID>ALFKI</CustomerID>
<CompanyName>Alfreds Futterkiste</CompanyName>
<ContactName>Maria Anders</ContactName>
<ContactTitle>Sales Representative</ContactTitle>
<Address>Obere Str. 57</Address>
<City>Berlin</City>
<PostalCode>12209</PostalCode>
<Country>Germany</Country>
<Phone>030-0074321</Phone>
<Fax>030-0076545</Fax>
</row>
<row>
<CustomerID>ANATR</CustomerID>
<CompanyName>Ana Trujillo Emparedados y helados</CompanyName>
<ContactName>Ana Trujillo</ContactName>
<ContactTitle>Owner</ContactTitle>
<Address>Avda. de la Constitucion 2222</Address
<City>Mexico D.F.</City
<PostalCode>05021</PostalCode>
<Country>Mexico</Country>
<Phone>(5) 555-4729</Phone>
<Fax>(5) 555-3745</Fax>
</row>
...
</Customers>

为了通过 SQL Server 执行此查询,需要使用下面的代码。

static String ConnectionString = "database=Northwind;Data Source=localhost;Integrated Security=SSPI";
using (SqlConnection connection = new SqlConnection(ConnectionString))
{
XmlDataSourceResolver resolver = new XmlDataSourceResolver();
resolver.Add("conn", connection);
XQueryCommand xq = new XQueryCommand();
StreamReader reader = new StreamReader("query.xml");
xq.Compile(reader);
XmlWriterSettings settings = new XmlWriterSettings();
settings.Indent = true;
settings.ConformanceLevel = ConformanceLevel.Auto;
using (XmlWriter writer = XmlWriter.Create("output.xml", settings))
{
try
{
connection.Open();
xq.Execute(resolver, writer);
}
catch (Exception e)
{
Console.Out.WriteLine(e.Message);
Console.Out.WriteLine(e.InnerException.Message);
}
}
}

这里,XmlDataSourceResolver 类提供使数据库连接与 sql:query 调用内可以使用的名称相关联的角色。首先从名为 query.xml 的本地文件中读取该查询,再进行编译,然后创建 XmlWriter 来写入结果。需要注意 XmlWriter 中使用的 ConformanceLevel.Auto 设置,它自动检测创建片段还是创建文档,这是必要的,因为可以借此构造结果中的元素,XQuery 能够生成这两者。在执行查询之前,向数据库打开连接,然后执行查询,并将结果写入文件。

sql:query 命令极其灵活而强大,它能够将有名称的参数提供给服务器上的 T-SQL 查询或调用的存储过程。sql:query 中命名的参数的作用完全等同于 System.Data.SqlClient 类。参数是在 T-SQL 查询中提供的,由后面紧跟名称的“@”表示。然后提供该名称和实际值之间的绑定作为查询的参数。对于每个命名的值,可以将相应的值(由序列中的位置决定) 用于绑定。必须有与语言绑定数量相同的参数名称。下面的示例最好地对此进行了演示。

declare namespace sql='http://schemas.microsoft.com/framework/2003/xml/sql';
let $country := "USA"
<Customers>{
for $customer in sql:query("select * from customers where Country = @country",
"conn", {"@country"}, {$country})
return $customer/PostalCode
}</Customers>

这里,将参数 country 提供给 T-SQL 查询,从而将 $country XQuery 变量绑定到 @country 参数。结果是所有美国顾客的邮政编码列表。

sql:query 函数还可用于调用 T-SQL 存储过程(如果需要的话,就以上述方式传送参数)。下面的示例显示了如何调用 Northwind 数据库中的“Ten Most Expensive Products”存储过程,它没有输入参数。

declare namespace sql ='http://schemas.microsoft.com/framework/2003/xml/sql';
for $prod in sql:query('Exec "Ten Most Expensive Products"', "conn")
return $prod

小节

本文描述了 SQL Server 2005 中 XML 的补充技术。服务器端功能包括 XML 存储、索引和查询处理的本机实现。现有的功能(例如 FOR XML 和 OpenXML)已经得到增强。客户端支持包括增强 ADO.NET 来支持 XML 数据类型,以及在 System.Xml 中支持 XQuery 查询不同的 XML 源。另外,SQLXML 映射技术的 Web 版增强现在已经合并到 SQL Server 2005 中。

客户端和服务器端支持可用于不同的情况。XML 数据类型提供了一种简单的机制来存储 XML 数据,方法是将 XML 数据插入非类型化的 XML 列。使用 XML 架构定义类型化的 XML 有助于数据库引擎优化存储和查询处理,另外,还可以提供数据验证。

XML 数据类型保留了文档顺序,它对于像文档管理这样的应用程序非常有用。它还可以处理递归的 XML 架构。对于带有已知架构的结构化数据,关系数据模型仍然是最佳的选择。[n]varchar(max) 正好适合于查询和更新不重要的情况。

只要是在服务器上对存储在表中的关系数据使用以 XML 为中心的编程模型,就可以使用 SQLXML 映射技术。映射基于将 XML 架构定义为 XML 视图。映射可用于将 XML 数据大量加载到表以及使用 XPath 1.0 来查询表。SQLXML 映射技术不保留文档顺序,因此可用于 XML 数据处理而不是 XML 文档处理。

在即将发布的 .NET 框架 V2.0 Beta 1 版中,System.Xml 内的核心 XML 类支持读、写、操作和转换 XML。通过改进性能、可用性、类型化和查询,V2.0 版对 XML 的支持在创新性、标准支持和易用性方面继续保持行业领先。这包括用于操作 SQL Server 外的 XML 源的 XQuery 处理器在内。

服务器端技术和客户端技术互为补充。映射技术可以利用服务器端功能来增加其功能,例如维护文档顺序和递归架构,以及找到更多的应用领域。另一方 面,CLR 将可扩展性和现有 XML 工具(如 XSLT 转换)的价值带入了 XML 数据类型。服务器和客户端中的 XQuery 实现彼此之间以及与 2003 年 11 月发布的 XQuery 规范草稿之间都是一致的。XML 架构实现在服务器端实现和中间层实现之间也是一致的。

Shankar Pal SQL Server 引擎的程序经理,主要研究服务器端 XML 技术。

Mark Fussell Microsoft WebData 组的首席程序经理,该小组负责开发 Microsof 的数据访问技术,其中包括 .NET 框架的 System.Xml System.Data 命名空间中的组件、 Microsoft XML 核心服务 (MSXML) 以及 Microsoft 数据访问组件 (MDAC) 。他的网络日记存放在 http://weblogs.asp.net/mfussell

Irwin Dolobowsky 是 Webdata 组的程序经理。

转到原英文页面

[SQL]SQL Server:SQL优化使用 EXISTS 代替 IN 和 inner join

mikel阅读(981)

    在使用Exists时,如果能正确使用,有时会提高查询速度:

      1,使用Exists代替inner join

      2,使用Exists代替 in

 

  1,使用Exists代替inner join例子:

     在一般写SQL语句时通常会遇到如下语句:

      两个表连接时,取一个表的数据,一般的写法通过关联查询(inner join):      

select a.id, a.workflowid,a.operator,a.stepid
from  dbo.[[zping.com]]] a
inner join workflowbase b on a.workflowid=b.id
and operator='4028814111ad9dc10111afc134f10041'

 

查询结果:

(1327 行受影响)
表 
'Worktable'。扫描计数 0,逻辑读取 0 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。
表 
'workflowbase'。扫描计数 1,逻辑读取 293 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。
表 
'[zping.com]'。扫描计数 1,逻辑读取 1339 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。

 

   还有一种写法使用exists来取数据

select a.id,a.workflowid,a.operator ,a.stepid
from  dbo.[[zping.com]]] a where exists
(
select 'X' from workflowbase b where a.workflowid=b.id)
and operator='4028814111ad9dc10111afc134f10041'

 执行结果:  

(1327 行受影响)
表 
'[zping.com]'。扫描计数 1,逻辑读取 1339 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。
表 
'workflowbase'。扫描计数 1,逻辑读取 291 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。

 

   这里两着的IO次数,EXISTS比inner join少 2个IO, 对比执行计划成本不一样, 看看两着的差异:  

 

 

   这时我们发现使用EXISTS要比inner join效率稍微高一下。  
     2,使用Exists代替 in

      要求:编写workflowbase表中id不在表中dbo.[[zping.com]]]的行:      

       一般的写法:

select * from workflowbase 
 
where  id not in (
select  a.workflowid
from  dbo.[[zping.com]]] a )

执行结果:


(
1 行受影响)
表 
'Worktable'。扫描计数 0,逻辑读取 0 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。
表 
'[zping.com]'。扫描计数 5,逻辑读取 56952 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。
表 
'workflowbase'。扫描计数 3,逻辑读取 1589 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。
表 
'Worktable'。扫描计数 0,逻辑读取 0 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。

    使用Existsl来写:

select * from workflowbase b
 
where not exists(
select 'X'
from  dbo.[[zping.com]]] a where a.workflowid=b.id )

   看看执行结果

(1 行受影响)
表 
'Worktable'。扫描计数 0,逻辑读取 0 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。
表 
'[zping.com]'。扫描计数 3,逻辑读取 18984 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。
表 
'workflowbase'。扫描计数 3,逻辑读取 1589 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。

  两个io的差距:56952+1589=58541次 (使用IN)

                     18984+1589=20573次  (使用Exists)

   使用exists是in的2.8倍,查询性能提高很大。

 

 

   EXISTS 使查询更为迅速,因为RDBMS核心模块将在子查询的条件一旦满足后,立刻返回结果。

http://www.zping.com/  热爱祖国