DataBase撰写的日志

SQL蠕虫防火墙当前无法遏止的病毒

亚洲上网率最普及、上网率居世界第一的韩国,在WORM_SQLP1434.A 速客一号病毒爆发当日,境内三大电信公司代管服务器都收到攻击,网络因此全面瘫痪,病毒爆发后的第一个工作日,韩国高科技指数开盘,跌入历史最新低点。更令 IT 人员困扰的是,花大钱买防火墙及其他全功能安全解决方案,却无法遏止这种凌厉的攻势。由此,韩国证管会与军方首度对可提供主动防御措施的趋势科技企业安全防护战略 EPS(Enterprise Protection Strategy)表现了高度兴趣,趋势科技董事长张明正日前受邀前往相关单位,并即将在中国发表第二代 EPS 的超强防御功能。
  
  传统防毒软件无法自服务器硬盘里查出恶性程序踪迹
  
  WORM_SQLP1434.A 速客一号病毒不同于以往的病毒,它并不将病毒信息写入被攻击的硬盘,而只存在于内存。它不通过文件传染,而是借助跳板服务器的网络连接、攻击其它的服务器,直接从一台计算机内存传到另一台计算机内存。趋势科技中国区技术总监陈文佑形容说:“通过网络弱点攻击的病毒像是劫机犯,利用海关安检漏洞,顺利登机,而后在空中取得飞机行进方向的主导权。现行的解决方案却只能在地面等待其降落,束手无策。”一般以传统病毒码扫描文件的扫毒方式,无法自系统里根除病毒。
  
  反入侵侦测系统拘限于黑名单比对
  
  与防毒软件病毒码比对一样,目前大多数的入侵侦测系统,绝大多数通过数据库比对来辨识攻击行为。假设数据库无此攻击特征的资料,便很难侦测出该病毒。在新的攻击特征分析出来之前,网络便只能任黑客宰割。而目前流行的DDOS 攻击往往在最初阶段,便可达到攻击的颠峰。
  
  防火墙系统无法辨别漏洞攻击的正当性
  
  网络漏洞就像是病毒黑客争食的美味肉汁一样,往往转眼间就会成为威胁网络安全的一把利刃。更何况世界上的上百种操作系统、软件都有未被发现的臭虫,给黑客留下了可以发挥的空间。况且如果黑客攻击是利用正常的渠道,假设有人发现某个软件的Client 与Server 沟通的 Port 有攻击弱点可利用,黑客便可利用正常的通信协议,大摇大摆地进行破坏行为,因此防火墙甚至所谓的探测行为模式扫描 Heuristic scanning 也很难判断。
  
  单机版不敷使用,企业需全面升级服务器防毒软件,保护数据库安全
  
  一个信息化的企业,文件服务器是必备的信息设备,诸如部门间的资料共享、储存与备份等,而且文件服务器储存了公司非常重要的数字资产,比如财务报表、客户数据库或其它机密文件,可说是公司信息传递的重要核心。换言之,文件服务器是企业资料的宝库,它的安全性是不容置疑的。无庸置疑,单机版已经无法胜任保护企业网络的重责大任。
  
  趋势科技EPS自网络边界到企业核心全程捍卫
  
  趋势科技第二代 EPS(Enterprise Protection Strategy) 企业安全防护战略,从黑客发动攻击的初始阶段,除在网络边界立即自动升起防护罩外,还每隔 15 分钟通过中央空管平台在各节点自动安装隔离策略。
  
  欧洲网络安全机构Frost & Sullivan的产业分析师Jose Lopez表示:“趋势科技的EPS扩充方案,在与越来越复杂的网络环境及层出不穷的混和性病毒、恶意程序、垃圾邮件充斥的持续抗衡中,标志着一个重要的发展阶段。”他还表示,产品及服务的强化范围不只提升了整个病毒爆发周期中的点对点管理的便利性,它的多平台支持也解决了今天网络管理员所面对的一个大难题,为复合计算机环境提供了一个集中的管理。
  
  趋势科技EPS扩充方案中,所涵盖的一系列新观念、新服务及整体防毒架构,中文版预计于今年四月后陆续上市。

SQL Server 管理常用的SQL和T-SQL

1. 查看数据库的版本
select @@version

常见的几种SQL SERVER打补丁后的版本号:

8.00.194 Microsoft SQL Server 2000
8.00.384 Microsoft SQL Server 2000 SP1
8.00.532 Microsoft SQL Server 2000 SP2
8.00.760 Microsoft SQL Server 2000 SP3
8.00.818 Microsoft SQL Server 2000 SP3 w/ Cumulative Patch MS03-031
8.00.2039 Microsoft SQL Server 2000 SP4

2. 查看数据库所在机器操作系统参数
exec master..xp_msver

3. 查看数据库启动的参数
sp_configure

4. 查看数据库启动时间
select convert(varchar(30),login_time,120) from master..sysprocesses where spid=1

查看数据库服务器名和实例名
print ‘Server Name……………: ‘ + convert(varchar(30),@@SERVERNAME)
print ‘Instance………………: ‘ + convert(varchar(30),@@SERVICENAME)

5. 查看所有数据库名称及大小
sp_helpdb

重命名数据库用的SQL
sp_renamedb ‘old_dbname’, ‘new_dbname’

6. 查看所有数据库用户登录信息
sp_helplogins

查看所有数据库用户所属的角色信息
sp_helpsrvrolemember

修复迁移服务器时孤立用户时,可以用的fix_orphan_user脚本或者LoneUser过程

更改某个数据对象的用户属主
sp_changeobjectowner [@objectname =] ‘object’, [@newowner =] ‘owner’

注意: 更改对象名的任一部分都可能破坏脚本和存储过程。

把一台服务器上的数据库用户登录信息备份出来可以用add_login_to_aserver脚本

查看某数据库下,对象级用户权限
sp_helprotect

7. 查看链接服务器
sp_helplinkedsrvlogin

查看远端数据库用户登录信息
sp_helpremotelogin

8.查看某数据库下某个数据对象的大小
sp_spaceused @objname

还可以用sp_toptables过程看最大的N(默认为50)个表

查看某数据库下某个数据对象的索引信息
sp_helpindex @objname

还可以用SP_NChelpindex过程查看更详细的索引情况
SP_NChelpindex @objname

clustered索引是把记录按物理顺序排列的,索引占的空间比较少。
对键值DML操作十分频繁的表我建议用非clustered索引和约束,fillfactor参数都用默认值。

查看某数据库下某个数据对象的的约束信息
sp_helpconstraint @objname

9.查看数据库里所有的存储过程和函数
use @database_name
sp_stored_procedures

查看存储过程和函数的源代码
sp_helptext ‘@procedure_name’

查看包含某个字符串@str的数据对象名称
select distinct object_name(id) from syscomments where text like ‘%@str%’

创建加密的存储过程或函数在AS前面加WITH ENCRYPTION参数

解密加密过的存储过程和函数可以用sp_decrypt过程

10.查看数据库里用户和进程的信息
sp_who

查看SQL Server数据库里的活动用户和进程的信息
sp_who ‘active’

查看SQL Server数据库里的锁的情况
sp_lock

进程号1–50是SQL Server系统内部用的,进程号大于50的才是用户的连接进程.

spid是进程编号,dbid是数据库编号,objid是数据对象编号

查看进程正在执行的SQL语句
dbcc inputbuffer ()

推荐大家用经过改进后的sp_who3过程可以直接看到进程运行的SQL语句
sp_who3

检查死锁用sp_who_lock过程
sp_who_lock

11.查看和收缩数据库日志文件的方法

查看所有数据库日志文件大小
dbcc sqlperf(logspace)

如果某些日志文件较大,收缩简单恢复模式数据库日志,收缩后@database_name_log的大小单位为M
backup log @database_name with no_log
dbcc shrinkfile (@database_name_log, 5)

12.分析SQL Server SQL 语句的方法:

set statistics time {on | off}

set statistics io {on | off}

图形方式显示查询执行计划

在查询分析器->查询->显示估计的评估计划(D)-Ctrl-L 或者点击工具栏里的图形

文本方式显示查询执行计划

set showplan_all {on | off}

set showplan_text { on | off }

set statistics profile { on | off }


13.出现不一致错误时,NT事件查看器里出3624号错误,修复数据库的方法

先注释掉应用程序里引用的出现不一致性错误的表,然后在备份或其它机器上先恢复然后做修复操作

alter database [@error_database_name] set single_user

修复出现不一致错误的表

dbcc checktable(‘@error_table_name’,repair_allow_data_loss)

或者可惜选择修复出现不一致错误的小型数据库名

dbcc checkdb(‘@error_database_name’,repair_allow_data_loss)

alter database [@error_database_name] set multi_user

CHECKDB 有3个参数:

repair_allow_data_loss 包括对行和页进行分配和取消分配以改正分配错误、结构行或页的错误,
以及删除已损坏的文本对象,这些修复可能会导致一些数据丢失。
修复操作可以在用户事务下完成以允许用户回滚所做的更改。
如果回滚修复,则数据库仍会含有错误,应该从备份进行恢复。
如果由于所提供修复等级的缘故遗漏某个错误的修复,则将遗漏任何取决于该修复的修复。
修复完成后,请备份数据库。

repair_fast 进行小的、不耗时的修复操作,如修复非聚集索引中的附加键。
这些修复可以很快完成,并且不会有丢失数据的危险。

repair_rebuild 执行由 repair_fast 完成的所有修复,包括需要较长时间的修复(如重建索引)。
执行这些修复时不会有丢失数据的危险。

SQL Server 2000桌面引擎默认配置空口令漏洞

涉及程序:
SQL Server 2000 Desktop Engine,MSDE 1.0

描述:
MSDE/SQL Server 2000桌面引擎默认配置空口令漏洞

详细:
Microsoft SQL Server Desktop Engine(MSDE)是一款Microsoft用来提供数据库管理服
务的产品.

Microsoft SQL Server 2000 Desktop Engine是一款Microsoft分发的数据库SQL
SERVER2000共享数据引擎。

Microsoft SQL Server Desktop Engine(MSDE)和SQL Server 2000 Desktop Engine默
认配置存在漏洞,可导致远程攻击者以管理员权限访问数据库。

Microsoft SQL Server Desktop Engine(MSDE)和SQL Server 2000 Desktop Engine默
认配置其管理员密码为空,远程攻击者可以利用此漏洞以管理员权限访问数据库。

目前已经存在利用Microsoft SQL server和一些衍生产品MSDE和SQL Server 2000
Desktop Engine的默认空密码进行攻击的蠕虫。


受影响的系统:
Microsoft SQL Server 2000 Desktop Engine
Microsoft MSDE 1.0


解决方案:
Microsoft提供如下地址参考对SQL进行安全设置:

* Q322336 HOW TO: Verify and Change the System Administrator Password by Using
MSD
http://support.microsoft.com/default.aspx?scid=kb;EN-US;q322336

* Q321081 Visio Installation of MSDE Creates an ’sa’ Account with a Blank
Password
http://support.microsoft.com/default.aspx?scid=kb;EN-US;q321081



临时解决方案:
手工设置强壮的管理员口令



攻击方法:
暂无有效攻击代码

附加信息:

MS SQL SERVER2005 XML 最佳实践

SQL Server 2005 为 XML 数据处理提供了广泛支持。可以将 XML 值以本机方式存储在 xml 数据类型列中,后者可以根据 XML 架构的集合进行类型化,也可以保持非类型化状态。可以对 XML 列创建索引。此外,通过使用 XQuery 和 XML DML 可支持精细的数据操作。XML DML 是针对数据修改的扩展。

SQL Server 2000 和 SQLXML Web 版本提供了强大的 XML 数据管理功能。这些功能着重于关系数据和 XML 数据之间的映射。可以使用带批注的 XSD (AXSD) 来定义关系数据的 XML 视图,以提供以 XML 为中心的方法,该方法支持对 XML 数据执行大容量数据加载、查询和更新功能。Transact-SQL 扩展提供了一个以 SQL 为中心的方法,以便使用 FOR XML 将关系查询结果映射到 XML,以及使用 OPENXML 从 XML 生成关系视图。这些支持已在 SQL Server 2005 中进行了扩展。结合新添加的本机 XML 支持,SQL Server 2005 提供了一个强大的平台,以针对半结构化和非结构化数据管理开发功能丰富的应用程序。

本主题提供了 SQL Server 2005 中的 XML 数据建模和使用准则,包含下列两个部分:

数据建模

可以通过使用本机 xml 数据类型和拆分到表中的 XML,以多种方式在 SQL Server 2005 中存储 XML 数据。本主题提供了为对 XML 数据进行建模做出适当选择的准则。另外,还介绍了对 XML 数据创建索引、属性提升和 XML 实例的类型化。


使用

本部分讨论了与使用相关的主题,例如将 XML 数据加载到服务器和查询编译中的类型推理。本部分还解释和区分了密切相关的功能,并就如何适当使用这些功能提出了建议。这些均通过示例进行了说明。


数据建模
本部分概述了应使用 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 数据创建索引以实现高效的查询处理和良好的可伸缩性,并使用一流查询优化器。


您希望对 XML 数据进行 SOAP、ADO.NET 和 OLE DB 访问。


您希望使用数据库服务器的管理功能来管理 XML 数据。例如,这可能是备份、恢复和复制。


如果上述条件均不满足,最好将数据存储为非 XML 大型对象类型,如 [n]varchar(max) 或 varbinary(max)。

XML 存储选项
SQL Server 2005 中的 XML 存储选项包括:

采用 xml 数据类型的本机存储

数据以保留数据的 XML 内容的内部表示形式进行存储,XML 内容包括包容层次结构、文档顺序、元素和属性值等。具体来说,就是保留 XML 数据的 InfoSet 内容。有关 InfoSet 的详细信息,请访问 http://www.w3.org/TR/xml-infoset。InfoSet 内容并不是文本 XML 的精确副本,因为其中未保留下列信息:无关紧要的空格、属性顺序、命名空间前缀和 XML 声明。

对于类型化的 xml 数据类型(即绑定到 XML 架构的 xml 数据类型),后架构验证 InfoSet (PSVI) 将类型信息添加到 InfoSet,并以内部表示形式进行编码。这会显著提高分析速度。有关详细信息,请参阅 http://www.w3.org/TR/xmlschema-1 和 http://www.w3.org/TR/xmlschema-2 上的 W3C XML 架构规范。


在 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 数据的 InfoSet 内容。对 XML 列创建索引有利于提高查询性能。

示例:保留 XML 数据的精确副本
为了进行说明,假定政府条例要求您保留 XML 文档的精确文本副本。例如,这些文档可能为签署的文档、法律文档或股票交易单。您可能希望将文档存储在 [n]varchar(max) 列中。

查询时,先在运行时将数据转换为 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),该架构可能由外部伙伴提供。


数据中的顺序并不重要,或查询表数据不是递归的,或事先已知最大递归深度。


您希望使用 XPath 1.0 版通过 XML 视图查询和修改数据。


您希望通过 XML 视图来大容量加载 XML 数据,并将其分解到基础表。


相关示例包括显示为用于数据交换和 Web 服务的 XML 的关系数据。有关详细信息,请参阅 MSDN Online Library。

示例:使用带批注的 XML 架构 (AXSD) 对数据进行建模
为了进行说明,假定您具有希望将其作为 XML 处理的关系数据(如客户、订单和行项)。请使用 AXSD 在关系数据上定义 XML 视图。通过使用 XML 视图可以将 XML 数据大容量加载到表,以及使用 XML 视图查询和更新关系数据。如果必须在 SQL 应用程序不间断工作时与其他应用程序交换包含 XML 标记的数据,该模型很有用。

混合模型
通常,对于数据建模,适于结合使用关系列和 xml 数据类型列。可以将 XML 数据中的某些值存储在关系列中,而将其余或全部 XML 值存储在 XML 列中。这可获得更好的性能,您可以更好地控制对关系列创建的索引和锁定特征。

要存储在关系列中的值取决于您的工作负荷。例如,如果基于路径表达式 /Customer/@CustId 检索所有 XML 值,则将 CustId 属性的值提升到关系列并对其创建索引可以获得更快的查询性能。另一方面,如果您的 XML 数据是以非冗余方式广泛地分解为关系列中,则重新汇集的开销可能很大。

对于高度结构化的 XML 数据,例如,表的内容已转换为 XML,您可以将所有值映射到关系列,并且可能使用 XML 视图技术。

使用 xml 数据类型进行数据建模
本部分讨论有关本机 XML 存储的数据建模主题,包括对 XML 数据创建索引、属性提升和类型化的 xml 数据类型。

相同或不同的表
可以在包含其他关系列的表中,或在与主表具有外键关系的单独表中创建 xml 数据类型列。

如果满足下列条件之一,请在同一个表中创建 xml 数据类型列:

您的应用程序对 XML 列执行数据检索,并且不需要 XML 列的 XML 索引。


您希望对 xml 数据类型列生成 XML 索引,并且主表的主键与其聚集键相同。有关详细信息,请参阅“对 xml 数据类型列创建索引”。


如果满足下列条件,请在单独的表中创建 xml 数据类型列:

您希望对 xml 数据类型列生成 XML 索引,但主表的主键与其聚集键不同,或主表没有主键,或主表是一个堆(即没有聚集键)。如果主表已存在,可能会这样。


您不希望因为表中存在 XML 列而降低表扫描的速度。无论该列是存储在行内还是行外,都会占用空间。


XML 数据的粒度
XML 列中存储的 XML 数据的粒度对锁定至关重要,在一定程度上,对更新也很重要。SQL Server 对 XML 数据和非 XML 数据都使用相同的锁定机制。因此,行级锁定会导致锁定行中的所有 XML 实例。当粒度较大时,锁定大型 XML 实例以便进行更新会导致多用户情况下的吞吐量下降。另一方面,过度分解会丢失对象封装,并增加重新汇集开销。

对于良好的设计而言,重要的是保持数据建模要求与锁定和更新特征之间的平衡。但在 SQL Server 2005 中,实际存储的 XML 实例的大小并不十分重要。

例如,通过使用对部分二进制大型对象 (BLOB) 和部分索引更新(将存储的现有 XML 实例与其更新后的版本进行比较)的新支持,对 XML 实例进行更新。部分二进制大型对象 (BLOB) 更新在两个 XML 实例之间执行差异比较,并只更新差异之处。部分索引更新只修改那些必须在 XML 索引中更改的行。

非类型化、类型化和约束的 xml 数据类型
SQL Server 2005 xml 数据类型实现了 ISO SQL-2003 标准 xml 数据类型。因此,它可以在非类型化的 XML 列中存储格式正确的 XML 1.0 版的文档、具有文本节点和任意数量顶级元素的所谓的 XML 内容片段。系统将检查数据格式是否正确,但不要求将列绑定到 XML 架构,并且拒绝在扩展意义上格式不正确的数据。对于非类型化的 XML 变量和参数也是如此。

如果您有说明 XML 数据的 XML 架构,则可以将架构与 XML 列相关联以产生类型化的 XML。XML 架构用于验证数据,在编译查询和数据修改语句过程中执行比非类型化的 XML 更精确的类型检查,以及优化存储和查询处理。

在下列情况下,请使用非类型化的 xml 数据类型:

您没有对应于您的 XML 数据的架构。


您有架构,但不希望服务器验证数据。当应用程序在将数据存储到服务器之前执行客户端验证时,或临时存储根据架构确定无效的 XML 数据时,或在服务器上使用不受支持的架构组件(如 key/keyref)时,有时会出现这种情况。


在下列情况下,请使用类型化的 xml 数据类型:

您有对应于您的 XML 数据的架构,并且希望服务器根据 XML 架构验证 XML 数据。


您希望充分利用基于类型信息的存储和查询优化。


您希望在编译查询过程中更好地充分利用类型信息。


类型化的 XML 列、参数和变量可以存储 XML 文档或内容。但是,必须使用标志指定在声明时是存储文档还是存储内容。此外,必须提供 XML 架构集合。如果每个 XML 实例都刚好有一个顶级元素,请指定 DOCUMENT。否则,请使用 CONTENT。查询编译器在编译查询过程中的类型检查中使用 DOCUMENT 标志以推断单一的顶级元素。

除了对 XML 列进行类型化之外,还可以对类型化或非类型化的 xml 数据类型列使用关系(列或行)约束。在下列情况下,请使用约束:

无法在 XML 架构中表达业务规则。例如,花店的交货地址必须在其营业地点周围 50 英里之内。这可以编写为 XML 列的约束。约束可能涉及 xml 数据类型方法。


您的约束涉及表中的其他 XML 列或非 XML 列。例如,强制使 XML 实例中的客户 ID (/Customer/@CustId) 与 CustomerID 关系列中的值匹配。


文档类型定义 (DTD)
可以使用 XML 架构来对 xml 数据类型列、变量和参数进行类型化,但不能使用 DTD 进行此项操作。但是,内联 DTD 既可用于非类型化的 XML,也可用于类型化的 XML,以便提供默认值,并将实体引用替换为其扩展形式。

可以通过使用第三方工具将 DTD 转换为 XML 架构文档,然后将 XML 架构加载到数据库中。

对 xml 数据类型列创建索引
可以对 xml 数据类型列创建 XML 索引。它将对列中 XML 实例的所有标记、值和路径进行索引,从而提高查询性能。在下列情况下,您的应用程序可以从 XML 索引中获益:

对 XML 列进行查询在您的工作负荷中很常见。必须考虑数据修改过程中的 XML 索引维护开销。


XML 值相对较大,而检索的部分相对较小。生成索引避免了在运行时分析所有数据,并且索引查找有利于进行高效的查询处理。


XML 列的第一个索引是主 XML 索引。使用它时,可以对 XML 列创建三种类型的辅助 XML 索引,以提供常见种类的查询的速度,如以下部分所述。

主 XML 索引
这将对 XML 列中 XML 实例的所有标记、值和路径进行索引。基表(即包含 XML 列的表)的主键必须具有聚集索引。主键用于将索引行与基表中的行相关联。可从 XML 列中检索完整的 XML 实例,例如 SELECT *。查询使用主 XML 索引,并通过使用索引本身返回标量值或 XML 子树。

示例:创建主 XML 索引
在大多数示例中,使用包含非类型化的 XML 列的表 T (pk INT PRIMARY KEY, xCol XML)。可以采用简单的方式将这些示例扩展为类型化的 XML。有关如何使用类型化的 XML 的详细信息,请参阅 xml 数据类型。为简化起见,针对 XML 数据实例说明了查询,如下所示:

复制代码
Michael Howard David LeBlanc 39.99

以下语句对表 T 的 XML 列 xCol 创建 XML 索引(名为 idx_xCol):

复制代码
CREATE PRIMARY XML INDEX idx_xCol on T (xCol)

辅助 XML 索引
创建了主 XML 索引之后,您可能希望创建辅助 XML 索引来提高工作负荷中不同种类查询的速度。三种类型的辅助 XML 索引(即 PATH、PROPERTY 和 VALUE)分别用于优化基于路径的查询、自定义属性管理方案和基于值的查询。PATH 索引功能是按文档顺序对列中的所有 XML 实例生成各个 XML 节点的 (path, value) 对的 B+ 树。PROPERTY 索引功能是创建各个 XML 实例中 (PK, path, value) 对的聚集 B+ 树,其中 PK 是基表的主键。最后,VALUE 索引功能是按文档顺序对 XML 列中的所有 XML 实例创建每个节点的 (value, path) 对的 B+ 树。

下面是创建一个或多个这些索引的一些准则:

如果工作负荷对 XML 列大量使用路径表达式,则 PATH 辅助 XML 索引可能会提高工作负荷的处理速度。最常见的情况是在 Transact-SQL 的 WHERE 子句中对 XML 列使用 exist() 方法。


如果工作负荷通过使用路径表达式从单个 XML 实例中检索多个值,则在 PROPERTY 索引中聚集各个 XML 实例中的路径可能会很有用。这种情况通常出现在属性包方案中,此时提取对象的属性并且已知其主键值。


如果工作负荷涉及查询 XML 实例中的值,但不知道包含那些值的元素名称或属性名称,则您可能希望创建 VALUE 索引。这通常出现在 descendant 轴查找中,例如 //author[last-name="Howard"],其中 元素可以出现在层次结构的任何级别上。这种情况也出现在通配符查询中,例如 /book [@* = "novel"],其中查询将查找具有某个值为“novel”的属性的 元素。


示例:基于路径的查找
为了进行说明,假定以下查询在您的工作负荷中很常见:

复制代码
SELECT pk, xColFROM TWHERE xCol.exist (‘/book/@genre[.="novel"]‘) = 1

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

复制代码
CREATE XML INDEX idx_xCol_Path on T (xCol) USING XML INDEX idx_xCol FOR PATH

示例:提取对象的属性
例如,下面的查询从表 T 中的各行检索书的属性 genre、title 和 ISBN:

复制代码
SELECT xCol.value (‘(/book/@genre)[1]‘, ‘varchar(50)’), xCol.value (‘(/book/title/text())[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

示例:基于值的查询
在下面的查询中,descendant-or-self (//) 指定部分路径,以便基于 ISBN 值的查找从 VALUE 索引的使用中获益。

复制代码
SELECT xColFROM TWHERE xCol.exist (‘//book/@ISBN[. = "0-7356-1588-2"]‘) = 1

VALUE 索引的创建方式如下:

复制代码
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 列创建了全文索引后,下面的查询将检查 XML 值是否在书的标题中包含“custom”一词:

复制代码
SELECT * FROM T WHERE CONTAINS(xCol,’custom’) AND xCol.exist(‘/book/title/text()[contains(.,"custom")]‘) =1

contains() 方法使用全文索引来将文档中任何位置包含“custom”一词的 XML 值组合为一个子集。exist() 子句确保“custom”一词出现在书的标题中。

使用 contains() 的全文搜索与 XQuery contains() 具有不同语义。后者是子字符串匹配,前者是使用词干匹配的标记匹配。因此,如果搜索标题中包含“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 查询不对基础表执行全文搜索。

属性提升
如果主要是对少数元素和属性值进行查询,您可能希望将那些数量提升到关系列。检索整个 XML 实例,但只对一小部分 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 TADD ISBN AS dbo.udf_get_book_ISBN(xCol)

可以按通常的方式对计算列创建索引。

示例:对基于 xml 数据类型方法的计算列的查询
若要获得其 ISBN 为 0-7356-1588-2 的

复制代码
SELECT xColFROM TWHERE xCol.exist(‘/book/@ISBN[. = "0-7356-1588-2"]‘) = 1

可以重新编写对 XML 列的查询以使用计算列,如下所示:

复制代码
SELECT xColFROM TWHERE ISBN = ‘0-7356-1588-2′

您可以创建返回 xml 数据类型的用户定义函数,并使用用户定义函数来创建计算列。但是,不能对 XML 计算列创建 XML 索引。

创建属性表
您可能希望将 XML 数据中的某些多值属性提升到一个或多个表中,对那些表创建索引,并再次定向查询以使用这些表。典型的情况是少数属性占了大部分查询工作负荷。您可以执行下列操作:

创建一个或多个表来保存多值属性。您会发现可以很方便做到:每个表存储一个属性,以及在属性表中复制基表的主键以便与基表进行后向联接。


如果希望维护属性的相对顺序,必须为相对顺序引入一个单独的列。


为 XML 列创建触发器以维护属性表。在触发器中,执行下列操作之一:


使用 xml 数据类型方法(如 nodes() 和 value())来插入和删除属性表的行。


在公共语言运行时 (CLR) 中创建流式表值函数来插入和删除属性表的行。


编写对属性表进行 SQL 访问的查询和对基表中的 XML 列进行 XML 访问的查询,这些表之间通过主键联接起来。


示例:创建属性表
为了进行说明,假定您希望提升作者的名字。书有一个或多个作者,因此名字为多值属性。每个名字都存储在属性表的单独行中。在属性表中复制基表的主键以便进行后向联接。

复制代码
create table tblPropAuthor (propPK int, propAuthor varchar(max))

示例:创建用户定义函数以从 XML 实例生成行集
以下表值函数 udf_XML2Table 接受主键值和 XML 实例。它检索 元素的所有作者的名字,然后返回主键-名字对行集。

复制代码
create function udf_XML2Table (@pk int, @xCol xml)returns @ret_Table table (propPK int, propAuthor varchar(max))with schemabindingasbegin insert into @ret_Table select @pk, nref.value(‘.’, ‘varchar(max)’) from @xCol.nodes(‘/book/author/first-name’) R(nref) returnend

示例:创建触发器以填充属性表
插入触发器将行插入属性表:

复制代码
create trigger trg_docs_INS on T for insertas 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)

删除触发器根据删除行的主键值删除属性表中的行:

复制代码
create trigger trg_docs_DEL on T for deleteas declare @FK int select @FK = PK from deleted delete tblPropAuthor where propPK = @FK

更新触发器根据更新的 XML 实例删除属性表中的现有行,然后将新行插入属性表:

复制代码
create trigger trg_docs_UPDon Tfor updateasif 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

示例:查找其作者名字为“David”的 XML 实例
可以对 XML 列执行查询。此外,也可以在属性表中搜索名字“David”,然后与基表进行后向联接以返回 XML 实例。例如:

复制代码
SELECT xCol FROM T JOIN tblPropAuthor ON T.pk = tblPropAuthor.propPKWHERE tblPropAuthor.propAuthor = ‘David’

示例:使用 CLR 流式表值函数的解决方案
此解决方案包括下列步骤:

定义 CLR 类 SqlReaderBase,它实现 ISqlReader,并通过在 XML 实例上应用路径表达式来生成流式表值输出。


创建程序集和 Transact-SQL 用户定义函数来启动该 CLR 类。


通过使用用户定义函数来定义插入、更新和删除触发器,以维护属性表。


若要如此,首先创建流式 CLR 函数。xml 数据类型显示为 ADO.NET 中的托管类 SqlXml,支持返回 XmlReader 的 CreateReader() 方法。

注意:
本部分中的示例代码使用了 XPathDocument 和 XPathNavigator。这些都强制要求您将所有 XML 文档加载到内存中。如果您要在您的应用程序中使用类似代码来处理多个大型 XML 文档,此代码并不可伸缩。而是应尽可能保持较小的内存分配并使用流式接口。有关性能的详细信息,请参阅 Architecture of CLR Integration。

复制代码
public class c_streaming_xml_tvf { public static ISqlReader streaming_xml_tvf (SqlXml xmlDoc, string pathExpression) { return (new TestSqlReaderBase (xmlDoc, pathExpression)); }}// Class that implements ISqlReaderpublic class TestSqlReaderBase : ISqlReader {XPathNodeIterator m_iterator; public SqlChars FirstName;// Metadata for current resultsetprivate 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; }}

然后,创建程序集和与 CLR 函数 streaming_xml_tvf 对应的 Transact-SQL 用户定义函数 SQL_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 schemabindingasbegin insert into @ret_Table select @pk, FirstName FROM SQL_streaming_xml_tvf (@xCol, ‘/book/author/first-name’) returnend

最后,定义触发器,如“创建触发器以填充属性表”示例中所示,但用 CLR_udf_XML2Table 函数替换了 udf_XML2Table。以下示例中显示了插入触发器:

复制代码
create trigger CLR_trg_docs_INS on T for insertas 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)

删除触发器与非 CLR 版本相同。但是,更新触发器只是用 CLR_udf_XML2Table() 函数替换了函数 udf_XML2Table()。

XML 架构集合
XML 架构集合是关系架构作用域内的元数据实体。它包含一个或多个可能相关(如通过 )也可能无关的 XML 架构。XML 架构集合中各个 XML 架构通过使用其目标命名空间来标识。

XML 架构集合是通过使用 CREATE XML SCHEMA COLLECTION (Transact-SQL) 语法并提供一个或多个 XML 架构来创建。可以通过使用 ALTER XML SCHEMA COLLECTION (Transact-SQL) 语法,将多个 XML 架构组件添加到现有 XML 架构中,并将多个架构添加到 XML 架构集合中。可以通过使用 SQL Server 2005 中的安全模式像任何 SQL 对象那样保证 XML 架构集合的安全。

多类型化列
XML 架构集合 C 根据多个 XML 架构对 XML 列 xCol 进行类型化。此外,DOCUMENT 标志和 CONTENT 标志指定是否可以将 XML 树或片段分别存储在 xCol 列中。

对于 DOCUMENT,每个 XML 实例都指定实例中顶级元素的目标命名空间,XML 实例根据它来进行类型化和验证。另一方面,对于 CONTENT,每个顶级元素都可以指定 C 中任何一个目标命名空间。XML 实例根据实例中存在的所有目标命名空间来进行验证和类型化。

架构演变
XML 架构集合用于对 XML 列、变量和参数进行类型化。它提供了 XML 架构演变的机制。为了进行说明,假定您将具有目标命名空间 BOOK-V1 的 XML 架构添加到 XML 架构集合 C 中。使用 C 类型化的 XML 列 xCol 可以存储符合 BOOK-V1 架构的 XML 数据。

然后,假定某个应用程序希望用新的架构组件(如复杂类型定义和顶级元素声明)扩展 XML 架构。可以将这些新的架构组件添加到 BOOK-V1 架构中,并且不需要重新验证 xCol 列中的现有 XML 数据。

假定该应用程序以后希望提供新版本的 XML 架构,并且它选择目标命名空间 BOOK-V2。可以将此 XML 架构添加到 C 中。XML 列可以存储 BOOK-V1 和 BOOK-V2 的实例,并且可以对符合这些命名空间的 XML 实例执行查询和数据修改。

加载 XML 数据
将 XML 数据从 SQL Server 2000 传输到 SQL Server 2005
您可以采用多种方式将 XML 数据传输到 SQL Server 2005。例如:

如果将数据存储在 SQL Server 2000 数据库中的 [n]text 或 image 中,则可以使用 SQL Server Integration Services 将表导入 SQL Server 2005 数据库中。使用 ALTER TABLE 语句将列类型更改为 XML。


可以使用 bcp out 从 SQL Server 2000 大容量复制数据,然后使用 bcp in 将数据大容量插入 SQL Server 2005 数据库中。


如果将数据存储在 SQL Server 2000 数据库的关系列中,请创建具有 [n]text 列和(可选)用于行标识符的主键列的新表。使用客户端编程检索在服务器上通过 FOR XML 生成的 XML,并将其写入 [n]text 列。然后,使用上述方法将数据传输到 SQL Server 2005 数据库中。您可以选择将 XML 直接写入 SQL Server 2005 数据库中的 XML 列。


示例:将列类型更改为 XML
假定您希望将 R 表中的 [n]text 列或 image 列 XYZ 的类型更改为非类型化的 XML。以下语句执行此类型更改:

复制代码
ALTER TABLE R ALTER COLUMN XYZ XML

如果需要,可以通过指定 XML 架构集合将目标为类型化为 XML。


大容量加载 XML 数据
可以通过使用 SQL Server 的大容量加载功能(如 bcp)将 XML 数据大容量加载到服务器中。通过使用 OPENROWSET 可以将文件中的数据加载到 XML 列中。以下示例说明了这一点。

示例:从文件中加载 XML
此示例显示了如何在表 T 中插入行。从文件 C:\MyFile\xmlfile.xml 中将 XML 列的值作为 CLOB 加载,并为整数列提供了值 10。

复制代码
INSERT INTO TSELECT 10, xColFROM (SELECT * FROM OPENROWSET (BULK ‘C:\MyFile\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,它存储为没有显式 XML 声明的 varchar(max)。以下语句添加编码为“iso8859-1”的 XML 声明,将 XML 文档串联起来,将结果转换为 varbinary(max) 以便保留字节表示形式,最终将其转换为 XML。这样,XML 处理器就可以根据指定的编码“iso8859-1”分析数据,并为字符串值生成相应的 UTF-16 表示形式。

复制代码
SELECT CAST( CAST ((‘‘+ vcdoc) AS VARBINARY (MAX)) AS XML)

XQuery 和类型推理
Transact-SQL 中嵌入的 XQuery 语言支持查询 xml 数据类型。该语言正在由万维网联盟 (W3C) 开发,Microsoft 的所有主要数据库供应商都参与其中。它包含了 XPath 2.0 版作为导航语言。同时,还提供了针对 xml 数据类型的数据修改语言构造。有关 SQL Server 中支持的 XQuery 构造、函数和运算符的详细信息,请参阅针对 xml 数据类型的 XQuery 函数。

错误模型
语法不正确的 Xquery 表达式和 XML DML 语句会返回编译错误。编译阶段会检查 XQuery 表达式和 DML 语句的静态类型正确性,并针对类型化的 XML 使用 XML 架构进行类型推理。如果表达式在运行时由于类型安全冲突而失败,会引起静态类型错误。静态错误的示例包括将字符串添加到整数,以及在不存在的节点中查询类型化的数据。

与 W3C 标准有所不同的是,XQuery 运行时错误被转换为空序列。这些序列根据调用上下文,可以作为空 XML 或 NULL 传播到查询结果。

通过显式转换为正确的类型,用户可以解决静态错误的问题,尽管运行时转换错误将被转换为空序列。

下列部分详细介绍了类型检查。

单一性检查
如果编译器无法确定是否在运行时保证单一性,则要求单一性的位置步骤、函数参数和运算符将返回错误。此问题经常出现在非类型化数据上。例如,对属性的查找需要单一的父元素。选择单个父节点的序号即可满足需要。计算 node()-value() 组合以提取属性值可能不需要指定序号。如以下示例中所示。

示例:已知单一性
在此示例中,nodes() 方法为每个 元素生成一个单独的行。对 节点进行计算的 value() 方法提取 @genre 值,其作为属性,具有单一性。

复制代码
SELECT nref.value(‘@genre’, ‘varchar(max)’) LastNameFROM T CROSS APPLY xCol.nodes(‘//book’) AS R(nref)

XML 架构用于对类型化的 XML 进行类型检查。如果某个节点指定为 XML 架构中单一的节点,则编译器将使用该信息,并且不会发生任何错误。否则,需要选择单个节点的序号。特别的情况是,使用 descendant-or-self (//)(如在 /book//title 中)会丢失 元素的单一性基数推理,即使 XML 架构指定其如此。因此,您应该将其重写为 (/book//title)[1]。 </p> <p>对于类型检查,务必注意 //first-name[1] 和 (//first-name)[1] 之间的差异。前者返回一组 <first-name> 节点,其中每个节点都是其同级节点间最左侧的 <first-name> 节点。后者返回 XML 实例中按文档顺序的第一个单一的 <first-name> 节点。</p> <p>示例:使用 value()<BR>下面对非类型化的 XML 列的查询导致发生静态的编译错误。这是因为 value() 希望将一个单一节点作为第一个参数,而编译器无法确定在运行时是否将仅有一个 <last-name> 节点:</p> <p> 复制代码 <BR>SELECT xCol.value(‘//author/last-name’, ‘nvarchar(50)’) LastNameFROM T<BR> </p> <p>可以考虑下面的解决办法:</p> <p> 复制代码 <BR>SELECT xCol.value(‘//author/last-name[1]‘, ‘nvarchar(50)’) LastNameFROM T<BR> </p> <p>但是,该解决办法不解决错误,因为在每个 XML 实例中可能会有多个 <author> 节点。采用下面的重写代码可以解决问题:</p> <p> 复制代码 <BR>SELECT xCol.value(‘(//author/last-name/text())[1]‘, ‘nvarchar(50)’) LastNameFROM T<BR> </p> <p>此查询返回每个 XML 实例中第一个 <last-name> 元素的值。</p> <p>parent 轴<BR>如果无法确定节点的类型,它将成为 anyType。这不会隐式转换为任何其他类型。在使用 parent 轴(如 xCol.query(‘/book/@genre/../price’))进行导航的过程中,尤其会发生这种情况。父节点类型确定为 anyType。在 XML 架构中,也可以将元素定义为 anyType。在这两种情况下,丢失更为精确的类型信息经常会导致发生静态类型错误,并需要将原子值显式转换为其特定类型。</p> <p>Data()、text() 和 string() 取值函数<BR>XQuery 有一个从节点提取类型化标量值的函数 fn:data()、一个返回文本节点的节点测试 text(),以及一个返回节点的字符串值的函数 fn:string()。它们的用法容易混淆。以下是在 SQL Server 2005 中正确使用它们的准则。使用 XML 实例 <age>12</age> 进行说明。 </p> <p>非类型化的 XML:路径表达式 /age/text() 返回文本节点“12”。函数 fn:data(/age) 返回字符串值“12”,fn:string(/age) 也是如此。</p> <p><BR>类型化的 XML:对于任何简单的类型化的 <age> 元素,表达式 /age/text() 都返回静态错误。另一方面,fn:data(/age) 返回整数 12。fn:string(/age) 产生字符串“12”。</p> <p><BR>联合类型的函数和运算符<BR>由于类型检查,联合类型要求进行小心地处理。下列示例中说明了其中两个问题。</p> <p>示例:联合类型的函数<BR>例如,以下联合类型的 <r> 的元素定义:</p> <p> 复制代码 <BR><xs:element name="r"><xs:simpleType> <xs:union memberTypes="xs:int xs:float xs:double"/></xs:simpleType></xs:element><BR> </p> <p>在 XQuery 上下文中,“average”函数 fn:avg (//r) 返回静态错误,因为 XQuery 编译器无法对 fn:avg() 的参数中的 <r> 元素的不同类型(xs:int、xs:float 或 xs:double)的值求和。为了解决此问题,请将函数调用重写为 fn:avg(for $r in //r return $r cast as xs:double ?)。 </p> <p>示例:联合类型的运算符<BR>加法运算(“+”)要求使用精确类型的操作数。因此,表达式 (//r)[1] + 1 返回静态错误,该错误包含前面所述的 <r> 元素的类型定义。一个解决方法是将其重写为 (//r)[1] cast as xs:int? +1,其中“?”表示取值 0 或 1。SQL Server 2005 要求带有“?”的“cast as”,因为任何转换都可能由于运行时错误导致产生空序列。</p> <p>Value()、Nodes() 和 OpenXML()<BR>您可以在 SELECT 子句中对 xml 数据类型使用多个 value() 方法以生成所提取值的行集。nodes() 方法为可用于其他查询的每个所选节点生成一个内部引用。生成行集时,如果行集有多个列且用于生成行集的路径表达式比较复杂,结合使用 nodes() 和 value() 方法可能会更有效。 </p> <p>nodes() 方法生成特定 xml 数据类型的实例,每个实例的上下文都设置为不同的所选节点。这种 XML 实例支持 query()、value()、nodes() 和 exist() 方法,并可用于 count(*) 聚合。所有其他用法都会导致错误。</p> <p>示例:使用 nodes()<BR>假定您希望提取作者的名字和姓氏,而名字不是“David”。此外,您希望提取该信息作为一个包含两列 FirstName 和 LastName 的行集。通过使用 nodes() 方法和 value() 方法便可以完成该操作,如下所示:</p> <p> 复制代码 <BR>SELECT nref.value(‘(first-name/text())[1]‘, ‘nvarchar(50)’) FirstName, nref.value(‘(last-name/text())[1]‘, ‘nvarchar(50)’) LastNameFROM T CROSS APPLY xCol.nodes(‘//author’) AS R(nref)WHERE nref.exist(‘first-name[. != "David"]‘) = 1<BR> </p> <p>在此示例中,nodes(‘//author’) 生成一个由对每个 XML 实例的 <author> 元素引用组成的行集。通过计算与那些引用相关的 value() 方法,即可获得作者的名字和姓氏。</p> <p>SQL Server 2000 提供了通过使用 OpenXml() 从 XML 实例生成行集的功能。您可以指定行集的关系架构,以及如何将 XML 实例中的值映射到行集中的列。 </p> <p>示例:对 xml 数据类型使用 OpenXml()<BR>可以通过使用 OpenXml() 重写上一个示例中的查询,如下所示。方法是创建一个游标,该游标将每个 XML 实例读取到 XML 变量,然后向其应用 OpenXML:</p> <p> 复制代码 <BR>DECLARE name_cursor CURSORFOR SELECT xCol FROM TOPEN name_cursorDECLARE @xmlVal XMLDECLARE @idoc intFETCH NEXT FROM name_cursor INTO @xmlValWHILE (@@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 @xmlValENDCLOSE name_cursorDEALLOCATE name_cursor <BR> </p> <p>OpenXml() 创建一个内存中的表示形式,并且使用工作表而不是查询处理器。它依赖于 MSXML 3.0 版的 XPath 1.0 版处理器,而不是 XQuery 引擎。工作表不在对 OpenXml() 的多个调用间共享(即使是在同一个 XML 实例上)。这就限制了它的可伸缩性。在未指定 WITH 子句时,可以通过 OpenXml() 访问 XML 数据的边缘表格式。另外,也可以通过它在单独的“overflow”列中使用其余的 XML 值。</p> <p>将 nodes() 和 value() 函数结合起来可有效地使用 XML 索引。因此,与 OpenXml 相比,这种结合有更高的可伸缩性。</p> <p>使用 FOR XML 从行集生成 XML<BR>可以通过在 FOR XML 中使用新的 TYPE 指令,从行集生成 xml 数据类型实例。 </p> <p>可以将结果赋给 xml 数据类型列、变量或参数。另外,可以嵌套 FOR XML 以生成任何层次结构。这使得嵌套的 FOR XML 比 FOR XML EXPLICIT 更容易编写,但对于较深的层次结构,它的性能可能不太好。FOR XML 还引入了新的 PATH 模式。这个新模式指定某个列的值在 XML 树中的路径。 </p> <p>可以使用新的 FOR XML TYPE 指令,采用 SQL 语法来定义关系数据上的只读 XML 视图。可以使用 SQL 语句和嵌入式 XQuery 查询该视图,如下面的示例所示。另外,您还可以在存储过程中引用这些 SQL 视图。</p> <p>示例:返回生成的 xml 数据类型的 SQL 视图<BR>以下 SQL 视图定义对关系列 pk 和从 XML 列中检索到的书作者创建 XML 视图:</p> <p> 复制代码 <BR>CREATE VIEW V (xmlVal) ASSELECT pk, xCol.query(‘/book/author’)FROM TFOR XML AUTO, TYPE<BR> </p> <p>V 视图包含一个行,该行只有一个 XML 类型的 columnxmlVal。可以查询像常规 xml 数据类型实例那样对它进行查询。例如,下面的查询返回名字为“David”的作者:</p> <p> 复制代码 <BR>SELECT xmlVal.query(‘//author[first-name = "David"]‘)FROM V<BR> </p> <p>SQL 视图定义与使用带批注的架构创建的 XML 视图有些相似。但二者之间存在重要的差异。SQL 视图定义是只读的,且必须使用嵌入式 XQuery 来操作。XML 视图是通过使用带批注的架构创建的。此外,SQL 视图在应用 XQuery 表达式之前具体化 XML 结果,而对 XML 视图的 XPath 查询是对基础表计算 SQL 查询。 </p> <p>添加业务逻辑<BR>可以采用多种方式将业务逻辑添加到 XML 数据中: </p> <p>您可以编写行或列约束,以在插入和修改 XML 数据时强制实施特定于域的约束。</p> <p><BR>您可以在 XML 列上编写插入或更新列中的值时激发的触发器。该触发器可以包含特定于域的验证规则或填充属性表。</p> <p><BR>您可以采用托管代码编写 SQLCLR 函数并将向其传递 XML 值,并且使用 System.Xml 命名空间提供的 XML 处理功能。例如,将 XSL 转换应用到 XML 数据。另外,您可以将 XML 反序列化为一个或多个托管类,并使用托管代码对它们进行操作。 </p> <p><BR>您可以编写 Transact-SQL 存储过程和函数,对 XML 列进行处理以满足业务需要。</p> <p><BR>示例:应用 XSL 转换<BR>例如,CLR 函数 TransformXml(),它接受 xml 数据类型实例和文件中存储的 XSL 转换,将转换应用到 XML 数据,然后在结果中返回转换的 XML。以下是用 C# 编写的主干函数:</p> <p> 复制代码 <BR>public static SqlXml TransformXml (SqlXml XmlData, string xslPath) { // Load XSL transformation XslCompiledTransform xform = new XslCompiledTransform(); XPathDocument xslDoc = new XPathDocument (xslPath); xform.Load(xslDoc); // Load XML data XPathDocument xDoc = new XPathDocument (XmlData.CreateReader()); // Return the transformed value MemoryStream xsltResult = new MemoryStream(); xform.Transform(xDoc, null, xsltResult); SqlXml retSqlXml = new SqlXml(xsltResult); return (retSqlXml);} <BR> </p> <p>在注册了程序集并创建了与 TransformXml() 对应的用户定义 Transact-SQL 函数 SqlXslTransform() 之后,就可以从 Transact-SQL 中调用该函数,如下面的查询所示:</p> <p> 复制代码 <BR>SELECT SqlXslTransform (xCol, ‘C:\MyFile\xsltransform.xsl’)FROM TWHERE xCol.exist(‘/book/title/text()[contains(.,"custom")]‘) =1<BR> </p> <p>查询结果包含转换的 XML 的行集。 </p> <p>SQLCLR 扩展了这样一些功能:将 XML 数据分解到多个表或属性提升,以及通过使用 System.Xml 命名空间中的托管类查询 XML 数据。有关详细信息,请参阅 SQL Server 联机丛书和 .Net Framework SDK 文档。 </p> <p>跨域查询<BR>当您的数据同时保存在关系和 xml 数据类型列中时,您可能希望编写将关系和 XML 数据处理结合起来的查询。例如,您可以通过使用 FOR XML 将关系列和 XML 列中的数据转换为 xml 数据类型实例,并使用 XQuery 对其进行查询。相反,您可以从 XML 值生成行集,并使用 Transact-SQL 对其进行查询。</p> <p>一种编写跨域查询的更方便且有效的方法是在 XQuery 或 XML DML 表达式中使用 SQL 变量或列的值:</p> <p>您可以在 XQuery 或 XML DML 表达式中,通过使用 sql:variable() 来使用 SQL 变量的值。</p> <p><BR>您可以在 XQuery 或 XML DML 表达式中,通过使用 sql:column() 来使用关系列中的值。</p> <p><BR>通过这两种方法,应用程序可以对查询进行参数化,如以下示例所示。但在 sql:variable() 和 sql:column() 中不允许使用 XML 和用户定义类型。</p> <p>示例:使用 sql:variable() 的跨域查询<BR>下面的查询是“示例:对基于 xml 数据类型方法的计算列的查询”中所示查询的修改版本。在下面的版本中,使用 SQL 变量 @isbn 传入了此特定 ISBN。通过将常量替换为 sql:variable(),可以使用查询来搜索任何 ISBN,而不仅是 ISBN 为 0-7356-1588-2 的书。</p> <p> 复制代码 <BR>DECLARE @isbn varchar(20)SET @isbn = ‘0-7356-1588-2′SELECT xColFROM TWHERE xCol.exist (‘/book/@ISBN[. = sql:variable("@isbn")]‘) = 1<BR> </p> <p>可以用相似的方式使用 sql:column(),它提供了其他好处。可以使用列的索引来提高效率,这由基于开销的查询优化器决定。另外,计算列可以存储提升的属性。 </p> <p>用于本机 XML 支持的目录视图<BR>目录视图用于提供有关 XML 用法的元数据信息。以下部分中讨论了其中某些视图。 </p> <p>XML 索引<BR>XML 索引项位于目录视图 sys.indexes 中,索引“type”为 3。名称列包含 XML 索引的名称。 </p> <p>另外,XML 索引还记录在目录视图 sys.xml_indexes 中。此视图包含 sys.indexes 的所有列和对 XML 索引有用的某些特定列。secondary_type 列中的值 NULL 表示主 XML 索引;值“P”、“R”和“V”分别表示 PATH、PROPERTY 和 VALUE 辅助 XML 索引。 </p> <p>可以在表值函数 sys.dm_db_index_physical_stats 中找到 XML 索引的空间使用情况。它提供了所有索引类型的相关信息,例如,占用的磁盘页数、平均行大小(字节)和记录数。其中也包括 XML 索引。对于每个数据库分区,都提供此信息。XML 索引使用基表的相同分区方案和分区函数。 </p> <p>检索 XML 架构集合<BR>XML 架构集合在目录视图 sys.xml_schema_collections 中枚举出来。XML 架构集合“sys”由系统定义。它包含无需显式加载即可在所有用户定义的 XML 架构集合中使用的预定义命名空间。此列表包含 xml、xs、xsi、fn 和 xdt 的命名空间。另外两个目录视图是 sys.xml_schema_namespaces(它枚举每个 XML 架构集合中的所有命名空间)和 sys.xml_components(它枚举每个 XML 架构中的所有 XML 架构组件)。 </p> <p>内置函数 XML_SCHEMA_NAMESPACE(schemaName、XmlSchemacollectionName、namespace-uri)生成 xml 数据类型实例。此实例包含在 XML 架构集合中所包含架构(预定义的 XML 架构除外)的 XML 架构片段。</p> <p>可以按下列方式枚举 XML 架构集合的内容: </p> <p>编写对 XML 架构集合的相应目录视图的 Transact-SQL 查询。 </p> <p><BR>使用内置函数 XML_SCHEMA_NAMESPACE()。您可以对此函数的输出应用 xml 数据类型方法。但不能修改基础 XML 架构。 </p> <p><BR>这些在下列示例中进行了说明。</p> <p>示例:枚举 XML 架构集合中的 XML 命名空间<BR>对 XML 架构集合“myCollection”使用下面的查询:</p> <p> 复制代码 <BR>SELECT XSN.nameFROM sys.xml_schema_collections XSC JOIN sys.xml_schema_namespaces XSN ON (XSC.xml_collection_id = XSN.xml_collection_id)WHERE XSC.name = ‘myCollection’ <BR> </p> <p>示例:枚举 XML 架构集合的内容<BR>以下语句枚举关系架构 dbo 中的 XML 架构集合“myCollection”的内容。</p> <p> 复制代码 <BR>SELECT XML_SCHEMA_NAMESPACE (N’dbo’, N’myCollection’)<BR> </p> <p>通过将目标命名空间指定为 XML_SCHEMA_NAMESPACE() 的第三个参数,可以按 xml 数据类型实例的形式获得集合中的单个 XML 架构。如下面的示例所示。</p> <p>示例:从 XML 架构集合输出指定的架构<BR>以下语句从关系架构 dbo 中的 XML 架构集合“myCollection”输出目标命名空间为“http://www.microsoft.com/books”的 XML 架构。</p> <p> 复制代码 <BR>SELECT XML_SCHEMA_NAMESPACE (N’dbo’, N’myCollection’, N’http://www.microsoft.com/books’)<BR> </p> <p>查询 XML 架构<BR>可以按下列方式查询加载到 XML 架构集合的 XML 架构: </p> <p>编写对 XML 架构命名空间的目录视图的 Transact-SQL 查询。 </p> <p><BR>创建包含 xml 数据类型列的表以存储 XML 架构,并将这些架构加载到 XML 类型系统中。可以通过使用 xml 数据类型方法查询 XML 列。另外,还可以对此列生成 XML 索引。但是,使用此方法时,应用程序必须保持 XML 列中存储的 XML 架构和 XML 类型系统之间的一致性。例如,如果从 XML 类型系统中删除 XML 架构命名空间,还必须从表中删除它以保持一致性。</p> </div> <div class="post-tags"> <a href="http://blog.zxlm.cn/tag/access" rel="tag" title="Access (481 topics)">Access</a>, <a href="http://blog.zxlm.cn/tag/database" rel="tag" title="DataBase (480 topics)">DataBase</a>, <a href="http://blog.zxlm.cn/tag/sql-server" rel="tag" title="SQL Server (487 topics)">SQL Server</a> </div> </div> <!-- /post --><!-- post --> <div id="post-1702" class="post-1702 post hentry category-database tag-access tag-database tag-sql-server clearfix preview-full"> <h2 class="title"><a href="http://blog.zxlm.cn/2006/05/sql-server%e6%95%b0%e6%8d%ae%e5%ba%93%e4%b8%ad%e4%bd%bf%e7%94%a8%e8%a7%a6%e5%8f%91%e5%99%a8%e7%bb%8f%e9%aa%8c.html" rel="bookmark" title="永久链接: SQL Server数据库中使用触发器经验">SQL Server数据库中使用触发器经验</a></h2> <div class="post-date"> <p class="day">五 2nd</p> </div> <div class="post-info clearfix "> <p class="author alignleft">由<a href="http://blog.zxlm.cn/author/admin/" title="由admin撰写的日志 ">admin</a>分类在<a href="http://blog.zxlm.cn/category/database">DataBase</a> </p> <p class="comments alignright"><a href="http://blog.zxlm.cn/2006/05/sql-server%e6%95%b0%e6%8d%ae%e5%ba%93%e4%b8%ad%e4%bd%bf%e7%94%a8%e8%a7%a6%e5%8f%91%e5%99%a8%e7%bb%8f%e9%aa%8c.html#comments" class="no comments">没有评论</a></p> </div> <div class="post-content clearfix"> <p>触发器是数据库应用中的重用工具,它的应用很广泛。这几天写一个化学数据统计方面的软件,需要根据采样,自动计算方差,在这里,我使用了触发器。可以定义一个无论何时用INSERT语句向表中插入数据时都会执行的触发器。当触发INSERT触发器时,新的数据行就会被插入到触发器表和inserted表中。inserted表是一个逻辑表,它包含了已经插入的数据行的一个副本。inserted表包含了INSERT语句中已记录的插入动作。inserted表还允许引用由初始化INSERT语句而产生的日志数据。触发器通过检查inserted表来确定是否执行触发器动作或如何执行它。inserted表中的行总是触发器表中一行或多行的副本。 </p> <p>日志记录了所有修改数据的动作(INSERT、UPDATE和DELETE语句),但在事务日志中的信息是不可读的。然而,inserted表允许你引用由INSERT语句引起的日志变化,这样就可以将插入数据与发生的变化进行比较,来验证它们或采取进一步的动作。也可以直接引用插入的数据,而不必将它们存储到变量中。 </p> <p>示例 </p> <p>在本例中,将创建一个触发器。无论何时订购产品(无论何时向Order Details表中插入一条记录),这个触发器都将更新Products表中的一列(UnitsInStock)。用原来的值减去订购的数量值即为新值。 </p> <p><CENTER><CCID_NOBR /></p> <p><PRE><CCID_CODE />USE Northwind</p> <p>CREATE TRIGGER OrdDet_Insert</p> <p>ON [Order Details]</p> <p>FOR INSERT</p> <p>AS</p> <p>UPDATE P SET</p> <p>UnitsInStock = P.UnitsInStock – I.Quantity</p> <p>FROM Products AS P INNER JOIN Inserted AS I</p> <p>ON P.ProductID = I.ProductID</CCID_CODE /></PRE></TR></TBODY></TABLE></CCID_NOBR /></CENTER></p> <p><P style="TEXT-INDENT: 2em">DELETE触发器的工作过程 </p> <p>当触发DELETE触发器后,从受影响的表中删除的行将被放置到一个特殊的deleted表中。deleted表是一个逻辑表,它保留已被删除数据行的一个副本。deleted表还允许引用由初始化DELETE语句产生的日志数据。 </p> <p>使用DELETE触发器时,需要考虑以下的事项和原则: </p> <p>·当某行被添加到deleted表中时,它就不再存在于数据库表中;因此,deleted表和数据库表没有相同的行。 </p> <p>·创建deleted表时,空间是从内存中分配的。deleted表总是被存储在高速缓存中。 </p> <p>·为DELETE动作定义的触发器并不执行TRUNCATE TABLE语句,原因在于日志不记录TRUNCATE TABLE语句。 </p> <p>示例 </p> <p>在本例中,将创建一个触发器,无论何时删除一个产品类别(即从Categories表中删除一条记录),该触发器都会更新Products表中的Discontinued列。所有受影响的产品都标记为1,标示不再使用这些产品了。 </p> <p><CENTER><CCID_NOBR /></p> <p><PRE><CCID_CODE />USE Northwind</p> <p>CREATE TRIGGER Category_Delete</p> <p>ON Categories</p> <p>FOR DELETE</p> <p>AS</p> <p>UPDATE P SET Discontinued = 1</p> <p>FROM Products AS P INNER JOIN deleted AS d</p> <p>ON P.CategoryID = d.CategoryID</CCID_CODE /></PRE></TR></TBODY></TABLE></CENTER></p> <p><DIV class=content></p> <p><P></p> <p>UPDATE触发器的工作过程 </p> <p>可将UPDATE语句看成两步操作:即捕获数据前像(before image)的DELETE语句,和捕获数据后像(after image)的INSERT语句。当在定义有触发器的表上执行UPDATE语句时,原始行(前像)被移入到deleted表,更新行(后像)被移入到inserted表。 </p> <p>触发器检查deleted表和inserted表以及被更新的表,来确定是否更新了多行以及如何执行触发器动作。 </p> <p>可以使用IF UPDATE语句定义一个监视指定列的数据更新的触发器。这样,就可以让触发器容易的隔离出特定列的活动。当它检测到指定列已经更新时,触发器就会进一步执行适当的动作,例如发出错误信息指出该列不能更新,或者根据新的更新的列值执行一系列的动作语句。 </p> <p>语法 </p> <p><CENTER><CCID_NOBR /></p> <p><PRE><CCID_CODE />IF UPDATE (<br /> <column_name>)</CCID_CODE /></PRE></TR></TBODY></TABLE></CCID_NOBR /></CENTER></p> <p><P style="TEXT-INDENT: 2em">例1 </p> <p>本例阻止用户修改Employees表中的EmployeeID列。 </p> <p><CENTER><CCID_NOBR /></p> <p><PRE><CCID_CODE />USE Northwind</p> <p>GO</p> <p>CREATE TRIGGER Employee_Update</p> <p>ON Employees</p> <p>FOR UPDATE</p> <p>AS</p> <p>IF UPDATE (EmployeeID)</p> <p>BEGIN</p> <p>RAISERROR (‘Transaction cannot be processed.\</p> <p>***** Employee ID number cannot be modified.’, 10, 1)</p> <p>ROLLBACK TRANSACTION</p> <p>END</CCID_CODE /></PRE></TR></TBODY></TABLE></CENTER></p> <p><P style="TEXT-INDENT: 2em">INSTEAD OF触发器的工作过程 </p> <p>可以在表或视图上指定INSTEAD OF触发器。执行这种触发器就能够替代原始的触发动作。INSTEAD OF触发器扩展了视图更新的类型。对于每一种触发动作(INSERT、UPDATE或 DELETE),每一个表或视图只能有一个INSTEAD OF触发器。 </p> <p>INSTEAD OF触发器被用于更新那些没有办法通过正常方式更新的视图。例如,通常不能在一个基于连接的视图上进行DELETE操作。然而,可以编写一个INSTEAD OF DELETE触发器来实现删除。上述触发器可以访问那些如果视图是一个真正的表时已经被删除的数据行。将被删除的行存储在一个名为deleted的工作表中,就像AFTER触发器一样。相似地,在UPDATE INSTEAD OF触发器或者INSERT INSTEAD OF触发器中,你可以访问inserted表中的新行。 </p> <p>不能在带有WITH CHECK OPTION定义的视图中创建INSTEAD OF触发器。 </p> <p>示例 </p> <p>在本例中,创建了一个德国客户表和一个墨西哥客户表。放置在视图上的INSTEAD OF触发器将把更新操作重新定向到适当的基表上。这时发生的插入是对CustomersGer表的插入而不是对视图的插入。 </p> <p>创建两个包含客户数据的表: </p> <p><CENTER><CCID_NOBR /></p> <p><PRE><CCID_CODE />SELECT * INTO CustomersGer FROM Customers </p> <p>WHERE Customers.Country = ‘Germany’</p> <p>SELECT * INTO CustomersMex FROM Customers </p> <p>WHERE Customers.Country = ‘Mexico’</p> <p>GO</CCID_CODE /></PRE></TR></TBODY></TABLE></CCID_NOBR /></CENTER></p> <p><P style="TEXT-INDENT: 2em">在该数据上创建视图: </p> <p><CENTER><CCID_NOBR /></p> <p><PRE><CCID_CODE />CREATE VIEW CustomersView AS</p> <p>SELECT * FROM CustomersGer</p> <p>UNION</p> <p>SELECT * FROM CustomersMex</p> <p>GO</CCID_CODE /></PRE></TR></TBODY></TABLE></CCID_NOBR /></CENTER></p> <p><P style="TEXT-INDENT: 2em">创建一个在上述视图上的INSTEAD OF触发器: </p> <p><CENTER><CCID_NOBR /></p> <p><PRE><CCID_CODE />CREATE TRIGGER Customers_Update2</p> <p>ON CustomersView</p> <p>INSTEAD OF UPDATE AS</p> <p>DECLARE @Country nvarchar(15)</p> <p>SET @Country = (SELECT Country FROM Inserted)</p> <p>IF @Country = ‘Germany’</p> <p>BEGIN</p> <p>UPDATE CustomersGer</p> <p>SET CustomersGer.Phone = Inserted.Phone</p> <p>FROM CustomersGer JOIN Inserted</p> <p>ON CustomersGer.CustomerID = Inserted.CustomerID</p> <p>END</p> <p>ELSE</p> <p>IF @Country = ‘Mexico’</p> <p>BEGIN</p> <p>UPDATE CustomersMex</p> <p>SET CustomersMex.Phone = Inserted.Phone</p> <p>FROM CustomersMex JOIN Inserted</p> <p>ON CustomersMex.CustomerID = Inserted.CustomerID</p> <p>END</CCID_CODE /></PRE></TR></TBODY></TABLE></CCID_NOBR /></CENTER></p> <p><P style="TEXT-INDENT: 2em">通过更新视图,测试触发器: </p> <p><CENTER><CCID_NOBR /></p> <p><PRE><CCID_CODE />UPDATE CustomersView SET Phone = ‘ 030-007xxxx’</p> <p>WHERE CustomerID = ‘ALFKI’</p> <p>SELECT CustomerID, Phone FROM CustomersView</p> <p>WHERE CustomerID = ‘ALFKI’</p> <p>SELECT CustomerID, Phone FROM CustomersGer</p> <p>WHERE CustomerID = ‘ALFKI’</CCID_CODE /></PRE></TR></TBODY></TABLE></CCID_NOBR /></CENTER></p> <p><P style="TEXT-INDENT: 2em">那么具体的讲,对于多列数据,如何计算方差呢? </p> <p><CENTER><CCID_NOBR /></p> <p><PRE><CCID_CODE />CREATE TRIGGER [calT1T2T3] ON dbo.DCLB</p> <p>FOR INSERT,UPDATE</p> <p>AS</p> <p>update P</p> <p>SET</p> <p>/**//*</p> <p>计算方差的触发器</p> <p>*/</p> <p>P.T1=(I.P1+I.P2+I.P3+I.P4+I.P5+I.P6),</p> <p>P.T2=(I.Y1+I.Y2+I.Y3+I.Y4+I.Y5+I.Y6 ),</p> <p>P.T3=SQRT(P.T1*P.T1+P.T2*P.T2)</p> <p>FROM DCLB AS P INNER JOIN Inserted AS I</p> <p>ON P.SID = I.SID</CCID_CODE /></PRE></TR></TBODY></TABLE></CCID_NOBR /></CENTER></p> <p><P style="TEXT-INDENT: 2em">触发器的使用很方便,而且也很简单,重要的是理解inserted过程。可将UPDATE语句看成两步操作:即捕获数据前像(before image)的DELETE语句,和捕获数据后像(after image)的INSERT语句。当在定义有触发器的表上执行UPDATE语句时,原始行(前像)被移入到deleted表,更新行(后像)被移入到inserted表。触发器检查deleted表和inserted表以及被更新的表,来确定是否更新了多行以及如何执行触发器动作。 </DIV></p> </div> <div class="post-tags"> <a href="http://blog.zxlm.cn/tag/access" rel="tag" title="Access (481 topics)">Access</a>, <a href="http://blog.zxlm.cn/tag/database" rel="tag" title="DataBase (480 topics)">DataBase</a>, <a href="http://blog.zxlm.cn/tag/sql-server" rel="tag" title="SQL Server (487 topics)">SQL Server</a> </div> </div> <!-- /post --><!-- post --> <div id="post-1701" class="post-1701 post hentry category-database tag-access tag-database tag-sql-server clearfix preview-full"> <h2 class="title"><a href="http://blog.zxlm.cn/2006/05/sql-server%e6%95%b0%e6%8d%ae%e5%ba%93%e4%b8%ad%e7%a9%ba%e5%80%bc%e5%a4%84%e7%90%86%e6%8a%80%e5%b7%a7.html" rel="bookmark" title="永久链接: SQL Server数据库中空值处理技巧">SQL Server数据库中空值处理技巧</a></h2> <div class="post-date"> <p class="day">五 2nd</p> </div> <div class="post-info clearfix "> <p class="author alignleft">由<a href="http://blog.zxlm.cn/author/admin/" title="由admin撰写的日志 ">admin</a>分类在<a href="http://blog.zxlm.cn/category/database">DataBase</a> </p> <p class="comments alignright"><a href="http://blog.zxlm.cn/2006/05/sql-server%e6%95%b0%e6%8d%ae%e5%ba%93%e4%b8%ad%e7%a9%ba%e5%80%bc%e5%a4%84%e7%90%86%e6%8a%80%e5%b7%a7.html#comments" class="no comments">没有评论</a></p> </div> <div class="post-content clearfix"> <p>数据完整性是任何数据库系统要保证的重点。不管系统计划得有多好,空数据值的问题总是存在。本文探讨了在SQL Server中处理这些值时涉及的3个问题:计数、使用空表值以及外键处理。 </p> <p>用COUNT(*)处理空值 </p> <p>大多数集合函数都能在计算时消除空值;COUNT函数则属于例外。对包含空值的一个列使用COUNT函数,空值会从计算中消除。但假如COUNT函数使用一个星号,它就计算所有行,而不管是否存在空值。 </p> <p>如果希望COUNT函数对给定列的所有行(包括空值)进行计数,请使用ISNULL函数。ISNULL函数会将空值替换成有效的值。 </p> <p>事实上,对集合函数来说,如果空值可能导致错误结果,ISNULL函数就非常有用。记住在使用一个星号时,COUNT函数会对所有行进行计算。下例演示了空值在AVG和COUNT集合函数中的影响: </p> <p><CENTER><CCID_NOBR /></p> <p><PRE><CCID_CODE />SET NOCOUNT ON</p> <p> GO</p> <p> CREATE TABLE xCount</p> <p> (pkey1 INT IDENTITY NOT NULL</p> <p> CONSTRAINT pk_xCount PRIMARY KEY,</p> <p> Col1 int NULL)</p> <p> GO</p> <p> INSERT xCount (Col1) VALUES (10)</p> <p> GO</p> <p> INSERT xCount (Col1) VALUES (15)</p> <p> GO</p> <p> INSERT xCount (Col1) VALUES (20)</p> <p> GO</p> <p> INSERT xCount (Col1) VALUES (NULL)</p> <p> GO</p> <p> SELECT AVG(Col1) AvgWithoutIsNullFunctionOnCol1,</p> <p> AVG(ISNULL(Col1,0)) AvgWithIsNullFunctionOnCol1,</p> <p> COUNT(Col1) NoIsNullFunctionOnCol1 ,</p> <p> COUNT(ISNULL(Col1,0)) UsingIsNullFunctionOnCol1,</p> <p> Count(*) UsingAsterisk</p> <p> FROM xCount</p> <p> GO</p> <p> DROP TABLE xCount</p> <p> GO</p> <p> OUTPUT:</p> <p>AvgWOIsNullFnctnCol1 AvgWIsNullFnctnCol1 WOIsNullFnctnCol1</p> <p>WIsNullFnctnCol1 UsingAsterisk</p> <p> —————- ————- ————– ———— </p> <p>15 11 3 4 4</CCID_CODE /></PRE></TR></TBODY></TABLE></CCID_NOBR /></CENTER></p> <p><P style="TEXT-INDENT: 2em">恰当使用空表值 </p> <p>SQL Server可能出现一种特殊情况:在引用父表的一个表中,因为不允许空值,所以“声明引用完整性”(DRI)可能不会得到强制。即使父表不包含空值,在子表引用了父表主键约束或惟一约束的列中,也可能包含空值。 </p> <p>假如来自父表的值目前未知,就不会有任何问题。例如,父表可能是一个地址表,而子表可能包含联系信息。由于许多原因,可能暂时不知道要传给父表的联系地址。这是一种基于时间的问题,空值在其中或许是合适的。 </p> <p>如下例所示,我们创建父表,并在其中插入两个值。 </p> <p><CENTER><CCID_NOBR /></p> <p><PRE><CCID_CODE />SET NOCOUNT ON</p> <p> GOCREATE TABLE Parent(pkey1 INT IDENTITY NOT NULL </p> <p> CONSTRAINT pkParent PRIMARY KEY,col1 INT NULL)GOINSERT </p> <p> Parent (col1) VALUES (284)GOINSERT </p> <p> Parent (col1) VALUES (326)GO</CCID_CODE /></PRE></TR></TBODY></TABLE></CCID_NOBR /></CENTER></p> <p><P style="TEXT-INDENT: 2em">以下代码则创建子表,并在引用父表的列中插入一个空值。 </p> <p><CENTER><CCID_NOBR /></p> <p><PRE><CCID_CODE />CREATE TABLE Child</p> <p> (pkey1 INT IDENTITYCONSTRAINT pkChild </p> <p> PRIMARY KEY,Parentpkey1 INT NULLCONSTRAINT fkChildParent </p> <p> FOREIGN KEYREFERENCES Parent(pkey1),col1 INT NULL)</p> <p> GOINSERT Child (Parentpkey1, col1) VALUES (null,2)GO</CCID_CODE /></PRE></TR></TBODY></TABLE></CCID_NOBR /></CENTER></p> <p><P style="TEXT-INDENT: 2em">但在以下代码中,要同时从父表和子表选择值。虽然父表不包含空值,但在子表引用了父表的那个列中,将允许一个空值。 </p> <p>然后丢弃所有表,清除这个演示所用的数据库对象。 </p> <p><CENTER><CCID_NOBR /></p> <p><PRE><CCID_CODE />SELECT * FROM ChildGOSELECT * FROM ParentGODROP TABLE Child, ParentGO</PRE></TR></TBODY></TABLE></CENTER></p> <p><P></p> <p><DIV class=content></p> <p><P></p> <p>在可以为空的外键中检查数据的有效性 </p> <p>如果由两个列共同组成主键,而且一个子表将主键作为可为空值的外键来继承,就可能得到错误的数据。可在一个外键列中插入有效的值,但在另一个外键列中插入空值。然后,可添加一个数据表检查约束,在可为空的外键中检查数据的有效性。 </p> <p>任何多列外键都可能遇到同样的问题。所以,你需要添加一个检查约束来检测异常。最初,检查约束将检查构成外键的所有列中可能为空的值。检查约束还要检查这些列中不能为空的值。如两个检查都通过,问题就解决了。 </p> <p>以下示范脚本展示了这样的一个异常,以及如何用检查约束来纠正它。 </p> <p><CENTER></CENTER></p> <p><P style="TEXT-INDENT: 2em">空值是所有数据库开发者和管理员都要遇到的。所以,要想开发成功的应用程序,必须知道如何处理这些值。本文和你分享了空值处理的一些技巧和技术。</DIV></p> </div> <div class="post-tags"> <a href="http://blog.zxlm.cn/tag/access" rel="tag" title="Access (481 topics)">Access</a>, <a href="http://blog.zxlm.cn/tag/database" rel="tag" title="DataBase (480 topics)">DataBase</a>, <a href="http://blog.zxlm.cn/tag/sql-server" rel="tag" title="SQL Server (487 topics)">SQL Server</a> </div> </div> <!-- /post --> <div class="page-navigation clearfix"> <div class="wp-pagenavi"> <span class="pages">Page 1 of 80</span><span class="current">1</span><a href="http://blog.zxlm.cn/tag/database/page/2" class="page" title="2">2</a><a href="http://blog.zxlm.cn/tag/database/page/3" class="page" title="3">3</a><a href="http://blog.zxlm.cn/tag/database/page/4" class="page" title="4">4</a><a href="http://blog.zxlm.cn/tag/database/page/5" class="page" title="5">5</a><a href="http://blog.zxlm.cn/tag/database/page/2" class="nextpostslink">»</a><a href="http://blog.zxlm.cn/tag/database/page/10" class="page" title="10">10</a><a href="http://blog.zxlm.cn/tag/database/page/20" class="page" title="20">20</a><a href="http://blog.zxlm.cn/tag/database/page/30" class="page" title="30">30</a><span class="extend">...</span><a href="http://blog.zxlm.cn/tag/database/page/80" class="last" title="Last »">Last »</a></div> </div> </div> <!-- /primary content --> <div id="sidebar"> <ul class="sidebar-blocks"> <li class="block"><div class="block-widget_search clearfix" id="instance-search-3"> <!-- search form --> <div class="search-form"> <form method="get" id="searchform" action="http://blog.zxlm.cn/" class="clearfix"> <fieldset> <div id="searchfield"> <input type="text" name="s" id="searchbox" class="text clearField" value="搜索" /> </div> <input type="submit" value="" class="submit" /> </fieldset> </form> </div> <!-- /search form --> </div></li><li class="block"><div class="block-widget_categories clearfix" id="instance-categories-2"><h3 class="title"><span>分类目录</span></h3><div class="block-div"></div><div class="block-div-arrow"></div> <ul> <li class="cat-item cat-item-69"><a href="http://blog.zxlm.cn/category/android" title="Android">Android</a> </li> <li class="cat-item cat-item-11"><a href="http://blog.zxlm.cn/category/aspnet" title="ASP.net">ASP.net</a> </li> <li class="cat-item cat-item-65"><a href="http://blog.zxlm.cn/category/collection" title="Collection">Collection</a> </li> <li class="cat-item cat-item-64"><a href="http://blog.zxlm.cn/category/database" title="DataBase">DataBase</a> </li> <li class="cat-item cat-item-70"><a href="http://blog.zxlm.cn/category/firefox" title="Firefox">Firefox</a> </li> <li class="cat-item cat-item-68"><a href="http://blog.zxlm.cn/category/google-adsense" title="Google Adsense">Google Adsense</a> </li> <li class="cat-item cat-item-61"><a href="http://blog.zxlm.cn/category/javascript" title="JavaScript">JavaScript</a> </li> <li class="cat-item cat-item-66"><a href="http://blog.zxlm.cn/category/jspjava" title="JSP/Java">JSP/Java</a> </li> <li class="cat-item cat-item-59"><a href="http://blog.zxlm.cn/category/killvirus" title="KillVirus">KillVirus</a> </li> <li class="cat-item cat-item-62"><a href="http://blog.zxlm.cn/category/linuxunix" title="Linux/Unix">Linux/Unix</a> </li> <li class="cat-item cat-item-63"><a href="http://blog.zxlm.cn/category/php" title="PHP">PHP</a> </li> <li class="cat-item cat-item-67"><a href="http://blog.zxlm.cn/category/seo%e5%92%8c%e7%bd%91%e7%ab%99%e6%8e%a8%e5%b9%bf" title="SEO和网站推广">SEO和网站推广</a> </li> <li class="cat-item cat-item-1"><a href="http://blog.zxlm.cn/category/uncategorized" title="查看 Uncategorized 下的所有文章">Uncategorized</a> </li> <li class="cat-item cat-item-60"><a href="http://blog.zxlm.cn/category/webdesign" title="WebDesign">WebDesign</a> </li> <li class="cat-item cat-item-76"><a href="http://blog.zxlm.cn/category/%e5%85%ac%e5%91%8a" title="公告">公告</a> </li> <li class="cat-item cat-item-74"><a href="http://blog.zxlm.cn/category/%e6%88%91%e7%9a%84%e9%a1%b9%e7%9b%ae" title="我的项目">我的项目</a> </li> <li class="cat-item cat-item-75"><a href="http://blog.zxlm.cn/category/%e6%97%b6%e4%ba%8b%e6%84%9f%e6%83%b3" title="时事感想">时事感想</a> </li> <li class="cat-item cat-item-72"><a href="http://blog.zxlm.cn/category/%e8%bd%af%e4%bb%b6%e7%a0%b4%e8%a7%a3" title="软件破解">软件破解</a> </li> <li class="cat-item cat-item-73"><a href="http://blog.zxlm.cn/category/%e9%85%8d%e7%bd%ae%e7%ae%a1%e7%90%86" title="配置管理">配置管理</a> </li> <li class="cat-item cat-item-71"><a href="http://blog.zxlm.cn/category/%e9%bb%91%e5%ae%a2%e6%8a%80%e6%9c%af" title="黑客技术">黑客技术</a> </li> </ul> </div></li><li class="block"><div class="block-widget_tag_cloud clearfix" id="instance-tag_cloud-3"><h3 class="title"><span>标签</span></h3><div class="block-div"></div><div class="block-div-arrow"></div><div><a href='http://blog.zxlm.cn/tag/access' class='tag-link-241' title='481个主题' style='font-size: 21.206185567pt;'>Access</a> <a href='http://blog.zxlm.cn/tag/adsense' class='tag-link-164' title='27个主题' style='font-size: 12.3298969072pt;'>adsense</a> <a href='http://blog.zxlm.cn/tag/aspnet' class='tag-link-11' title='15个主题' style='font-size: 10.5257731959pt;'>ASP.net</a> <a href='http://blog.zxlm.cn/tag/baidu' class='tag-link-110' title='6个主题' style='font-size: 8pt;'>baidu</a> <a href='http://blog.zxlm.cn/tag/blog' class='tag-link-39' title='8个主题' style='font-size: 8.72164948454pt;'>blog</a> <a href='http://blog.zxlm.cn/tag/database' class='tag-link-64' title='480个主题' style='font-size: 21.206185567pt;'>DataBase</a> <a href='http://blog.zxlm.cn/tag/discuz' class='tag-link-159' title='13个主题' style='font-size: 10.1649484536pt;'>discuz</a> <a href='http://blog.zxlm.cn/tag/firefox' class='tag-link-70' title='10个主题' style='font-size: 9.37113402062pt;'>Firefox</a> <a href='http://blog.zxlm.cn/tag/google' class='tag-link-24' title='35个主题' style='font-size: 13.1237113402pt;'>Google</a> <a href='http://blog.zxlm.cn/tag/iis' class='tag-link-366' title='11个主题' style='font-size: 9.65979381443pt;'>iis</a> <a href='http://blog.zxlm.cn/tag/jsp' class='tag-link-41' title='6个主题' style='font-size: 8pt;'>jsp</a> <a href='http://blog.zxlm.cn/tag/linux' class='tag-link-111' title='16个主题' style='font-size: 10.7422680412pt;'>linux</a> <a href='http://blog.zxlm.cn/tag/mysql' class='tag-link-136' title='610个主题' style='font-size: 22pt;'>mysql</a> <a href='http://blog.zxlm.cn/tag/php' class='tag-link-63' title='15个主题' style='font-size: 10.5257731959pt;'>PHP</a> <a href='http://blog.zxlm.cn/tag/rtx' class='tag-link-375' title='8个主题' style='font-size: 8.72164948454pt;'>rtx</a> <a href='http://blog.zxlm.cn/tag/sablog' class='tag-link-161' title='14个主题' style='font-size: 10.381443299pt;'>sablog</a> <a href='http://blog.zxlm.cn/tag/seo' class='tag-link-23' title='92个主题' style='font-size: 16.0824742268pt;'>SEO</a> <a href='http://blog.zxlm.cn/tag/sql' class='tag-link-82' title='9个主题' style='font-size: 9.0824742268pt;'>sql</a> <a href='http://blog.zxlm.cn/tag/sql-server' class='tag-link-240' title='487个主题' style='font-size: 21.2783505155pt;'>SQL Server</a> <a href='http://blog.zxlm.cn/tag/sql-server-2000' class='tag-link-137' title='6个主题' style='font-size: 8pt;'>sql server 2000</a> <a href='http://blog.zxlm.cn/tag/svn' class='tag-link-315' title='6个主题' style='font-size: 8pt;'>svn</a> <a href='http://blog.zxlm.cn/tag/visual-studio' class='tag-link-26' title='12个主题' style='font-size: 9.87628865979pt;'>visual studio</a> <a href='http://blog.zxlm.cn/tag/vss' class='tag-link-54' title='6个主题' style='font-size: 8pt;'>vss</a> <a href='http://blog.zxlm.cn/tag/windows' class='tag-link-35' title='13个主题' style='font-size: 10.1649484536pt;'>windows</a> <a href='http://blog.zxlm.cn/tag/wordpress' class='tag-link-22' title='9个主题' style='font-size: 9.0824742268pt;'>Wordpress</a> <a href='http://blog.zxlm.cn/tag/zend' class='tag-link-307' title='9个主题' style='font-size: 9.0824742268pt;'>zend</a> <a href='http://blog.zxlm.cn/tag/%e5%81%a5%e5%ba%b7' class='tag-link-88' title='6个主题' style='font-size: 8pt;'>健康</a> <a href='http://blog.zxlm.cn/tag/%e5%bb%ba%e8%ae%ae' class='tag-link-101' title='6个主题' style='font-size: 8pt;'>建议</a> <a href='http://blog.zxlm.cn/tag/%e5%bf%ab%e4%b9%90' class='tag-link-96' title='6个主题' style='font-size: 8pt;'>快乐</a> <a href='http://blog.zxlm.cn/tag/%e6%80%a7%e8%83%bd' class='tag-link-83' title='9个主题' style='font-size: 9.0824742268pt;'>性能</a> <a href='http://blog.zxlm.cn/tag/%e6%8f%92%e4%bb%b6' class='tag-link-160' title='7个主题' style='font-size: 8.36082474227pt;'>插件</a> <a href='http://blog.zxlm.cn/tag/%e6%90%9c%e7%b4%a2%e5%bc%95%e6%93%8e' class='tag-link-157' title='12个主题' style='font-size: 9.87628865979pt;'>搜索引擎</a> <a href='http://blog.zxlm.cn/tag/%e6%90%9c%e7%b4%a2%e5%bc%95%e6%93%8e%e4%bc%98%e5%8c%96' class='tag-link-235' title='80个主题' style='font-size: 15.6494845361pt;'>搜索引擎优化</a> <a href='http://blog.zxlm.cn/tag/%e6%95%b0%e6%8d%ae%e5%ba%93' class='tag-link-42' title='21个主题' style='font-size: 11.5360824742pt;'>数据库</a> <a href='http://blog.zxlm.cn/tag/%e6%97%a5%e5%bf%97' class='tag-link-411' title='7个主题' style='font-size: 8.36082474227pt;'>日志</a> <a href='http://blog.zxlm.cn/tag/%e6%9c%8d%e5%8a%a1%e5%99%a8' class='tag-link-28' title='11个主题' style='font-size: 9.65979381443pt;'>服务器</a> <a href='http://blog.zxlm.cn/tag/%e6%9f%a5%e8%af%a2%e4%bc%98%e5%8c%96' class='tag-link-156' title='7个主题' style='font-size: 8.36082474227pt;'>查询优化</a> <a href='http://blog.zxlm.cn/tag/%e6%b3%a8%e5%86%8c%e8%a1%a8' class='tag-link-142' title='9个主题' style='font-size: 9.0824742268pt;'>注册表</a> <a href='http://blog.zxlm.cn/tag/%e7%97%85%e6%af%92' class='tag-link-141' title='7个主题' style='font-size: 8.36082474227pt;'>病毒</a> <a href='http://blog.zxlm.cn/tag/%e7%be%a4%e9%9b%86' class='tag-link-199' title='6个主题' style='font-size: 8pt;'>群集</a> <a href='http://blog.zxlm.cn/tag/%e8%83%a1%e6%80%9d%e4%b9%b1%e6%83%b3' class='tag-link-91' title='15个主题' style='font-size: 10.5257731959pt;'>胡思乱想</a> <a href='http://blog.zxlm.cn/tag/%e8%b0%83%e8%af%95' class='tag-link-27' title='6个主题' style='font-size: 8pt;'>调试</a> <a href='http://blog.zxlm.cn/tag/%e8%bf%85%e9%9b%b7' class='tag-link-284' title='8个主题' style='font-size: 8.72164948454pt;'>迅雷</a> <a href='http://blog.zxlm.cn/tag/%e9%85%8d%e7%bd%ae' class='tag-link-126' title='12个主题' style='font-size: 9.87628865979pt;'>配置</a> <a href='http://blog.zxlm.cn/tag/%e9%94%99%e8%af%af' class='tag-link-29' title='29个主题' style='font-size: 12.5463917526pt;'>错误</a></div> </div></li> <li class="block"><div class="block-widget_recent_comments clearfix" id="instance-recent-comments-3"> <h3 class="title"><span>最近评论</span></h3><div class="block-div"></div><div class="block-div-arrow"></div> <ul id="recentcomments"><li class="recentcomments">admin 在 <a href="http://blog.zxlm.cn/2008/05/%e6%8e%a8%e8%8d%90%e4%b8%80%e4%ba%9b%e5%9f%ba%e4%ba%8ephp%e7%9a%84%e7%9b%b8%e5%86%8c%e5%9b%be%e5%ba%93%e7%a8%8b%e5%ba%8f.html/comment-page-1#comment-437">推荐一些基于PHP的相册图库程序</a> 上的评论</li><li class="recentcomments"><a href='http://www.shuaizhu.com' rel='external nofollow' class='url'>帅</a> 在 <a href="http://blog.zxlm.cn/2008/05/%e6%8e%a8%e8%8d%90%e4%b8%80%e4%ba%9b%e5%9f%ba%e4%ba%8ephp%e7%9a%84%e7%9b%b8%e5%86%8c%e5%9b%be%e5%ba%93%e7%a8%8b%e5%ba%8f.html/comment-page-1#comment-435">推荐一些基于PHP的相册图库程序</a> 上的评论</li><li class="recentcomments">admin 在 <a href="http://blog.zxlm.cn/2009/10/%e7%bb%88%e4%ba%8e%e8%8e%b7%e5%be%97google-wave%e7%9a%84%e9%82%80%e8%af%b7.html/comment-page-1#comment-434">终于获得Google Wave的邀请</a> 上的评论</li><li class="recentcomments">aqzgx 在 <a href="http://blog.zxlm.cn/2009/10/%e7%bb%88%e4%ba%8e%e8%8e%b7%e5%be%97google-wave%e7%9a%84%e9%82%80%e8%af%b7.html/comment-page-1#comment-431">终于获得Google Wave的邀请</a> 上的评论</li><li class="recentcomments">天涯 在 <a href="http://blog.zxlm.cn/2007/12/visual-studio%e8%b5%b7%e5%a7%8b%e9%a1%b5%e7%9a%84%e6%9c%80%e8%bf%91%e7%9a%84%e9%a1%b9%e7%9b%ae%e4%b8%8d%e6%98%be%e7%a4%ba%e6%9c%80%e8%bf%91%e6%89%93%e5%bc%80%e7%9a%84%e9%a1%b9%e7%9b%ae.html/comment-page-1#comment-430">Visual Studio起始页的"最近的项目"不显示最近打开的项目</a> 上的评论</li></ul> </div></li><li class="block"><div class="block-widget_links clearfix" id="linkcat-2"><h3 class="title"><span>Blogroll</span></h3><div class="block-div"></div><div class="block-div-arrow"></div> <ul class='xoxo blogroll'> <li><a href="http://wordpress.org/development/">Development Blog</a></li> <li><a href="http://codex.wordpress.org/">Documentation</a></li> <li><a href="http://googlechinablog.com/" title="Google黑板报">Google黑板报</a></li> <li><a href="http://wordpress.org/extend/plugins/">Plugins</a></li> <li><a href="http://www.chinamyhosting.com/seoblog/" title="介绍和研究搜索引擎优化SEO技术,Zac的SEO优化及网络营销,电子商务,网站推广运营思考。">SEO每天一贴</a></li> <li><a href="http://wordpress.org/extend/ideas/">Suggest Ideas</a></li> <li><a href="http://wordpress.org/support/">Support Forum</a></li> <li><a href="http://wordpress.org/extend/themes/">Themes</a></li> <li><a href="http://planet.wordpress.org/">WordPress Planet</a></li> <li><a href="http://www.sqlite.com.cn/" title="SQLite是一个小型的C程序库,实现了独立、可嵌入、零配置的SQL数据库引擎。官网http://www.sqlite.org">小型数据库引擎SQLite</a></li> <li><a href="http://www.sqlmanager.net" title="这里提供了很多用于连接各种数据库的工具,只是都是收费的。免费的只能用12个月。">数据库连接管理工具</a></li> <li><a href="http://www.williamlong.info/" title="月光博客">月光博客</a></li> <li><a href="http://blog.sina.com.cn/m/kaifulee" title="值得去看一下的.">李开复在新浪的Blog</a></li> <li><a href="http://www.dunsh.org/" title="点石互动">点石互动</a></li> <li><a href="http://www.googlechinawebmaster.com/" title="谷歌网站管理员博客">谷歌网站管理员博客</a></li> </ul> </div></li> <li class="block"><div class="block-widget_meta clearfix" id="instance-meta-2"><h3 class="title"><span>功能</span></h3><div class="block-div"></div><div class="block-div-arrow"></div> <ul> <li><a href="http://blog.zxlm.cn/wp-login.php?action=register">注册</a></li> <li><a href="http://blog.zxlm.cn/wp-login.php">登录</a></li> <li><a href="http://blog.zxlm.cn/feed" title="使用 RSS 2.0 同步此内容">文章 <abbr title="Really Simple Syndication">RSS</abbr></a></li> <li><a href="http://blog.zxlm.cn/comments/feed" title="RSS 上的最近评论"><abbr title="Really Simple Syndication">RSS</abbr> 评论</a></li> <li><a href="http://wordpress.org/" title="基于 WordPress,优美的个人信息发布平台。">WordPress.org</a></li> </ul> </div></li> <li class="block"><div class="block-widget_akismet clearfix" id="instance-akismet"> <h3 class="title"><span>Spam Blocked</span></h3><div class="block-div"></div><div class="block-div-arrow"></div> <div id="akismetwrap"><div id="akismetstats"><a id="aka" href="http://akismet.com" title=""><div id="akismet1"><span id="akismetcount">492</span> <span id="akismetsc">条垃圾评论</span></div> <div id="akismet2"><span id="akismetbb">被截获</span><br /><span id="akismeta">Akismet</span></div></a></div></div> </div></li> </ul> </div> </div> </div> <!-- /main content --> <!-- foooter --> <div id="footer"> <div class="page-content"> <div id="copyright"> <abbr title="Mystique/1.72">Mystique</abbr> theme by <a href="http://digitalnature.ro">digitalnature</a> | Powered by <a href="http://wordpress.org/">WordPress</a> <br /> <a class="rss-subscribe" href="http://blog.zxlm.cn/feed" title="RSS订阅">RSS订阅</a> <a class="valid-xhtml" href="http://validator.w3.org/check?uri=referer" title="Valid XHTML">XHTML 1.1</a> <a id="goTop" class="js-link">顶部</a> <script type="text/javascript"> /* <![CDATA[ */ var isIE6 = false; var isIE = false; var lightbox = 1; var ajaxComments = 1; var redirectReadMore = 1; /* ]]> */ </script> <!--[if lte IE 6]> <script type="text/javascript"> isIE6 = true; isIE = true; </script> <![endif]--> <!--[if gte IE 7]> <script type="text/javascript"> isIE = true; </script> <![endif]--> <script type='text/javascript' src='http://blog.zxlm.cn/wp-content/themes/mystique/js/jquery.mystique.js?ver=1.72'></script> </div> </div> </div> <!-- /footer --> </div> </div> <!-- /shadow --> <!-- page controls --> <div id="pageControls"></div> <!-- /page controls --> <!-- 27 queries. 0.557 seconds. --> </div> </body> </html>