把Oracle数据库移植到Microsoft SQL Server 7.0
摘要:本文是为那些想把自己的Oracle应用程序转换为Microsoft SQL Server应用程序的开发人员编写的。本文描述了一个成功的转换所需要的工具、过程和技术。同时强调了建立高性能、高度并行的SQL Server应用程序的基本的设计要素。
本文的读者应该具有:
-
Oracle关系型数据管理系统(RDBMS)的坚实基础。
-
普通数据库管理知识。
-
熟悉Oracle SQL和PL/SQL语言。
-
C/C++编程语言的工作经验。
- 在sysadmin组中设定服务器规则的成员资格
本文假定你熟悉Oracle RDBMS的术语、概念和工具。如果想要了解关于Oracle RDBMS以及它的结构的更多信息,请参考Oracle 7 Server Concepts Manual。对于使用Oracle脚本和示例,仍然假定你熟悉Oracle Server Manager和Oracle SQL*Plus工具。要得到更详细的信息,请参看Oracle文档。
目录
-
开发和应用程序平台
-
概述
-
本文的组织形式
-
结构和术语
-
安装和配置Microsoft SQL Server
-
定义数据库对象
-
加强数据完整性和商业规则
-
事务、锁定和并行
-
死锁
-
SQL语言支持
-
实现游标
-
调整SQL语句
-
使用ODBC
-
开发和管理数据库复制
-
移植你的数据和应用程序
- 数据库示例
开发和应用程序平台 为了便于清楚的表述,本文参照的开发和应用程序平台假定为Microsoft Visual Studio version 6.0、Microsoft Windows NT version 4 (Service Pack 4)、SQL Server 7.0、Oracle 7.3。Oracle 7.3使用Visigenic Software ODBC(版本2.00.0300)驱动,SQL Server 7.0使用Microsoft Corporation ODBC(版本3.70)驱动。Microsoft SQL Server 7.0包括针对Oracle的OLE DB驱动,但是该驱动程序在本章中并没有广泛的讨论。
概述 应用程序的移植似乎非常复杂。在不同的关系数据管理系统之间有太多的结构差异。用来描述Oracle结构的用词和术语通常与该词在Microsoft SQL Server中的意思完全不同。另外,Oracle和SQL Server都对SQL-92标准做了许多自有的扩展。
从一个应用程序开发人员的观点来看,Oracle和SQL Server是以相似的方法来管理数据的。在Oracle和SQL Server之间有着重大的内部区别,但是如果管理得当,可以把这些区别对移植的影响减到最小。
SQL语言扩展 开发人员面临的最重要的移植问题是执行SQL-92语言标准和每一个关系数据管理系统提供的语言扩展。有一些开发人员只使用标准的SQL语法,喜欢尽可能的保持他们的程序代码的普遍性。通常,这种方法把程序代码限制在SQL-92标准的登录级别(entry-level)上,而这个级别是被许多的数据库产品实现了的,包括Oracle和SQL Server。
这种方法将会产生一些不必要的程序代码复杂性而且还会对程序的性能造成很大的影响。例如,Oracle的DECODE函数是一个非标准的SQL扩展。Microsoft SQL Server的CASE表达式是一个超越了登录级别的SQL-92扩展,而且在所有其他的数据库产品中都没有实现。
Oracle的DECODE和SQL Server的CASE都是可选的,你可以不用这两个函数而实现它们的功能,而这需要从关系数据管理系统中提取更多的数据。
还有,对SQL语言的程序扩展也会造成困难。Oracle的PL/SQL和SQL Server的Transact-SQL语言在函数上是相似的,但是在语法上不同。在两种数据库和程序扩展中间没有明确的对称性。因此,你可能会决定不使用想程序和触发器这样的存储的程序。这是很不幸的,因为它们提供了别的任何方式都无法实现的性能和安全性上的优点。
私有开发接口的使用带来了新的问题。用Oracle OCI(Oracle Call Interface)进行程序转换通常需要很多资源。开发一个可能用到多个关系数据管理系统的应用程序,最好是考虑使用开放数据库连接(Open Database Connectivity,ODBC)接口。
ODBC ODBC是为同多个数据库管理系统协同工作而设计的。ODBC提供了一个一致的应用程序编程接口(application programming interface,API),该接口使用一个针对数据库的驱动程序同不同的数据库协同工作。
一致的应用程序编程接口意味着程序用来建立连接、执行命令以及获取结果的函数是一样的,无论该程序是和Oracle还是SQL Server对话。
ODBC同时还定义了一个标准化的调用级别的接口并且针对那些不同数据库里完成同样任务但语法不同的SQL函数使用标准的出口次序。ODBC驱动器可以自动的把这个ODBC语法转化为Oracle或者SQL Server的本地语法,这个过程不需要对程序代码做任何的修订。在某些情况下,最好的方法是编写一个程序并且让ODBC在运行时间执行转换处理。
ODBC并不是一个万能的可以针对任何数据库实现完全独立的、完整功能和高性能的解决方案。不同数据库和第三方经销商提供了对ODBC不同级别的支持。一些驱动器仅仅实现了核心的API函数,这些函数映射了顶层或者其他接口库。其他一些驱动器,例如Microsoft SQL Server的驱动器,在一个本地的、高性能的驱动器中提供了完整的2级支持。
如果一个程序仅使用核心的ODBC API,它很可能会丢弃某些数据库的特征和性能。此外,并不是所有的本地SQL扩展都可以在ODBC出口次序中描述的(例如Oracle的DECODE和SQL Server的CASE表达式)。
另外,书写SQL语句来利用数据库优化器是意见很自然的事情。但是在Oracle中用来扩展数据库性能的技术和方法在Microsoft SQL Server 7.0中不一定是最好的。ODBC接口并不能把一个系统所用的技术翻译为另一个系统的技术。
ODBC并不影响一个应用程序使用数据库专有的特征和调整来提高性能,但是应用程序需要某些数据库专有的代码部分。ODBC使得在多个数据库间保持程序结构和多数程序代码一致变得容易。
OLE DB OLE DB是下一代数据访问技术。Microsoft SQL Server 7.0利用包含在其自身的组件之中的OLE DB。这样,应用程序开发人员可以考虑使用OLE DB来进行新的SQL Server 7.0开发。微软在SQL Server 7.0中还提供了支持Oracle 7.3的OLE DB。
OLE DB是微软用来管理跨组织的数据的战略性的系统级编程接口。OLE DB是在ODBC特征上建立的具有开放性的设计。ODBC是设计来访问相关的数据库的,而OLE DB则是设计来访问相关的或者不相关的信息源,例如主机上的ISAM/VSAM和分层数据库,电子邮件和文件系统存储,文本、图像和地理数据以及定制的业务对象。
OLE DB了一组COM接口以压缩不同的数据库管理服务,同时还允许创建软件组件来实现这些服务.OLE DB组件包含了数据提供者(保持和显露数据)、数据消费者(使用数据)以及服务组件(处理和传输数据,例如查询处理器和光标引擎)。
OLE DB接口的设计目的是帮助实现组件的平滑集成,这样的话OLE DB组件提供商就可以迅速的向市场提供高质量的OLE DB组件了。此外、OLE DB还包含一座连接ODBC的桥梁,如此就可以为今天可以得到的大量的ODBC相关的数据库驱动程序继续提供支持了。
本文的组织方式 为了帮助你一步一步的实现从Oracle到SQL Server的转换,本文的每一部分都有一个关于Oracle7.3和Microsoft SQL Server 7.0的不同之处的概述。同时还包括转换的考虑,SQL Server 7.0的优势以及多个实例。
结构和术语 作为成功移植的开始,你应该掌握Microsoft SQL Server 7.0所用的基本的结构和术语。这一部分中的许多例子都是从本文包含的Oracle和SQL Server应用程序中截取下来的。 数据库的定义
在Oracle中,数据库是指整个Oracle RDBMS环境,并且包含以下组件。
Oracle数据库处理过程和数据缓存(实例)。
-
包含一个集中的系统目录的SYSTEM表空间。
-
DBA定义的其它表空间(可选的)。
-
两个或者多个Redo日志。
-
存档的Redo日志(可选)
- 各种其它文件(控制文件、Init.ora等等)。
一个Microsoft SQL Server数据库提供了数据、应用程序以及安全机制的逻辑区分,更像一个表空间(tablespaces)。正如Oracle支持多个表空间,SQL Server也支持多个数据库。表空间也用来提供数据的物理放置,SQL Server通过文件组(filegroups)来提供同样的功能。
Microsoft SQL Server将缺省的安装下列数据库。
-
model数据库是所有新建用户数据库的模板。
-
Tempdb数据库跟Oracle中的临时表空间很相象,都是用来进行临时的工作储存以及排序操作。不同的是,当用户退出登录时,Microsoft SQL Server自动删除其创建的临时表空间。
-
Msdb数据库支持SQL Server代理以及其预定的工作、警报和复制信息。
-
Northwind和pubs数据库是用于培训的实例数据库。
如果想获得缺省数据库的更多信息,请参看SQL Server联机图书。 数据库系统目录 每个Oracle数据库都在一个集中系统目录(或者是数据字典(data dictionary))上运行,该目录存在于SYSTEM表空间中。而每个Microsoft SQL Server 7.0数据库都维护一个自己的系统目录,该目录包含下列信息: 数据库对象(表、索引、存储程序、视图、触发器等等)。
-
约束(Constraints)。
-
用户和许可。
-
用户定义数据类型。
-
复制定义。
- 数据库所用的文件。
SQL Server同时在master数据库中保存一个集中系统目录,该目录包含系统目录和每个数据库的某些信息:
-
数据库名和每个数据库的初始文件位置。
-
SQL Server登录账号。
-
系统消息。
-
数据库配置值。
-
远程和/或已连接的服务器。
-
当前活动信息。
- 系统存储过程。
像Oracle中的SYSTEM表空间一样,SQL Server的master数据库也必须能访问任何其他数据库。同样,对数据库做了任何重大的改变以后,通过备份master数据库来防止失败是很重要的。数据库管理员也应该能够为组成master数据库的文件做镜象。
物理和逻辑存储结构(Physical and Logical Storage Structures) Oracle RDBMS是由表空间组成的,而表空间又是由数据文件组成的。表空间数据文件被格式化为内部的块单位。块的大小,是由DBA在Oracle第一次创建的时候设置的,可以在512到8192个字节的范围内变动。当一个对象在Oracle表空间中创建的时候,用户用叫做长度的单位(初始长度((initial extent)、下一个长度(next extent)、最小长度(min extents)、以及最大长度(max extents))来标明该对象的空间大小。一个Oracle长度的大小可以变化,但是要包含一个由至少五个连续的块构成的链。
Microsoft SQL Server在数据库级别使用文件组来控制表和索引的物理放置。文件组是一个或者多个文件的逻辑容器,一个文件组中的数据按比例填充属于该文件组的全部文件。 如果没有显明的定义和使用文件组,数据库对象将放置在一个缺省的文件组中,该文件组是在数据库的创建过程中隐含定义的。文件组允许你进行下列操作: 把大的表分布在多个文件中以提高I/O吞吐量。 把索引存储在不同的文件中,而不是放在各自的表中,再一次提高I/O吞吐量以及实现磁盘并行操作。 把text、ntext、和image columns(大对象)储存在一个表的不同文件中。 把数据库对象放置在特定的磁盘锭(disk spindles)上。 在一个文件组中备份和恢复个别表和表的设置。
SQL Server把文件格式化为叫做页(pages)的单位。页的大小固定为8192字节(即8K)。页按固定为8个连续页大小的格式组织为长度。当创建表或者索引时,SQL Server自动为其分配一页,比起分配一个长度来说,储存较小的表和索引,这种方法要更有效些。
标记数据(Striping Data) (译注:Strip--在海量存储系统(MSS)中,可由给定磁头位置访问的数据盒式磁带中的那部分) Oracle类型的段对于大多数Microsoft SQL Server安装来说都不需要。取而代之的是,SQL Server可以利用基于硬件的RAID或者Windows NT软件RAID来较好的完成数据的分布或者标记。基于硬件的RAID或者Windows NT软件RAID可以设置一个由多个硬盘组成的标记装置,使它们看起来就像一个逻辑驱动器一样。如果数据库文件是在这个标记装置上创建的,磁盘子系统就假定为负责通过多个磁盘来进行分布式的I/O装载。建议管理员使用RAID来将数据分布在多个物理磁盘上。
针对SQL Server的RAID推荐配置是RAID 1(镜象)或者RAID 5(拥有一个作为冗余的额外的驱动器的标记设备)。RAID 10(对有奇偶的标记设备的镜象)也是推荐的,但它比起前两个来要昂贵的多。标记设备在分散数据库文件上通常的随机I/O来说是很好的。
如果不能使用RAID,文件组就是一个很有吸引力的选择了,它提供了RAID可以提供的某些同样的好处。此外,对于那些可能跨越多个物理RAID阵列的非常大的数据库来说,文件组可能是一个很好的选择,它可以通过一种受控制的方式将I/O分布在多个RAID阵列上。
必须优化事务日志文件(Transaction log files),使之适应连续的I/O,并且必须保护该文件以防止单点失败。因此,建议采用RAID1(镜象)来做事务日志。该驱动器的大小至少应该和在线恢复日志(online redo logs)以及反转段表空间两者加起来的大小一样才行。创建一个或者更多个日志文件,把逻辑驱动器上定义的空间占满。和存储在文件组中的数据不同,事务日志条目总是按顺序写入的,而不是按比例填充的。
欲获得关于RAID的更多信息,请参看SQL Server联机图书,你的Windows NT服务器文档,以及Microsoft Windows NT资源指南。
事务日志和自动恢复(Transaction Logs and Automatic Recovery) Oracle RDBMS在每次启动时执行自动修复。它检查表空间文件的目录与在线恢复日志文件中的目录是否一样。如果不一样,Oracle就使用在线恢复日志文件覆盖表空间文件(roll forward、前滚),然后去掉它在后滚段中发现的所有未完成的事务(roll back,后滚)。如果Oracle不能从在线恢复日志中得到需要的信息,则Oracle就求助于存档的恢复日志文件。 Microsoft SQL Server 7.0同样在每次启动时通过检查系统中的每个数据库来执行自动恢复。它首先检查master数据库,然后启动线程以覆盖系统中的所有数据库。对于每一个SQL Server数据库,自动修复机制将检查事务日志。如果事务日志中包含任何未完成的事务,则该事务后滚。然后自动修复机制再检查事务日志以找出那些还没有写入数据库的未完成事务。如果找到,则执行该事务,前滚。
SQL Server事务日志包含了Oracle后滚段和Oracle在线恢复日志两者总的功能。每个数据库都有自己的事务日志,该日志记录了数据库发生的任何改变,并且日志由数据库的所有用户共享。当一个事务开始并且发生一次数据修改,则一个BEGIN TRANSACTION事件(同modification事件一样)被记录在日志中。在自动恢复的过程中使用该事件来确定事务的起始点。每收到一个数据修改事件,改变都被记入事务日志中,优先写入其数据库中。欲了解更多信息,请参看本章后面的"事务、锁定和并行"部分。
SQL Server有一个自动检查点机制,该机制确保完成了的事务规则的从SQL Server磁盘缓存中写入事务日志文件。从数据库的上一个检查点算起,任何修改过的缓存页将被写入一个检查点。向数据库上的这些缓存页(dirty pages,污损页)写入检查点,确保了所有已完成的事务被写到磁盘上。该过程缩短了发生失败(例如能量损耗,power outage)时修复系统的所花的时间。该设置可以用SQL Server Enterprise Manager修改,还可以用Transact-SQL修改(sp_configure系统存储程序)。
备份和恢复数据 Microsoft SQL Server提供了几种备份数据的选择: 完全的数据库备份 要进行完全的数据库备份,使用BACKUP DATABASE语句或者"备份向导"(Backup Wizard)。 微分备份(Differential backup) 在经过完全的数据库备份以后,定期使用BACKUP DATABASE WITH DIFFERENTIAL语句或者"备份向导"来备份改变过的数据和索引页。
事务日志备份 Microsoft SQL Server中的事务日志有一个独立的数据库。该数据库仅在备份或者被删除以后才填充。SQL Server 7.0中的缺省设置是事务日志自动增长,直到它用完了所有的可用空间或者达到其设置的最大空间。当事务日志过满时,它会生成一个错误并且阻止任何的数据修改,直到该日志被备份或者被删除。其他的数据库不会受到影响。可以用BACKUP LOG语句或者"备份向导"进行事务日志备份。
文件或者文件组备份 SQL Server可以备份文件或者文件组。欲知详情,请参看SQL Server联机图书。 备份可以在数据库正在使用的时候进行,这样就可以使那些必须不断运行的系统进行备份。SQL Server 7.0的备份过程和数据结构已经大大的改进,可以使备份在对事务吞吐量影响最小的情况下达到最大的数据传输率。
Oracle和SQL Server都需要一个特殊的日志文件格式。在SQL Server中,这些叫做备份设备的文件是用SQL Server Enterprise Manager、Transact-SQL的sp_addumpdevice存储程序或者等价的SQL-DMO命令创建的。
虽然备份可以通过手工操作进行,但是建议你使用SQL Server Enterprise Manager和/或者"数据库维护计划向导"进行定期的备份,或者基于数据库活动进行备份。 应用事务日志备份和/或者微分备份,一个数据库可以按时储存在一个完全备份数据库(设备)的特定的点上。数据库使用备份中包含的信息恢复数据。可以用SQL Server Enterprise Manager、Transact-SQL (RESTORE DATABASE)或者SQL-DMO进行恢复。
就像你可以关掉Oracle存档器以跳过备份一样,在Microsoft SQL Server中,db_owner组中的成员可以强制事务日志在检查点发生的时候抹去目录。可以用SQL Server Enterprise Manager(删除检查点上的日志),Transact-SQL(sp_dboption存储过程)或者SQL-DMO来完成。 网络
Oracle SQL*Net支持Oracle数据库和其客户端的网络连接。它们通过透明网络层数据流协议进行通信,并且允许用户运行许多不同的网络协议,而不需要编写任何特殊的代码。SQL*Net未包含在核心Oracle数据库软件产品中。
在Microsoft SQL Server中,Net库(网络库)支持客户端和服务器的连接,它们通过列表数据流协议进行通信。这使得可以同时和运行名字管道(Named Pipes)、TCP/IP套接字或者其他交互处理机制(Inter-Process Communication、IPC)的客户端连接。SQL Server CD-ROM包含了所有的客户端Net库,不需要另外购买这些产品了。
SQL Server Net库选项可以在安装后修改。客户端网络工具为运行Windows NT、Windows 95、 或者Windows 98的客户端配置缺省的Net库和服务器连接信息。除非在ODBC数据源的安装过程中改变或者在ODBC连接字串中显式的标明,所有的ODBC客户端也使用同样的Net库和服务器连接信息。欲了解关于Net库的更多信息,请参看SQL Server联机手册。 数据库安全性和角色(Database Security and Roles) 为了把你的Oracle应用程序完整的移植到Microsoft SQL Server 7.0上,你需要明白SQL Server是如何实现数据库的安全性和角色的。
登录账号 登录账号允许一个用户访问SQL Server数据或者管理选项。登录账号允许用户仅仅是登录到SQL Server上并且显示那些可以让访客(guest)访问的数据库。(guest账号不是缺省设置的,必须创建)
SQL Server提供了两种类型的登录安全性。Windows NT验证模式(也称为集成的)和SQL Server验证模式(也称为标准的)。SQL Server 7.0还支持标准的和集成的安全性的联合使用,称为混合的。
Windows NT验证模式在检验登录连接时使用Windows NT内建的安全机制,并且依赖用户的Windows NT安全信任。用户不需要为SQL Server输入登录ID和口令--其登录信息直接从网络连接上截取。当发生连接时,一个条目就被写入syslogins表,并且在Windows NT和SQL Server之间加以验证。这种方式叫做可信连接,其工作原理同两台Windows NT服务器之间的可信关系是一样的。此功能同Oracle中用于用户账号的IDENTIFIED EXTERNALLY选项是类似的。
SQL Server验证模式在用户请求访问SQL Server时要求用户输入登录ID和口令。这种方式又叫做不信任连接。此功能同Oracle中用于用户账号的IDENTIFIED BY PASSWORD选项是类似的。使用标准安全模式,登录仅仅提供用户访问SQL Server数据库引擎的能力,不允许用户访问用户数据库。
欲了解关于安全机制的更多信息,请参看SQL Server联机手册。 组、角色和许可(Groups, roles, and permissions)
Microsoft SQL Server和Oracle都使用许可来加强数据库安全性。SQL Server用语句级的许可来限制创建新的数据库对象的能力。(同Oracle一样)
SQL Server还提供了对象级的许可。像Oracle一样,对象级所有权是分配给对象的创建者的,并且不能过户。在其他用户可以访问对象之前必须给予他们对象级的许可。sysadmin 固定服务器角色、db_owner 固定数据库角色、或者db_securityadmin 固定数据库角色的成员同样可以给予其他用户对某个用户对象的许可。
SQL Server语句级和对象级的许可可以直接赋予数据库用户账号。但是,通常更简单的方法是赋予数据库角色管理员许可。SQL Server角色用来赋予或者撤消数据库用户组的特权(同Oracle角色非常相象)。角色(Roles)是一个带有特殊数据库的数据库对象。每次安装都有一些固定的服务器角色,这些角色在数据库之上工作。一个固定服务器角色的例子是sysadmin。Windows NT组也可以作为SQL Server登录,就像数据库用户一样。许可可以赋予一个Windows NT组或者一个Windows NT用户。
一个数据库可以有任意数量的角色或者Windows NT组。缺省的public角色总是可以在任何一个数据库上找到,这些角色不能被清除。public角色的功能很像Oracle中的PUBLIC账号。每个数据库用户都是public角色的成员。在public角色之外,一个数据库用户还可以是任意数量角色的成员。Windows NT用户或组也可以是任意数量角色的成员,同样,他们也是public角色的成员。
数据库用户和Guest账号(Database users and the guest account) 在Microsoft SQL Server中,一个用户登录账号必须被授权使用一个数据库和它的对象。登录账号可以用下面方法中的一种来访问数据库: 登录账号可以被设定为数据库用户。 登录账号可以在数据库中使用访客(Guest)账号。 一个Windows NT组登录可以被映射为一个数据库角色。作为该组成员的单个Windows NT账号就可以连接到数据库上。
db_owner或者db_accessadmin角色或者sysadmin固定服务器角色的成员可以创建数据库用户账号角色。一个账号可以包含一些参数:SQL Server登录ID,数据库用户名(可选)、以及一个角色名(可选)。数据库用户名不一定要和用户登录ID一样。如果未提供一个数据库用户名,则用户的登录ID和数据库用户名就是一样的。如果未提供一个角色名,则该数据库用户就仅是public角色的成员。在创建了数据库用户之后,用户可以根据需要分配任意的角色。 db_owner或者db_accessadmin角色的成员也可以创建一个guest账号。guest账号允许任意有效的SQL Server登录账号访问一个数据库,甚至不需要有数据库用户账号。缺省情况下,guest账号继承了分配给public角色的特权;但是,这些特权可以修改为多于或者少于public账号的特权。
一个Windows NT用户或者一个组的账号可以被赋予访问数据库的权利,就像SQL Server登录所能做的一样。如果一个Windows NT组的一个成员连接数据库,该用户会收到分配给这个组的许可。如果该用户是不止一个Windows NT组的成员,则他会收到所有这些组对数据库的权限的集合。
Sysadmin角色(The sysadmin role) Microsoft SQL Server sysadmin固定服务器角色中的成员拥有与Oracle DBA组中的成员相似的权限。在SQL Server 7.0中,如果SQL Server是安装在一台Windows NT计算机上,那么以sa SQL Server验证模式登录的账号缺省为该角色的成员,也就是本地Administrator组中的成员。一个sysadmin角色中的成员可以增加或者删除Windows NT用户和组,以及SQL Server登录账号。典型的该角色中的成员有以下职责: 安装SQL Server。
配置服务器和客户端。 创建数据库。* 设立登录权限和用户许可。* 将数据导入或者导出SQL Server数据库。* 备份和恢复数据库。* 执行和维护复制。 安排无人值守的操作。* 监视和调试SQL Server的性能。* 诊断系统问题。 *这些项目可以委派给其他安全性角色和用户。
在SQL Server 7.0中,对于sysadmin固定服务器角色中的成员能干什么是没有限制的。因此,该角色中的成员可以通过一个特殊的SQL Server实例访问任何数据库、所有的对象(包括数据)。像一个Oracle DBA一样,有一些命令和系统程序是只有sysadmin角色中的成员才能使用的。
db_owner角色
虽然Microsoft SQL Server数据库在使用上和Oracle表空间很相似,但是它们在管理上是不一样的。每个SQL Server数据库都是一个自持的管理域。每个数据库都标明了数据库所有者(dbo)。该用户总是db_owner固定数据库角色的成员。其他用户也可以是db_owner角色的成员。该角色中的所有成员都可以管理与他的数据库相关的管理任务。(不象在Oracle中,DBA管理所有数据库的管理任务)。这些管理任务包括: 管理数据库访问。 修改数据库设置(只读,单用户,等等)。 备份和恢复数据库目录。 授予和取消数据库许可。 创建和删除数据库对象。 db_owner角色中的成员可以在他们的数据库上做任何事情。分配给该角色的大多数权利被分给一些固定数据库角色,或者也可以赋予数据库用户。在数据库上行使db_owner特权并不需要赋予sysadmin服务器范围特权。
安装和配置Microsoft SQL Server
了解了Oracle和SQL Server之间基本结构上的差异以后,你就可以开始进行移植过程的第一步了。SQL Server Query Analyzer将用来运行下面的脚本:
- 使用Windows NT基于软件的RAID或者基于硬件的RAID第五级来创建一个足够放下你的所有数据的逻辑驱动器。对空间的估算可以通过计算被Oracle系统、临时文件以及应用程序表空间占用的文件空间大小来进行。
-
使用Windows NT基于软件的RAID或者基于硬件的RAID第一级创建一个第二逻辑驱动器来放事务日志。该驱动器的大小起码应该和在线恢复以及后滚表空间的总的大小一致。
-
使用SQL Server Enterprise Manager创建一个和Oracle应用程序表空间名字一样的数据库。(示例应用程序使用的数据库名字叫做USER_DB)标明文件位置,使它们分别和你在第一步以及第二步中为数据和事务创建的磁盘位置一致。如果你使用多个Oracle表空间,不需要也建议你不要创建多个SQL Server数据库,RAID会自动为你分配的。
-
创建SQL Server登录账号:
USE MASTER
EXEC SP_ADDLOGIN STUDENT_ADMIN, STUDENT_ADMIN
EXEC SP_ADDLOGIN DEPT_ADMIN, DEPT_ADMIN
EXEC SP_ADDLOGIN ENDUSER1, ENDUSER1
GO
|
-
为数据库添加角色:
USE USER_DB
EXEC SP_ADDROLE DATA_ADMIN
EXEC SP_ADDROLE USER_LOGON
GO
|
-
为角色授予许可:
GRANT CREATE TABLE, CREATE TRIGGER, CREATE VIEW,
CREATE PROCEDURE TO DATA_ADMIN
GO
|
- 增加作为数据库用户账号的登录账号:
EXEC SP_ADDUSER ENDUSER1, ENDUSER1, USER_LOGON
EXEC SP_ADDUSER DEPT_ADMIN, DEPT_ADMIN, DATA_ADMIN
EXEC SP_ADDUSER STUDENT_ADMIN, STUDENT_ADMIN, DATA_ADMIN
GO
|
定义数据库对象
Oracle数据库对象(表、视图和索引)可以很容易的移植到Microsoft SQL Server上,这是因为两个数据库都基本遵循SQL-92标准,该标准承认对象定义。把Oracle SQL的表、索引和视图的定义转换为SQL Server的表、索引和视图的定义只需要做相对简单的语法改变。下表指出了Oracle和Microsoft SQL Server之间的数据库对象的某些不同之处。
类别 |
Microsoft SQL Server |
Oracle |
列数 |
1024 |
254 |
行尺寸 |
8060 byte, 外加16 byte用来指向每一个text或者image列 |
无限制 (每行只允许有一个long或者long raw) |
最大行数 |
无限制 |
无限制 |
BLOB类型存储 |
行中存储一个16-byte 指针。数据存储在其他数据页。 |
每表一个long或者long raw。 必须在行的结尾。数据存储在行的同一个块里。 |
分簇表索引 |
每表一个 |
每表一个(index-organized tables) |
未分簇的表索引 |
每表249 |
无限制 |
在单一索引中的最大索引列数 |
16 |
16 |
索引中列值的最大长度 |
900 bytes |
? block |
表名约定 |
[[[server.]database.]owner.] table_name |
[schema.]table_name |
视图名约定 |
[[[server.]database.]owner.] table_name |
[schema.]table_name |
索引名约定 |
[[[server.]database.]owner.] table_name |
[schema.]table_name |
假设你是从一个Oracle脚本或者程序开始的,该脚本或者程序用来创建你的数据库对象。拷贝你的脚本或者程序并且进行如下修改。这些修改将在本部分的其他地方加以讨论。该例子是从示例应用程序脚本Oratable.sql和Sstable.sql中截取的:
-
确保数据库对象标识遵循Microsoft SQL Server命名法则。你可能只需要修改索引的名字。
-
修改数据存储参数使之能在SQL Server下工作。如果你使用RAID,就不需要任何存储参数了。
-
修改Oracle约束定义使之能在SQL Server中工作。如果需要的话,创建一个触发器以支持外部键DELETE CASCADE语句。如果表跨数据库的话,使用触发器来增强外部键的关系。
-
修改CREATE INDEX语句以利用分簇的索引。
-
使用数据转换服务来创建新的CREATE TABLE语句。回顾该语句,注意Oracle数据类型是如何映射到SQL Server数据类型上的。
-
清除所有的CREATE SEQUENCE语句。在CREATE TABLE或者ALTER TABLE语句中使用同等列来替换顺序的使用。
-
如果需要的话,修改CREATE VIEW语句。
-
清除所有对同义字的引用。
-
评估对Microsoft SQL Server临时表的使用和其在你的应用程序中的用处。
-
把所有的Oracle的CREATE TABLE…AS SELECT命令改为SQL Server的SELECT…INTO语句。
- 评估潜在的对用户定义规则、数据类型和缺省的使用。
数据对象标识符
下表比较了Oracle和Microsoft SQL Server是如何处理对象标识符的。在许多情况下,当移植到SQL Server上时,你不需要改变对象的名字。
Oracle |
Microsoft SQL |
1-30 字符长度。 数据库名称:最多8个字符长度。 数据库连接名称:最多128个字符长度。 |
1-128 Unicode字符长度。 临时表名称:最多116个字符。 |
标识符的名称必须用:字母、包含文字数字的字符、或者字符_, $, 和 #开头 |
标识符名称可以用:字母数字字符、或者_开头,实际上可以用任何字符开头。
如果标识符用空格开头,或者包含了不是_、@、#、或者$的字符,你必须用[](定界符)包围标识符名称
如果一个对象用下面这些字符开头: @ 则表明该对象是一个本地变量。 # 则该对象是一个本地临时对象。 ## 则该对象是一个全局临时对象
|
表空间名必须唯一. |
数据库名必须唯一 |
标识符名在用户账号(计划,Schema)范围内必须唯一。 |
标识符名在数据库用户账号范围内必须唯一 |
列名在表和视图范围内必须唯一。 |
列名在表和视图范围内必须唯一。 |
索引名在用户账号(Schema)范围内必须唯一。 |
索引名在数据库表名范围内必须唯一 |
修饰表名
当访问存在于你的用户账号中的表时,该表可以简单的通过未经限制的表名来选中。访问其他Oracle计划中的表就需要把该计划的名字作为前缀加到表名上,两者之间用点号(.)隔开。Oracle同义字可以提供更高的位置透明度。
涉及到表时,Microsoft SQL Server采用一种不同的方法。因为一个SQL Server登录账号可以在多个数据库中用同一个名字创建一个表,所以采用下面的方法来访问表和视图:[[数据库名字]所有者名字]表名]
用……访问一个表 |
Oracle |
Microsoft SQL Server |
你的用户账号 |
SELECT * FROM STUDENT |
SELECT * FROM USER_DB.STUDENT_ ADMIN.STUDENT |
其他模式(schema) |
SELECT * FROM STUDENT_ADMIN.STUDENT |
SELECT * FROM OTHER_DB.STUDENT_ ADMIN.STUDENT |
这是一些为Microsoft SQL Server表和视图命名的指导方针:
-
使用数据库名字和用户名字是可选的。如果一个表只通过名字加以引用(例如,STUDENT),SQL Server在当前数据库中以当前用户帐号搜索该表。如果没有找到,就在数据库中寻找由dbo的保留用户名拥有的具有同样名字的对象。表名在同一个数据库中的同一个用户帐号下必须是唯一的。
- 同一个SQL Server登录账号可以在多个数据库中拥有同样名字的表。例如,ENDUSER1账号拥有下列数据库对象:USER_DB.ENDUSER1.STUDENT和OTHER_DB.ENDUSER1.STUDENT。这里所加的限制是数据库用户名而不是SQL Server登录名,因为两者不一定要一样。
同时,这些数据库的其他用户可以有同样名字的对象:
-
USER_DB.DBO.STUDENT
-
USER_DB.DEPT_ADMIN.STUDENT
-
USER_DB.STUDENT_ADMIN.STUDENT
- OTHER_DB.DBO.STUDENT
因此,建议你在引用数据库对象时包含所有者的名字。如果应用程序有多个数据库,建议你再把数据库名字也包含在引用中。如果查询跨越多个服务器,还要包括服务器名。
SQL Server的每个连接都有一个当前数据库上下文,这是在登录时用USE语句设置的。例如,假设有下面的场景:
-
一个用户,使用ENDUSER1账号,登录到USER_DB数据库上。用户请求STUDENT表。SQL Server就查询ENDUSER1.STUDENT表。如果找到,SQL Server就在USER_DB.ENDUSER1.STUDENT表上做要求的数据库操作。如果在ENDUSER1数据库账号下没有找到该表,SQL Server就为该数据库以dbo账号搜寻USER_DB.DBO.STUDENT。如果还是找不到该表,SQL Server就返回一个错误消息,指出该表不存在。
-
如果另一个用户,例如DEPT_ADMIN拥有该表,则该表必须以数据库用户名作为前缀(DEPT_ADMIN.STUDENT)。另外,数据库名字缺省为在当前上下文中的数据库名字。
- 如果被引用的表在另一个数据库中,则数据库名必须作为引用的一部分。例如,要访问在OTHERDB数据库中由ENDUSER1拥有的表STUDENT,就需要用OTHER_DB.ENDUSER1.STUDENT来引用。
可以在数据库和表名之间加两个点号来省略对象的所有者名。例如,如果应用程序引用STUDENT_DB..STUDENT,SQL Server就做如下搜寻:
-
STUDENT_DB.current_user.STUDENT
- STUDENT_DB.DBO.STUDENT
如果应用程序一次只使用一个数据库,在做对象引用时省略数据库名字,这样的话,该应用程序可以方便的用于其他数据库。所有的对象引用都隐含的访问当前所用的数据库。这对于你要想在同一台服务器上维持一个测试数据库和一个产品数据库时很有用
创建表
因为Oracle和SQL Server都支持SQL-92条目级(entry-level)的关于标识RDBMS对象的协议,CREATE TABLE的语法是相似的。
Oracle |
Microsoft SQL |
CREATE TABLE [schema.]table_name ( {col_name column_properties [default_expression] [constraint [constraint [...constraint]]]| [[,] constraint]} [[,] {next_col_name | next_constraint}...] ) [Oracle Specific Data Storage Parameters] |
CREATE TABLE [server.][database.][owner.] table_name ( {col_name column_properties[constraint [constraint [...constraint]]]| [[,] constraint]} [[,] {next_col_name | next_constraint}...] ) [ON filegroup_name] |
Oracle数据库对象名字是不分大小写的。在Microsoft SQL Server中,数据库对象的名字可以是大小写敏感的,这要看安装时的设置。
当SQL Server第一次设置的时候,缺省的排序顺序是字典顺序,区分大小写。(可以用SQL ServerSetup来做不同的设置)因为Oracle对象的名字总是唯一的,你在把数据库对象移植到SQL Server上时不会遇到任何的麻烦。建议你把Oracle和SQL Server中的所有的表和列的名字都写成大写的以避免万一有用户安装了区分大小写的SQL Server时出问题。
表和索引存储参数
对于Microsoft SQL Server,使用RAID通常可以简化数据库对象的放置。在表的结构中集成了一个SQL Server的分簇的索引,就像一个Oracle索引组织表一样。
Oracle |
Microsoft SQL |
CREATE TABLE DEPT_ADMIN.DEPT ( DEPTVARCHAR2(4) NOT NULL, DNAMEVARCHAR2(30) NOT NULL, CONSTRAINT DEPT_DEPT_PK PRIMARY KEY (DEPT) USING INDEX TABLESPACE USER_DATA PCTFREE 0 STORAGE (INITIAL 10K NEXT 10K MINEXTENTS 1 MAXEXTENTS UNLIMITED), CONSTRAINT DEPT_DNAME_UNIQUE UNIQUE (DNAME) USING INDEX TABLESPACE USER_DATA PCTFREE 0 STORAGE (INITIAL 10K NEXT 10K MINEXTENTS 1 MAXEXTENTS UNLIMITED) ) PCTFREE 10PCTUSED 40 TABLESPACE USER_DATA STORAGE (INITIAL 10K NEXT 10K MINEXTENTS 1 MAXEXTENTS UNLIMITED FREELISTS 1) |
CREATE TABLE USER_DB.DEPT_ADMIN.DEPT ( DEPTVARCHAR(4) NOT NULL, DNAMEVARCHAR(30) NOT NULL, CONSTRAINT DEPT_DEPT_PK PRIMARY KEY CLUSTERED (DEPT), CONSTRAINT DEPT_DNAME_UNIQUE UNIQUE NONCLUSTERED (DNAME) ) |
用SELECT语句创建表
使用Oracle,一个表可以用任何有效的SELECT命令创建。Microsoft SQL Server提供了同样的功能,但是语法不一样。
Oracle |
Microsoft SQL |
CREATE TABLE STUDENTBACKUP AS SELECT * FROM STUDENT |
SELECT * INTO STUDENTBACKUP FROM STUDENT |
要SELECT…INTO能够起作用,必须将使用该程序的数据库的选项select into/bulkcopy设定为true。(数据库所有者可以用SQL Server Enterprise Manager或者Transact-SQL的sp_dboption系统存储程序来设置该选项)。用sp_helpdb系统存储过程来检查数据库的状态。如果select into/bulkcopy未设定为true,你仍然可以用SELECT语句拷贝到临时表中,就像下面这样:
SELECT * INTO #student_backup FROM user_db.student_admin.student
当用SELECT.. INTO语句来创建新的表时,其参考的完整性定义不会转换到新的表中。
将select into/bulkcopy设定为true的要求可能会使移植的过程变得复杂。如果你必须用SELECT语句拷贝数据到表中,请首先创建表,然后再用INSERT INTO…SELECT语句来载入该表。对于Oracle和SQL Server来说,语法是一样的,也不需要设置任何数据库选项。
视图
在Microsoft SQL Server中创建视图的语法同Oracle一样。
Oracle |
Microsoft SQL |
CREATE [OR REPLACE] [FORCE | NOFORCE] VIEW [schema.]view_name [(column_name [, column_name]...)] AS select_statement [WITH CHECK OPTION [CONSTRAINT name]] [WITH READ ONLY] |
CREATE VIEW [owner.]view_name [(column_name [, column_name]...)] [WITH ENCRYPTION] AS select_statement [WITH CHECK OPTION] |
SQL Server视图要求表必须存在,并且视图的所有者必须有访问在SELECT语句中标明的数据库的权限(同Oracle中的FORCE选项相似)。
缺省情况下,不会检查视图上的数据修改语句来判定受影响的行是否在视图的范围内。要检查所有的修改,请使用WITH CHECK OPTION。对于WITH CHECK OPTION主要的不同之处在于,Oracle将其作为约束来定义,而SQL Server不是。此外,两者的功能是一样的。
在定义视图的时候,Oracle提供了一个WITH READ ONLY选项。SQL Server应用程序可以用仅向视图用户提供SELECT权限的方法来达到同样的结果。
SQL Server和Oracle视图都支持派生列、使用数学表达式、函数以及常量表达式。SQL Server的某些特殊的不同之处是:
-
如果数据修改只影响一个基本表,则数据修改语句(INSERT或者UPDATE)可以存在于多个视图上。单个语句中的数据修改语句不能用在超过一个表上。
-
READTEXT或者WRITETEXT不能用于视图中的列。
-
不能使用ORDER BY、COMPUTE、FOR BROWSE、或者COMPUTE BY子句。
- 在视图中不能使用INTO关键字。
当一个视图是和一个外部连接一起定义的,并且查询限定在外部接合点的内部表上时,SQL Server和Oracle的结果会有所不同。在大多数情况下,Oracle视图很容易转化为SQL Server视图。
Oracle |
Microsoft SQL |
CREATE VIEW STUDENT_ADMIN.STUDENT_GPA (SSN, GPA) AS SELECT SSN, ROUND(AVG(DECODE(grade ,'A', 4 ,'A+', 4.3 ,'A-', 3.7 ,'B', 3 ,'B+', 3.3 ,'B-', 2.7 ,'C', 2 ,'C+', 2.3 ,'C-', 1.7 ,'D', 1 ,'D+', 1.3 ,'D-', 0.7 ,0)),2) FROM STUDENT_ADMIN.GRADE GROUP BY SSN |
CREATE VIEW STUDENT_ADMIN.STUDENT_GPA (SSN, GPA) AS SELECT SSN, ROUND(AVG(CASE grade WHEN 'A' THEN 4 WHEN 'A+' THEN 4.3 WHEN 'A-' THEN 3.7 WHEN 'B' THEN 3 WHEN 'B+' THEN 3.3 WHEN 'B-' THEN 2.7 WHEN 'C' THEN 2 WHEN 'C+' THEN 2.3 WHEN 'C-' THEN 1.7 WHEN 'D' THEN 1 WHEN 'D+' THEN 1.3 WHEN 'D-' THEN 0.7 ELSE 0 END),2) FROM STUDENT_ADMIN.GRADE GROUP BY SSN |
索引
Microsoft SQL Server提供了分簇和未分簇的索引结构。这些索引是由来自于一个叫做B-tree的树型结构中的页构成的(同Oracle中的B-tree索引结构相似)。起始页(“根”级)说明了表中值的范围。“根”级页中的每一个范围指向其他页(判断节点),该节点包含了表中值的更小的范围。以此类推,该节点又可以指向其他的判断节点,这样就缩小了搜索的范围。树型结构的最后一级叫做“叶”级。
分簇的索引
分簇的索引在Oracle中是以索引组织表的形式实现的。一个分簇的索引是一个物理的包含在一个表中的索引。表和索引分享同一块存储空间。分簇的索引按索引顺序物理的重排数据行,建立起中间判断节点。索引的“叶”页包含了真实的表数据。这个结构允许每个表只有一个分簇的索引。Microsoft SQL Server为表自动的创建一个分簇的索引,无论该表设置了PRIMARY KEY还是UNIQUE约束。分簇的索引对下面这些是有用的:
-
主键(Primary keys)
-
不能被更新的列。
- 返回一个值的范围的查询,使用诸如BETWEEN、>、>=、<、以及<=这样的操作符,例如:
SELECT * FROM STUDENT WHERE GRAD_DATE
BETWEEN '1/1/97' AND '12/31/97'
SELECT * FROM STUDENT WHERE LNAME = 'SMITH'
- 被用做排序操作的列(ORDER BY、GROUP BY)
例如,在STUDENT表上,在ssn的主键上包含一个未分簇的索引是很有用的,而分簇的索引可以在lname、fname(last name、first name)上创建,因为这是一种常用的区分学生的方法。
- 分布表上的更新行为可以防止出现“热点”。热点通常是由于多个用户向一个有上升键的表中填充而引起的。这样的情景经常导致行级别的锁定。
删除和重建一个分簇的索引在SQL Server中是一种很普通的重新组织表的技术。这是一种确保数据页在磁盘上是连续的以及重建表中的一些可用空间的简单的方法。这同Oracle中导出、删除以及导入一个表是很相似的。
一个SQL Server分簇的索引与Oracle的簇在根本上是不一样的。一个Oracle的簇。一个Oracle的簇是两个或者更多的表的物理集合,它们分享同一个数据块,使用一个公共的列来作为簇键。SQL Server没有与Oracle簇相似的结构。
作为一个普遍的原则,在表上定义一个分簇的索引将提高SQL Server的性能并且加强空间管理。如果你不知道对于给定表的查询和更新模式,你可以在主键上创建一个分簇的索引。
下表摘录自示例应用程序的源代码。请注意SQL Server“簇”化索引的使用。
Oracle |
Microsoft SQL |
CREATE TABLE STUDENT_ADMIN.GRADE ( SSNCHAR(9) NOT NULL, CCODEVARCHAR2(4) NOT NULL, GRADEVARCHAR2(2) NULL, CONSTRAINT GRADE_SSN_CCODE_PK PRIMARY KEY (SSN, CCODE) CONSTRAINT GRADE_SSN_FK FOREIGN KEY (SSN) REFERENCES STUDENT_ADMIN.STUDENT (SSN), CONSTRAINT GRADE_CCODE_FK FOREIGN KEY (CCODE) REFERENCES DEPT_ADMIN.CLASS (CCODE) ) |
CREATE TABLE STUDENT_ADMIN.GRADE ( SSNCHAR(9) NOT NULL, CCODEVARCHAR(4) NOT NULL, GRADEVARCHAR(2) NULL, CONSTRAINT GRADE_SSN_CCODE_PK PRIMARY KEY CLUSTERED (SSN, CCODE), CONSTRAINT GRADE_SSN_FK FOREIGN KEY (SSN) REFERENCES STUDENT_ADMIN.STUDENT (SSN), CONSTRAINT GRADE_CCODE_FK FOREIGN KEY (CCODE) REFERENCES DEPT_ADMIN.CLASS (CCODE) ) |
未分簇的索引
在未分簇的索引中,索引数据和表数据在物理上是分开的,并且表中的行并不是按顺序存储在索引中的。你可以把Oracle索引定义移植到Microsoft SQL Server未分簇的索引定义上(就像在下表中显示的一样)。可是,考虑到性能的缘故,你可能希望选择表的其中一个索引把它创建为分簇的索引。
Oracle |
Microsoft SQL |
CREATE INDEX STUDENT_ADMIN.STUDENT_ MAJOR_IDX ON STUDENT_ADMIN.STUDENT (MAJOR) TABLESPACE USER_DATA PCTFREE 0 STORAGE (INITIAL 10K NEXT 10K MINEXTENTS 1 MAXEXTENTS UNLIMITED) |
CREATE NONCLUSTERED INDEX STUDENT_MAJOR_IDX ON USER_DB.STUDENT_ ADMIN.STUDENT (MAJOR) |
索引语法和命名
在Oracle中,一个索引的名字在一个用户账号中是唯一的。在In Microsoft SQL Server,一个索引的名字在一个表名中必须是唯一的,但是不必在用户名和数据库名中唯一。因此,在SQL Server中创建或者删除索引时,你必须说明表名和索引名。另外,SQL Server的DROP INDEX语句可以一次删除多个索引。
Oracle |
Microsoft SQL |
CREATE [UNIQUE] INDEX [schema].index_name ON [schema.]table_name (column_name [, column_name]...) [INITRANS n] [MAXTRANS n] [TABLESPACE tablespace_name] [STORAGE storage_parameters] [PCTFREE n] [NOSORT]
DROP INDEX ABC;
|
CREATE [UNIQUE] [CLUSTERED | NONCLUSTERED] INDEX index_name ON table (column [,…n]) [WITH [PAD_INDEX] [[,] FILLFACTOR = fillfactor] [[,] IGNORE_DUP_KEY] [[,] DROP_EXISTING] [[,] STATISTICS_NORECOMPUTE] ] [ON filegroup]
DROP INDEX USER_DB.STUDENT.DEMO_IDX, USER_DB.GRADE.DEMO_IDX
|
索引数据存储参数
Microsoft SQL Server功能选项中的FILLFACTOR选项在很多方面与Oracle中的PCTFREE变量相似。当表的尺寸增加的时候,索引页也相应改变以容纳新的数据。索引必须自己进行重新组合以容纳新的数据。只有在创建索引的时候,才使用填充参数百分比,而且在这之后也不加以维护。
FILLFACTOR选项(0~100)控制着在创建索引时应该留下多少空间。如果没有表明参数,就使用缺省参数,该参数是0,表示将完全填充索引的“叶”页,并且在每个判断节点为至少一个条目留下空间(如果有两个条目,则表示是一个不唯一的“簇”化索引)。
一个较低的填充因数将会减少索引页的分裂,但是会增加B-tree结构的层数。较高的填充因数能更有效的使用索引页空间,只需要较少的磁盘I/O来访问索引数据,并且将会减少B-tree结构的层数。
PAD_INDEX选项表示,填充因数也将应用到判断节点页上,就象要用在索引的数据页上一样。
虽然在 Oracle中可能需要调整PCTFREE参数以优化性能。但是在CREATE INDEX语句中很少使用FILLFACTOR参数。填充因数是为性能优化而提供的。但是它仅仅在一个表上为已有数据创建索引时才有用,并且只有在你能精确的预测数据在未来的变化时才有用。
如果你将Oracle中的PCTFREE参数设为0,可以考虑将它设为100。这在表中不会发生数据输入和修改(只读表)时是很有用的。如果填充因数设为100,服务器将创建这样一个索引,它的每一页都是完全填满的。
忽略重复的关键字
无论在Oracle还是在Microsoft SQL Server中,用户都不能在一个或者一些唯一索引的列中输入重复的值。这样做将会产生一个错误消息。然而,SQL Server允许开发人员选择INSERT或者UPDATE语句将如何处理这个错误。
如果在CREATE INDEX语句中使用了IGNORE_DUP_KEY,并且执行了一个创建重复的关键字的INSERT或者UPDATE语句,SQL Server将给出一个警告信息,并且忽略重复行。如果没有使用IGNORE_DUP_KEY,SQL Server将给出一个错误信息,并且后滚整个INSERT语句。如果需要了解关于这个选项的更多信息,请参看SQL Server联机手册。
使用临时表
一个Oracle应用程序也许必须创建一个暂时存在的表。应用程序必须确保在某个时候删除所有为此目的创建的表。如果应用程序不这样做,那么表空间将很快变得混乱,难以管理。
Microsoft SQL Server提供了临时表数据库对象,这个表就是为上面提到的目的创建的。这样的表总是在tempdb数据库中创建的。表的名字决定了该表在tempdb数据库中要存在多长时间。
表名 |
描述 |
#table_name |
这个本地临时表只在用户会话或者创建它的过程的生命期内存在。在用户退出登录或者创建它的过程完成以后,该表自动删除。该表不能在多个用户之间共享。其它数据库用户不能访问该表。在该表上不能赋予或者撤消许可。 |
##table_name |
该表也典型的存在于用户会话或者创建它的过程的生命期内。但该表可以被多个用户共享。在最后一个引用它的用户会话断开以后,该表自动删除。所有其它数据库的用户都可以访问该表。在该表上不能赋予或者撤消许可。 |
可以为临时表定义索引。但是只能在那些在tempdb中显明的创建的表上创建视图,这些表的名字前不加#或者##前缀。下面的例子显示了一个临时表和相应的索引的创建。当用户退出的时候,表和索引就自动删除了。
SELECT SUM(ISNULL(TUITION_PAID,0)) SUM_PAID, MAJOR INTO #SUM_STUDENT
FROM USER_DB.STUDENT_ADMIN.STUDENT GROUP BY MAJOR
CREATE UNIQUE INDEX SUM STUDENT IDX ON #SUM STUDENT (MAJOR)
|
在你的程序代码中使用临时表,你可以发现它的好处。
数据类型
同Oracle比起来,Microsoft SQL Server在数据库类型的选择上更强大。在Oracle和SQL Server数据类型之间有很多可能的转换方式。我们建议你使用DTS向导来自动创建新的CREATE TABLE语句。需要的时候,你还可以修改它。
Oracle |
Microsoft SQL |
CHAR |
推荐使用char。 char 类型的列比varchar列的访问速度要稍微快一点,因为char列使用一个固定的存储长度。 |
VARCHAR2 和 LONG |
varchar 或者 text. (如果在你的Oracle列中数据值的长度小于或等于8000 bytes ,使用varchar;否则,你必须使用text。) |
RAW 和 LONG RAW |
varbinary 或者 image. (如果在你的Oracle列中数据值的长度小于或等于8000 bytes,使用varbinary;否则,你必须使用image。) |
NUMBER |
如果整数在1到255之间, 使用tinyint. 如果整数在-32768到32767之间,使用smallint. 如果整数在-2,147,483,648到2,147,483,647之间,使用int. 如果你需要浮点型的数,使用numeric (精确且可以定标). 注意: 不要使用float或者real, 因为可能会发生截断(Oracle NUMBER和 SQL Server numeric 不会截断). 如果你不确定,使用numeric; 它同Oracle NUMBER数据类型非常相似。 |
DATE |
datetime. |
ROWID |
使用identity列类型 |
CURRVAL, NEXTVAL |
使用identity列类型, 以及@@IDENTITY, IDENT_SEED(), 和IDENT_INCR() 函数。 |
SYSDATE |
GETDATE(). |
USER |
USER. |
使用Unicode数据
Unicode规范定义了一个编码方案,该方案使用单一编码方式为全世界范围内业务上使用的所有字符编码。所有的计算机都能使用单一的Unicode编码把Unicode数据中的位模式转换成为字符。这个方案确保了在所有的计算机上,同样的位模式转换为同样的字符。数据可以自由的从一个数据库或者一台计算机传送到另一个上面,而不用考虑接受系统能否把位模式正确的转换成字符。
使用一个字节来表示字符的方法有一个问题,就是这种数据类型只能表示256个字符。这样就为不同的语言产生了多个编码规范(或者叫做代码页)。这样做也不可能处理日文或者韩文这样有上千个字符的语言。
Microsoft SQL Server把在SQL Server中安装了代码页的字符的位模式转换成char,varchar,或者text类型的列。客户端则使用操作系统安装的代码页来解释字符的位模式。现在有很多不同的代码页。有些字符只在某些代码页上才有,在别的代码页上就没有。某些字符在某些代码页上定义为一种位模式,在另外一些代码页上又定义为另一种位模式。如果你要建立一个必须处理各种语言的国际系统时,为那些满足语言要求或者多个国家的计算机挑选代码页就变得非常困难。同样,在和一个使用不同代码页的系统连接时,确保每一台计算机都能正确的实现字符转换也非常困难。
Unicode规范使用双字节编码方案解决了这个问题。使用双字节编码,就有足够的空间来覆盖最广泛使用的商业语言了。因为所有的Unicode系统都采用同样的位模式来代表所有的字符,在从一个系统转移到另一个系统的时候,就不会发生字符转换不正确的问题了。
在SQL Server中,nchat,nvarchar和ntext数据类型支持Unicode数据。如果需要了解关于SQL Server数据类型的更多信息,请参看SQL Server联机手册。
用户定义数据类型
可以为model数据库或者单用户数据库创建用户定义数据类型。如果是为model定义用户定义数据类型,则该数据类型可以被定义之后所有新创建的用户数据库使用。用户定义数据类型是通过sp_addtype系统存储程序来定义的。如果需要了解更多信息,请参看SQL Server联机手册。
你可以在CREATE TABLE和ALTER TABLE语句中使用用户定义数据类型,并且为它绑定缺省方式和规则。如果在表的创建过程使用用户定义数据类型时显明的定义了nullability,则它比在数据定义时定义的nullability优先级高。
下例显示了如何创建用户定义数据类型。参数是用户类型名字,数据类型和nullability。
sp_addtype gender_type, 'varchar(1)', 'not null'
go
|
这个能力对于解决与Oracle表创建脚本移植到SQL Server上相关的问题是很有用的。例如,要增加一个Oracle的DATE数据类型是非常简单的。
sp_addtype date, datetime |
这个功能不能用在那些需要变长度的数据类型上,例如Oracle数据类型NUMBER。如果这样做,系统将会返回一个错误信息,告诉你需要标明数据长度。
sp_addtype varchar2, varchar
Go
Msg 15091, Level 16, State 1
You must specify a length with this physical type.
|
Microsoft timestamp列
timestamp列使得BROWSE模式修改和游标修改操作更有效。timestamp是这样一个数据类型,含有timestamp列的行有输入或者修改操作时,该数据类型自动修改。
timestamp列中的值不是按照实际的日期和时间存储的,而是作为binary(8)或者varbinary(8)存储的,这个值表示表中一行发生的事件的频率。一个表只能有一个timestamp列。
如果要了解更多信息,请参看SQL Server联机手册。
对象级许可
Microsoft SQL Server对象特权可以向任何其他数据库用户、数据库组以及public角色授予、拒绝授予、和撤消。SQL Server不允许对象的所有者授予其他用户、组或者public角色ALTER TABLE和CREATE INDEX特权,这一点与Oracle不同。这些特权必须被对象所有者保留。
GRANT语句创建一个安全系统的入口许可,该许可允许当前数据库中的一个用户可以操作当前数据库中的数据,或者执行特定的Transact-SQL语句。GRANT语句的语法在Oracle和SQL Server中是一样的。
DENY语句在安全系统中创建一个条目,拒绝当前数据库中的一个安全账号的许可,并且禁止该安全账号继承自该账号所属的组或者角色成员的许可。Oracle中没有DENY语句。REVOKE语句清除以前授予给当前数据库中一个用户的许可或者拒绝其许可。
Oracle |
Microsoft SQL |
GRANT {ALL [PRIVILEGES][column_list] | permission_list [column_list]} ON {table_name [(column_list)] | view_name [(column_list)] | stored_procedure_name} TO {PUBLIC | name_list } [WITH GRANT OPTION] |
GRANT {ALL [PRIVILEGES] | permission[,…n]} { [(column[,…n])] ON {table | view} | ON {table | view}[(column[,…n])] | ON {stored_procedure | extended_procedure} } TO security_account[,…n] [WITH GRANT OPTION] [AS {group | role}]
REVOKE [GRANT OPTION FOR] {ALL [PRIVILEGES] | permission[,…n]} { [(column[,…n])] ON {table | view} | ON {table | view}[(column[,…n])] | {stored_procedure | extended_procedure} } {TO | FROM} security_account[,…n] [CASCADE] [AS {group | role}]
DENY {ALL [PRIVILEGES] | permission[,…n]} { [(column[,…n])] ON {table | view} | ON {table | view}[(column[,…n])] | ON {stored_procedure | extended_procedure} } TO security_account[,…n] [CASCADE]
|
如果需要了解关于对象级许可的更多信息,请参看 SQL Server联机手册。
在Oracle中,REFERENCES特权只能授予用户。SQL Server则允许把该特权授予数据库用户和数据库组。INSERT、UPDATE、DELETE和SELECT特权的授予在Oracle和SQL Server中以同样的方式处理。
加强数据完整性和商业规则
加强数据完整性确保数据库中数据的质量。规划表时重要的两个步骤是鉴定列中值的有效性和如何加强列中数据的完整性。数据完整性可以分为四类,它们是以不同的方式进行加强的。
完整性类型 |
如何强制 |
Entity integrity |
PRIMARY KEY constraint UNIQUE constraint IDENTITY property |
Domain integrity |
Domain DEFAULT definition FOREIGN KEY constraint CHECK constraint Nullability |
Referential integrity |
Domain DEFAULT definition FOREIGN KEY constraint CHECK constraint Nullability |
User-defined integrity |
All column- and table-level constraints in CREATE TABLE Stored procedures Triggers |
实体完整性(Entity Integrity)
实体完整性把特定表中的一行作为一个唯一的实体加以定义。实体完整性通过索引、UNIQUE约束、PRIMARY KEY约束或者IDENTITY特性加强表中标识列或者主关键字的完整性,
为约束命名
你总是可以显式的命名你的约束。如果你不这样做,Oracle和Microsoft SQL Server将使用不同的命名惯例来隐式的为约束命名。在命名上的不同会为你的移植带来不必要的麻烦。在删除约束或者使约束失效时将会出现问题,因为约束必须通过名字来删除。显式命名约束的语法在Oracle和SQL Server中是一样的。
CONSTRAINT constraint_name |
主键和唯一列
SQL-92标准要求主关键字中的所有值都是唯一的并且该列不允许空值。Oracle和Microsoft SQL Server都是通过自动创建唯一的索引这种方式来强制实现唯一性的,无论是否定义了PRIMARY KEY或者UNIQUE约束。
虽然可以创建一个未分簇的索引,但是SQL Server缺省的为主关键字创建一个分簇的索引。Oracle在主关键字上的索引可以通过删除约束或者使约束失效的方法来清除,而SQL Server的索引只能通过删除约束来实现。
无论在哪种RDBMS中,其他关键字都可以定义一个UNIQUE约束。可以在任何表中定义多个UNIQUE约束。UNIQUE约束列可以为空。在SQL Server中,除非另外说明,否则将缺省的创建一个未分簇的索引
在移植你的应用程序时,重要的是注意到SQL Server只允许完全唯一的关键字(单个或者多个列索引)中有一行是NULL值的,而Oracle则允许完全唯一的关键字中任意行是NULL值。
Oracle |
Microsoft SQL |
CREATE TABLE DEPT_ADMIN.DEPT (DEPT VARCHAR2(4) NOT NULL, DNAME VARCHAR2(30) NOT NULL, CONSTRAINT DEPT_DEPT_PK PRIMARY KEY (DEPT) USING INDEX TABLESPACE USER_DATA PCTFREE 0 STORAGE ( INITIAL 10K NEXT 10K MINEXTENTS 1 MAXEXTENTS UNLIMITED), CONSTRAINT DEPT_DNAME_UNIQUE UNIQUE (DNAME) USING INDEX TABLESPACE USER_DATA PCTFREE 0 STORAGE ( INITIAL 10K NEXT 10K MINEXTENTS 1 MAXEXTENTS UNLIMITED) ) |
CREATE TABLE USER_DB.DEPT_ADMIN.DEPT (DEPTVARCHAR(4) NOT NULL, DNAMEVARCHAR(30) NOT NULL, CONSTRAINT DEPT_DEPT_PK PRIMARY KEY CLUSTERED (DEPT), CONSTRAINT DEPT_DNAME_UNIQUE UNIQUE NONCLUSTERED (DNAME) ) |
增加和清除约束
使约束失效可以改善数据库性能,并且使数据复制过程更加流畅。例如,当你在一个远程站点上重建或者复制表中的数据时,你不用重复约束检查,因为数据的完整性是在它原来输入数据库时就检查过的。你可以编制Oracle应用程序来使能或者失效约束(除了PRIMARY KEY和UNIQUE)。你可以在Microsoft SQL Server的ALTER TABLE语句中使用CHECK和WITH NOCHECK来达到同样的目的。
下面的插图显示了该过程的比较。
在SQL Server中,你在NOCHECK子句上使用ALL关键字来推迟所有的表的约束。
如果你的Oracle应用程序使用CASCADE选项来失效或者删除PRIMARY KEY或者UNIQUE约束,你也许需要重写某些代码,因为CASCADE选项同时失效或者删除父类和子类的完整性约束。
这是关于语法的示例:
DROP CONSTRAINT DEPT_DEPT_PK CASCADE |
SQL Server应用程序必须修改成首先删除子类的约束,然后删除父类的约束。例如,为了删除DEPT表上的PRIMARY KEY约束,STUDENT.MAJOR和CLASS.DEPT相关列的外部关键字必须被删除。这是语法的示例:
ALTER TABLE STUDENT
DROP CONSTRAINT STUDENT_MAJOR_FK
ALTER TABLE CLASS
DROP CONSTRAINT CLASS_DEPT_FK
ALTER TABLE DEPT
DROP CONSTRAINT DEPT_DEPT_PK
|
ALTER TABLE增加和删除约束的语法在Oracle和SQL Server中的语法是一样的。
生成连续的数字值
如果你的Oracle应用程序使用SEQUENCEs,该选项可以很容易的改变以利用Microsoft SQL Server的IDENTITY特性。
类别 |
Microsoft SQL Server |
语法 |
CREATE TABLE new_employees ( Empid int IDENTITY (1,1), Employee_Name varchar(60), CONSTRAINT Emp_PK PRIMARY KEY (Empid) ) If increment interval is 5: CREATE TABLE new_employees ( Empid int IDENTITY (1,5), Employee_Name varchar(60), CONSTRAINT Emp_PK PRIMARY KEY (Empid) ) |
每个表拥有的标识列 |
一个 |
允许空值 |
否 |
使用缺省约束、值 |
不能使用. |
强制唯一 |
是 |
在INSERT, SELECT INTO 或者bulk copy 语句完成以后,查询最大的当前标识数 |
@@IDENTITY (function) |
返回创建标识列时指定的种子值 |
IDENT_SEED('table_name') |
返回创建标识列时指定的增加值 |
IDENT_INCR('table_name') |
SELECT语法 |
当在SELECT, INSERT, UPDATE, 和DELETE语句中引用带有IDENTITY属性的列时,可以在列名上使用IDENTOTY关键字 |
虽然IDENTITY特性使一个表中的行记数自动化,但是不同的表,如果每一个都有自己的标识列,可以产生同样的值。这是因为IDENTITY特性只能在使用它的表上被担保为唯一的。如果一个应用程序必须生成一个在整个数据库,或者全世界每一台联网计算机上的每一个数据库中唯一的标识列,可以使用ROWGUIDCOL特性,uniqueidentifier数据类型,以及NEWID函数。SQL Server使用全局独立的标识列来并入复制,确保该行在所有该表的拷贝中是唯一的标识。
如果需要了解关于创建和修改标识列的更多信息,请参看SQL Server联机手册。
域完整性
域完整性约束对给定列的有效入口。域完整性是通过限制类型(通过数据类型),格式(通过CHECK约束),或者可能值的范围(通过REFERENCE和CHECK约束)来实现的。
DEFAULT和CHECK约束
Oracle把缺省(default)当作一个列属性来对待,而Microsoft SQL Server把缺省当作一个约束来对待。SQL Server的DEFAULT约束可以包含整型值,内建的不带参数的函数(niladic函数),或者NULL。
要很方便的移植Oracle的DEFAULT列属性,你应该在SQL Server中定义列级别的不使用约束名字的DEFAULT约束。SQL Server为每一个DEFAULT约束生成一个唯一的名字。
用来定义CHECK约束的语法在Oracle和SQL Server中是一样的。搜索条件应该用布尔表达式来表示而且不能包含子查询。列级别的约束只能用在被约束列上,表级别的约束只能用在被约束的表中的列上。可以为一个表定义多个CHECK约束。SQL Server语法允许在一个CREATE TABLE语句中只创建一个列级别的CHECK约束,并且该约束可以有多个条件。
测试你修改过的CREATE TABLE语句的最好方式是使用SQL Server中的SQL Server Query Analyzer,并且只分析语法。输出结果将会指出任何错误。如果需要了解关于约束语法的更多信息,请参看SQL Server联机手册。
Oracle |
Microsoft SQL |
CREATE TABLE STUDENT_ADMIN.STUDENT ( SSN CHAR(9) NOT NULL, FNAME VARCHAR2(12) NULL, LNAME VARCHAR2(20) NOT NULL, GENDER CHAR(1) NOT NULL CONSTRAINT STUDENT_GENDER_CK CHECK (GENDER IN ('M','F')), MAJOR VARCHAR2(4) DEFAULT 'Undc' NOT NULL, BIRTH_DATE DATE NULL, TUITION_PAID NUMBER(12,2) NULL, TUITION_TOTAL NUMBER(12,2) NULL, START_DATE DATE NULL, GRAD_DATE DATE NULL, LOAN_AMOUNT NUMBER(12,2) NULL, DEGREE_PROGRAM CHAR(1) DEFAULT 'U' NOT NULL CONSTRAINT STUDENT_DEGREE_CK CHECK (DEGREE_PROGRAM IN ('U', 'M', 'P', 'D')), ... |
CREATE TABLE USER_DB.STUDENT _ADMIN.STUDENT ( SSN CHAR(9) NOT NULL, FNAME VARCHAR(12) NULL, LNAME VARCHAR(20) NOT NULL, GENDER CHAR(1) NOT NULL CONSTRAINT STUDENT_GENDER_CK CHECK (GENDER IN ('M','F')), MAJOR VARCHAR(4) DEFAULT 'Undc' NOT NULL, BIRTH_DATE DATETIME NULL, TUITION_PAID NUMERIC(12,2) NULL, TUITION_TOTAL NUMERIC(12,2) NULL, START_DATE DATETIME NULL, GRAD_DATE DATETIME NULL, LOAN_AMOUNT NUMERIC(12,2) NULL, DEGREE_PROGRAM CHAR(1) DEFAULT 'U' NOT NULL CONSTRAINT STUDENT_DEGREE_CK CHECK (DEGREE_PROGRAM IN ('U', 'M', 'P','D')), ... |
关于用户定义规则和缺省(default)要注意:关于Microsoft SQL Server规则和缺省的语法是考虑了向后兼容的,但是建议把CHECK约束和DEFAULT约束用在新的开发中。如果需要了解更多的信息,请参看SQL Server联机手册。
Nullability
Microsoft SQL Server和Oracle创建列约束来强制nullability。在Oracle的CREATE TABLE和ALTER TABLE语句中,列缺省是NULL,而不是NOT NULL。在Microsoft SQL Server,数据库和会话的设置可以越过在列定义中使用的数据类型的nullability。
你的所有的SQL脚本(无论是Oracle还是SQL Server),都必须显明的给出每一列的NULL和NOT NULL定义。要了解这个策略是如何实现的,请参看Oracle.sql和Sstable.sql这两个示例的表创建脚本。如果没有显明的定义,则列的nullability遵循如下的规则。
Null settings |
Description |
列是用一个用户定义数据类型定义的 |
SQL Server 使用在创建数据类型时指定的空值性。使用sp_help 系统存储过程来获取数据类型的缺省的空值性 |
列是用一个系统提供的数据类型定义的 |
如果系统提供的数据类型只有一个选项,则优先使用该选项。当前, bit数据类型只能被定义为NOT NULL。 如果任何会话设置为ON (用SET打开), 则: 如果ANSI_NULL_DFLT_ON是ON, 则指定为NULL. 如果ANSI_NULL_DFLT_OFF是ON, 则指定为NOT NULL. 如果任何数据库设置被修改过(用sp_dboption 系统存储过程修改), 则: 如果ANSI null default是true, 则指定为NULL. 如果ANSI null default是false, 则指定为NOT NULL |
NULL/NOT NULL 没有定义 |
当没有显明的定义时(ANSI_NULL_DFLT选项一个都没有设定),会话将被修改,并且数据库被设定为缺省(ANSI null default是false),然后SQL Server指定它为NOT NULL。 |
引用完整性
下表提供了一个用来定义referential完整性约束的语法比较。
约束 |
Oracle |
Microsoft SQL Server |
PRIMARY KEY |
[CONSTRAINT constraint_name] PRIMARY KEY (col_name [, col_name2 [..., col_name16]]) [USING INDEX storage_parameters] |
[CONSTRAINT constraint_name] PRIMARY KEY [CLUSTERED | NONCLUSTERED] (col_name [, col_name2 [..., col_name16]]) [ON segment_name] [NOT FOR REPLICATION] |
UNIQUE |
[CONSTRAINT constraint_name] UNIQUE (col_name [, col_name2 [..., col_name16]]) [USING INDEX storage_parameters] |
[CONSTRAINT constraint_name] UNIQUE [CLUSTERED | NONCLUSTERED](col_name [, col_name2 [..., col_name16]]) [ON segment_name] [NOT FOR REPLICATION] |
FOREIGN KEY |
[CONSTRAINT constraint_name] [FOREIGN KEY (col_name [, col_name2 [..., col_name16]])] REFERENCES [owner.]ref_table [(ref_col [, ref_col2 [..., ref_col16]])] [ON DELETE CASCADE] |
[CONSTRAINT constraint_name] [FOREIGN KEY (col_name [, col_name2 [..., col_name16]])] REFERENCES [owner.]ref_table [(ref_col [, ref_col2 [..., ref_col16]])] [NOT FOR REPLICATION] |
DEFAULT |
Column property, not a constraint DEFAULT (constant_expression) |
[CONSTRAINT constraint_name] DEFAULT {constant_expression | niladic-function | NULL} [FOR col_name] [NOT FOR REPLICATION] |
CHECK |
[CONSTRAINT constraint_name] CHECK (expression) |
[CONSTRAINT constraint_name] CHECK [NOT FOR REPLICATION] (expression) |
NOT FOR REPLICATION子句用来在复制过程中挂起列级别,FOREIGN KEY,以及CHECK约束。
外部键
定义外部关键字的语法在各种RDBMS中都是相似的。在外部关键字中标明的列数和每一列的数据类型必须和REFERENCES子句相匹配。一个输入到列中的非空的值必须在REFERENCES子句中定义表和列中存在,并且被提及的表的列必须有一个PRIMARY KEY或者UNIQUE约束。
Microsoft SQL Server约束提供了在同一个数据库中引用表的能力。要实现在数据库范围的应用完整性,可以使用基于表的触发器。
Oracle和SQL Server都支持自引用表,这种表中有对同一个表的一列或几列的引用。例如,CLASS表中的prereq列可以引用CLASS表中的ccode列以确保一个有效的课程编号是作为一个子句的先决条件输入的。
在Oracle中实现层叠式的删除和修改是使用CASCADE DELETE子句,而SQL Server用表触发器来实现同样的功能。如果需要了解更多的信息,请参看本章后面的“SQL语言支持”部分 。
用户定义的完整性
用户定义的完整性允许你定义特定的商业规则,该规则不属于其他完整性的范畴。
存储过程
Microsoft SQL Server存储程序用CREATE PROCEDURE语句来接受或者返回用户提供的参数。除临时存储程序以外,存储程序是在当前数据库中创建的。下表显示了Oracle和SQL Server的语法。
Oracle |
Microsoft SQL |
CREATE OR REPLACE PROCEDURE [user.]procedure [(argument [IN | OUT] datatype [, argument [IN | OUT] datatype] {IS | AS} block |
CREATE PROC[EDURE] procedure_name [;number] [ {@parameter data_type} [VARYING] [= default] [OUTPUT] ] [,…n] [WITH { RECOMPILE | ENCRYPTION | RECOMPILE, ENCRYPTION} ] [FOR REPLICATION] AS sql_statement […n] |
在SQL Server中,临时存储程序是在tempdb数据库中通过在procedure_name前加上数字标记来创建的。加一个数字标记(#procedure_name)表示是一个本地临时存储程序,加两个数字标记(##procedure_name)表示是一个全局临时程序。
一个本地临时程序只能被创建它的用户使用。执行本地临时程序的许可不能授予其他用户。本地临时程序在用户会话结束时自动删除。
一个全局的临时程序可以被所有的SQL Server用户使用。如果一个全局临时程序被创建了,所有的用户都可以访问它,并且不能显式的撤回许可。全局临时程序在最后一个用户会话结束的时候自动删除。
SQL Server存储程序可以有最多32级嵌套。嵌套层数在被调用的程序开始执行时增加,在被调用的程序结束运行时减少。
下面的例子说明了怎样用一个Transact-SQL存储程序来代替一个Oracle的PL/SQL封装函数。Transact-SQL的版本更简单一些,因为SQL Server的返回结果的能力是在一个存储程序中直接用SELECT语句设置的,不需要使用游标。
Oracle |
Microsoft SQL |
CREATE OR REPLACE PACKAGE STUDENT_ADMIN.P1 AS ROWCOUNT NUMBER :=0; CURSOR C1 RETURN STUDENT%ROWTYPE; FUNCTION SHOW_RELUCTANT_STUDENTS (WORKVAR OUT VARCHAR2) RETURN NUMBER; END P1; /
CREATE OR REPLACE PACKAGE BODY STUDENT_ADMIN.P1 AS CURSOR C1 RETURN STUDENT%ROWTYPE IS SELECT * FROM STUDENT_ADMIN.STUDENT WHERE NOT EXISTS (SELECT 'X' FROM STUDENT_ADMIN.GRADE WHERE GRADE.SSN=STUDENT.SSN) ORDER BY SSN;
FUNCTION SHOW_RELUCTANT_STUDENTS (WORKVAR OUT VARCHAR2) RETURN NUMBER IS WORKREC STUDENT%ROWTYPE; BEGIN IF NOT C1%ISOPEN THEN OPEN C1; ROWCOUNT :=0; ENDIF; FETCH C1 INTO WORKREC; IF (C1%NOTFOUND) THEN CLOSE C1; ROWCOUNT :=0; ELSE WORKVAR := WORKREC.FNAME||' '||WORKREC.LNAME|| ', social security number '||WORKREC.SSN||' is not enrolled in any classes!'; ROWCOUNT := ROWCOUNT + 1; ENDIF; RETURN(ROWCOUNT); |
CREATE PROCEDURE STUDENT_ADMIN.SHOW_ RELUCTANT_STUDENTS AS SELECT FNAME+'' +LNAME+', social security number'+ SSN+' is not enrolled in any classes!' FROM STUDENT_ADMIN.STUDENT S WHERE NOT EXISTS (SELECT 'X' FROM STUDENT_ADMIN.GRADE G WHERE G.SSN=S.SSN) ORDER BY SSN RETURN@@ROWCOUNT GO |
EXCEPTION WHEN OTHERS THEN IF C1%ISOPEN THEN CLOSE C1; ROWCOUNT :=0; ENDIF; RAISE_APPLICATION_ERROR(-20001,SQLERRM); END SHOW_RELUCTANT_STUDENTS; END P1; / |
|
SQL Server不支持与Oracle包或者函数相似的构造,也不支持在创建存储程序时的CREATE OR REPLACE选项。
延迟存储过程的执行
Microsoft SQL Server提供了WAITFOR,允许开发人员给定一个时间,时间段,或者事件来触发一个语句块、存储程序或者事务的执行。这是Transact-SQL对于Oracle中dbms_lock_sleep的等价。
WAITFOR {DELAY 'time' | TIME 'time'}
指示Microsoft SQL Server等待直到给定的时间过去以后再执行,最多可以到24小时。
在这里
指示Microsoft SQL Server等待,直到给定数量的时间过去以后才执行,最多可以设置到24小时。
需要等待的时间,时间可以是任何可接受的datetime数据类型的格式,或者可以作为一个本地变量给出。但是,不能指定datetime值的日期部分。
指示SQL Server等到指定的时间
例如:
BEGIN
WAITFOR TIME '22:20'
EXECUTE update_all_stats
END
|
指定存储程序中的参数
要在一个存储程序中指定一个参数,可以使用下面给出的语法。
Oracle |
Microsoft SQL |
Varname datatype DEFAULT <value>; |
{@parameter data_type} [VARYING] [= default] [OUTPUT] |
触发器(Triggers)
Oracle和Microsoft SQL Server都有触发器,但它们在执行上有些不同。
描述 |
Oracle |
Microsoft SQL Server |
每表可以有的触发器数 |
无限制 |
无限制 |
在INSERT, UPDATE, DELETE之前执行触发器 |
是 |
否 |
在INSERT, UPDATE, DELETE之后执行触发器 |
是 |
是 |
语句级触发器 |
有 |
有 |
行级触发器 |
有 |
无 |
在执行之前检查约束 |
是,除非触发器被取消 |
是。另外,这是DTS(Data Transformation Services)中的一个选项 |
在一个UPDATE或者DELETE触发器中提交旧的或者以前的值 |
:old |
DELETED.column |
在INSERT触发器中提交新值 |
:new |
INSERTED.column |
取消触发器 |
ALTER TRIGGER |
DTS中的选项 |
DELETED和INSERTED是SQL Server为触发器创建的概念上的表。该表在结构上同触发器定义于其上的表相似,并且保存了可能被用户的行动改变的旧的或者新的行中的值。该表将跟踪在Transact-SQL中的行一级的变化。这些表提供了与Oracle中的行级别的触发器同样的功能。当一个INSERT、UPDATE、或者DELETE语句在SQL Server中执行的时候,行被加入到触发器表中,而且是同时加入到INSERTED和DELETED表中。
INSERTED和DELETED表同触发器表是一样的。它们有同样的列名和数据类型。例如,如果在GRADE表中放置一个触发器,那么INSERTED和DELETED就有这样的结构。
GRADE |
INSERTED |
DELETED |
SSN CHAR(9) CCODE VARCHAR(4) GRADE VARCHAR(2) |
SSN CHAR(9) CCODE VARCHAR(4) GRADE VARCHAR(2) |
SSN CHAR(9) CCODE VARCHAR(4) GRADE VARCHAR(2) |
INSERTED和DELETED表可以被触发器检查以确定要执行什么样的触发器行动。INSERTED表同INSERT和UPDATE语句一起使用。DELETED表则和DELETE以及UPDATE语句一起使用。
UPDATE语句使用INSERTED和DELETED表,这是因为进行UPDATE操作时,SQL Server总是要删除旧的行,填入新的行。因此,执行UPDATE时,INSERTED表中的行总是DELETED表中的行的副本。
下面的例子使用INSERTED和DELETED表来代替PL/SQL中的行级别的触发器。一个完全的外部接合点被用来查询任意表中的所有行。
Oracle |
Microsoft SQL Server |
CREATE TRIGGER STUDENT_ADMIN.TRACK_GRADES AFTER INSERT OR UPDATE OR DELETE ON STUDENT_ADMIN.GRADE FOR EACH ROW BEGIN INSERT INTO GRADE_HISTORY( TABLE_USER, ACTION_DATE, OLD_SSN, OLD_CCODE, OLD_GRADE, NEW_SSN, NEW_CCODE, NEW_GRADE) VALUES (USER, SYSDATE, :OLD.SSN, :OLD.CCODE,
:OLD.GRADE, :NEW.SSN,
:NEW.CCODE, :NEW.GRADE), END;
|
CREATE TRIGGER STUDENT_ADMIN.TRACK_GRADES ON STUDENT_ADMIN.GRADE FOR INSERT, UPDATE, DELETE AS INSERT INTO GRADE_HISTORY( TABLE_USER, ACTION_DATE, OLD_SSN, OLD_CCODE, OLD_GRADE NEW_SSN, NEW_CCODE, NEW_GRADE) SELECT USER, GETDATE(), OLD.SSN, OLD.CCODE, OLD.GRADE, NEW.SSN, NEW.CCODE, NEW.GRADE FROM INSERTED NEW FULL OUTER JOIN DELETED OLD ON NEW.SSN = OLD.SSN |
你可以只在当前数据库中创建一个触发器,你也可以引用当前数据库之外的对象。如果你使用所有者名称来修饰触发器,那么就用同样的方法来修饰表名。
触发器可以最多嵌套32级。如果一个触发器改变了某个表,而该表有另外一个触发器,则第二个触发器是活动的,可以调用第三个触发器,如此类推。如果链上的任何触发器引起了死循环,则嵌套级别溢出,该触发器被取消。此外,如果某表结果上的一行上的一个更新触发器同时是另一行的更新,那么更新触发器将只执行一次。
Microsoft SQL Server的公布引用完整性(declarative referential integrity,DRI)没有提供跨数据库的引用完整性定义。如果需要跨数据库的完整性,可以使用触发器。
下面的语句在Transact-SQL触发器中是不被允许的。
-
CREATE 语句(DATABASE, TABLE, INDEX, PROCEDURE, DEFAULT, RULE, TRIGGER, SCHEMA, 和VIEW)
-
DROP 语句(TRIGGER, INDEX, TABLE, PROCEDURE, DATABASE, VIEW, DEFAULT, RULE)
-
ALTER 语句(DATABASE, TABLE, VIEW, PROCEDURE, TRIGGER)
-
TRUNCATE TABLE
-
GRANT, REVOKE, DENY
-
UPDATE STATISTICS
-
RECONFIGURE
-
UPDATE STATISTICS
-
RESTORE DATABASE, RESTORE LOG
-
LOAD LOG, DATABASE
-
DISK语句
- SELECT INTO (因为它创建一个表)
如果需要了解关于触发器的更多信息,请参看SQL Server联机手册。
事务、锁定和并行
本部分解释了在Oracle和Microsoft SQL Server事务是如何执行的,并且提供了所有数据库类型中锁定过程和并行问题之间的区别。
事务
在Oracle中,执行插入、更新或者删除操作时自动开始事务。一个应用程序必须给出一个COMMIT命令来保存数据库的所有修改。如果没有执行COMMIT,所有的修改都将后滚或者自动变成未完成的。
缺省情况下,Microsoft SQL Server在每次插入、更新或者删除操作之后自动执行一个COMMIT语句。因为数据是自动保存的,你不能后滚任何改变。你可以使用隐式的或者显式的事务模式来改变这个缺省行为。
隐式的事务模式允许SQL Server像Oracle一样运转,这种模式是用SET IMPLICIT_TRANSACTIONS ON语句激活的。如果该选项是ON并且当前没有突出的事务,则每一个SQL语句自动开始一个事务。如果有一个打开的事务,则不会有任何新的事务开始。打开的事务必须由用户用COMMIT TRANSACTION语句来显明的承诺,以使所有的改变生效并且释放所有的锁定。
一个显明的事务是一组由下述事务分隔符包围的SQL语句:
-
BEGIN TRANSACTION [transaction_name]
-
COMMIT TRANSACTION [transaction_name]
- ROLLBACK TRANSACTION [transaction_name | savepoint_name]
在下面这个例子中,英语系被改变为文学系。请注意BEGIN TRANSACTION和COMMIT TRANSACTION语句的用法。
Oracle |
Microsoft SQL |
INSERT INTO DEPT_ADMIN.DEPT (DEPT, DNAME) VALUES ('LIT', 'Literature') / UPDATE DEPT_ADMIN.CLASS SET MAJOR = 'LIT' WHERE MAJOR = 'ENG' / UPDATE STUDENT_ADMIN.STUDENT SET MAJOR = 'LIT' WHERE MAJOR = 'ENG' / DELETE FROM DEPT_ADMIN.DEPT WHERE DEPT = 'ENG' / COMMIT / |
BEGIN TRANSACTION
INSERT INTO DEPT_ADMIN.DEPT (DEPT, DNAME) VALUES ('LIT', 'Literature')
UPDATE DEPT_ADMIN.CLASS SET DEPT = 'LIT' WHERE DEPT = 'ENG'
UPDATE STUDENT_ADMIN.STUDENT SET MAJOR = 'LIT' WHERE MAJOR = 'ENG'
DELETE FROM DEPT_ADMIN.DEPT WHERE DEPT = 'ENG'
COMMIT TRANSACTION GO |
所有显明的事务必须用BEGIN TRANSACTION...COMMIT TRANSACTION语句封闭。SAVE TRANSACTION语句的功能同Oracle中的SAVEPOINT命令是一样的,在事务中设置一个保存点,这样就可以进行部分后滚(roll back)了。
事务可以嵌套。如果出现了这种情况,最外层的一对创建并提交事务,内部的对跟踪嵌套层。当遇到一个嵌套的事务时,@@TRANCOUNT函数就增加。通常,这种显然的事务嵌套发生在存储程序或者有BEGIN…COMMIT对互相调用的触发器中。尽管事务可以嵌套,但是对ROLLBACK TRANSACTION语句的行为的影响是很小的。
在存储过程和触发器中,BEGIN TRANSACTION语句的个数必须和COMMIT TRANSACTION语句的个数相匹配。包含不匹配的BEGIN TRANSACTION和COMMIT TRANSACTION语句的存储过程和触发器在运行的时候会产生一个错误消息。语法允许在一个事务中调用包含BEGIN TRANSACTION和COMMIT TRANSACTION语句对的存储过程和触发器。
只要情况许可,就应该把一个大的事务分成几个较小的事务。确保每个事务都在一个单独的batch中有完整的定义。为了把可能的并行冲突减到最小,事务既不应该跨越多个batch,也不应该等待用户输入。把多个事务组合到一个运行时间较长的事务中会给恢复时间带来消极的影响,并且还会造成并行问题。
在使用ODBC编程的时候,你可以通过使用SQLSetConnectOption函数来选择显式或者隐式的事务模式。究竟该选择哪种模式要视AUTOCOMMIT连接选项的情况而定。如果AUTOCOMMIT是ON(缺省的),你就是在显式模式中。如果AUTOCOMMIT是OFF,则在隐式模式下。
如果你通过SQL Server Query Analyzer或者其他查询工具使用脚本,你可以显式的包括一个上面提到的BEGIN TRANSACTION语句,也可以利用SET IMPLICIT_TRANSACTIONS ON语句来开始脚本。BEGIN TRANSACTION的方法更灵活一些,而隐式的方法更适合Oracle。
锁定和事务孤立
Oracle和Microsoft SQL Server有着很不一样的锁定和孤立策略。当你把Oracle应用程序转化为SQL Server应用程序的时候,你必须考虑到这些不同以确保应用程序的可伸缩性。
Oracle对所有读数据的SQL语句隐式或者显式的使用一种多版本一致模型(multiversion consistency model)。在这种模型中,数据读者在读数据行以前,缺省的既不获得一个锁定也不等待其他的锁定解开。当读者需要一个已经改变但别的写入者还没有提交的数据时,Oracle通过使用后滚段来重建一个数据行的快照的方法来重新创建旧的数据。
Oracle中的数据写入者在更新、删除或者插入数据时要请求锁定。锁定将一直维持到事务结束,并且禁止别的用户覆盖尚未提交的修改。
Microsoft SQL Server使用多粒度锁定,该锁定允许用事务来锁定不同类型的资源。为了把锁定的开销降到最低,SQL Server自动在与任务相配的层次上锁定资源。以较小的间隔尺寸锁定,例如行,增强了并行,但是管理开销较大,因为如果有许多行锁定,就必须维持多个锁定。以较大的间隔尺寸锁定,例如表,在并行方面是昂贵的,因为对整个表的锁定限制了其他事务对表中任何一部分的访问,但是管理开销却比较小,因为只要维持少数几个锁定。SQL Server可以锁定这些资源(按照间隔尺寸递增的顺序排列)。
资源 |
描述 |
RID |
行标识符。用于一个单行表的独立锁定。 |
Key |
键;索引中的一个行锁定。用于在一个可串行化的事务中保护键范围。 |
Page |
8-KB数据页或者索引页。 |
Extent |
相邻的八个数据页或者索引页的组。 |
Table |
整个表,包括所有数据和索引。 |
DB |
数据库。 |
SQL Server使用不同的锁定模式锁定资源,使用哪种模式决定了当前事务访问如何访问资源。
锁定模式 |
描述 |
Shared (S) |
用于那些不修改或者更新数据的操作(只读操作),例如一个SELECT语句。 |
Update (U) |
用于那些可以被更新的资源。防止当多个会话被读入、锁定,然后潜在的更新资源时发生一个公共形式的死锁。 |
Exclusive (X) |
用于数据修改操作,例如UPDATE、INSERT、或者DELETE。确保不会同时发生对同一个资源的多个修改操作。 |
Intent |
用于建立一个锁定层次。 |
Schema |
在一个依靠表的模式的操作执行时使用。有两种类型的模式锁定:schema stability (Sch-S)和schema modification (Sch-M)。 |
对于任何RDBMS都很重要的一点是,快速释放锁定以提供最大的并行性。你可以通过尽可能短的保持一个事务来确保快速释放锁定。如果可能的话,事务不应该跨越多个往返行程到服务器,也不应该包括用户“思考”的时间。如果你使用游标,你也应该使你的应用程序很快提取数据,因为未提取数据的扫描将在服务器上占据共享锁定,因此将阻碍更新。欲了解更多信息,请参看本章后面的“使用ODBC”部分。
改变缺省的锁定行为
Microsoft SQL Server和Oracle都允许开发人员使用非缺省的锁定和孤立行为。在Oracle中,最普通的机制是SELECT 命令的FOR UPDATE子句,SET TRANSACTION READ ONLY命令,以及显式的LOCK TABLE命令。
因为两者的锁定和孤立策略如此不同,所以很难在Oracle和SQL Server之间直接映射锁定选择。要更好的理解这一过程,重要的一点是理解SQL Server提供的修改缺省锁定行为的选择。
在SQL Server中,修改缺省锁定行为最常用的机制是SET TRANSACTION ISOLATION LEVEL语句和在SELECT和UPDATE语句中支持的锁定暗示。SET TRANSACTION ISOLATION LEVEL语句为一个用户会话的持续时间设定事务孤立级别。除非在一个SQL语句的FROM子句中标明了表级别的锁定暗示,否则这将变成该会话的缺省行为。事务孤立是这样设定的:
SET TRANSACTION ISOLATION LEVEL
{
READ COMMITTED
| READ UNCOMMITTED
| REPEATABLE READ
| SERIALIZABLE
}
|
缺省的SQL Server孤立级别。如果你使用这种选择,你的应用程序将不能读取其他事务还没有提交的数据。在这种模式下,一旦数据从页上读出,仍然要释放共享锁定。如果应用程序在同一个事务中重新读取同一个的数据区,将会看到别的用户做的修改。
如果设定了这种选择,事务将同其他事务孤立起来。如果你不希望在查询中看到其他用户做的修改,你可以设置事务的孤立级别为SERIALIZABLE。SQL Server将占据所有的共享锁定,直到事务结束。你可以通过在SELECT语句中表名的后面使用HOLDLOCK暗示来在一个更小的级别上取得同样的效果。
如果设定为这种选择,SQL Server读者将不会受到阻塞,就像在Oracle中一样。该选择实现了污损读取或者说是孤立级别为0的锁定,这意味着不使用任何共享锁定并且也不使用任何独占的锁定。当这个选项选定后,有可能会读到未提交的或者污损的数据;在事务结束以前,数据可能会改变,数据集中的行可能出现也可能消失。这个选项同一个事务中在所有SELECT语句中设定所有的表为NOLOCK的效果是一样的。这是四种孤立级别中限制性最小的一种。只有在你已经彻底的搞清楚了它将对你的应用程序结果的精确度有什么样的影响的前提下才能使用这种选择。
SQL Server有两种方法实现Oracle中的READ ONLY功能:
-
如果一个应用程序中的事务需要可重复读取的行为,你也许需要使用SQL Server提供的SERIALIZABLE孤立级别。
- 如果所有的数据库访问都是只读的,你可以设置SQL Server数据库选项为READ ONLY来提高性能。
SELECT…FOR UPDATE
当一个应用程序利用WHERE CURRENT OF 语法来在一个游标上实现定位更新或者删除时,首先使用Oracle中的SELECT…FOR UPDATE语句。在这种情况下,可以随意去掉FOR UPDATE子句,因为Microsoft SQL Server游标的缺省行为是“可更新的”。
缺省情况下,SQL Server游标在提取行下不占据锁定。SQL Server使用一种乐观的并行策略(optimistic concurrency strategy)来防止更新时相互之间的覆盖。如果一个用户试图更新或者删除一个读入游标后已经被修改过的行,SQL Server将给出一个错误消息。应用程序可以捕获该消息,并且重新进行适当的更新或者删除。要改变这个行为,开发人员可以在游标声明中使用SCROLL_LOCKS。
通常情况下,乐观的并行策略支持较高的并行性,所谓通常情况是指更新器之间冲突很少的情况。如果你的应用程序确实需要保证一行在被提取以后不会被修改,你可以在SELECT语句中使用UPDLOCK暗示。这个暗示不会阻碍别的读者,但是它禁止其他潜在的写入者也获得该数据的更新锁定。使用ODBC时,你可以通过使用SQLSETSTMTOPTION (…,SQL_CONCURRENCY)= SQL_CONCUR_LOCK来达到同样的目的。但是,其他的任何选择都将减少并行性。
表级别的锁定
Microsoft SQL Server可以用SELECT…table_name (TABLOCK)语句来锁定整个表。这和Oracle的 LOCK TABLE…IN SHARE MODE语句是一样的。该锁定允许其他人读取一个表,但是禁止他们修改该表。缺省情况下,锁定将维持到语句的结束。如果你同时加上了HOLDLOCK关键字(SELECT…table_name (TABLOCK HOLDLOCK)),表的锁定将一直维持到事务的结束。
可以用SELECT…table_name (TABLOCKX)语句在一个SQL Server表上设置一个独占的锁定。该语句请求一个表上的独占锁定。该锁定禁止其他人读取和修改该表,并且将一直维持到命令或者事务结束。这同Oracle中TABLE…IN EXCLUSIVE MODE语句的功能是一样的。
SQL Server没有为显式的锁定请求提供NOWAIT选项。
锁定升级
当一个查询向表请求行时,Microsoft SQL Server自动生成一个页级别的锁定。但是,如果查询请求表中的大部分行时,SQL Server将把锁定从页级别升级到表级别。这个过程叫做锁定升级。
锁定增加使那些产生较大结果集的表的扫描和操作更加有效,因为它减少了锁定的管理开销。缺少WHERE子句的SQL语句一般都要造成锁定增加。
在读取操作中,如果一个共享页级别的锁定增加为一个表锁定时,将应用一个共享表锁定(TABLOCK)。在下列情况下应用共享的表级别的锁定:
-
使用了HOLDLOCK或者SET TRANSACTION ISOLATION LEVEL SERIALIZABLE语句。
-
优化器选择了一个表的扫描。
- 表中积累的共享锁定的数目超过锁定升级的极限。
表中缺省的锁定升级的极限是200页,但是该极限可以用最小和最大范围定制为依赖于表尺寸的一个百分比。欲了解关于锁定升级极限的更多信息,请参看SQL Server联机手册。
在一个写操作中,当一个UPDATE锁定被升级为一个表锁定时,应用一个独占表锁定(TABLOCKX)。独占表锁定在下列情况下使用:
-
更新或者删除操作无索引可用。
-
表中有独占锁定的页的数目超过锁定升级上限。
- 创建了一个分簇的索引。
Oracle不能升级行级别的锁定,这将导致一些包含了FOR UPDATE子句的查询出问题。例如,假设STUDENT表有100,000行数据,并且一个Oracle用户给出下列语句:
SELECT * FROM STUDENT FOR UPDATE |
这个语句强制Oracle RDBMS依次锁定STUDENT表的一行;这将花去一段时间。它永远也不会要求升级锁定到整个表。
在SQL Server同样的查询是:
SELECT * FROM STUDENT (UPDLOCK) |
当这个查询运行的时候,页级别的锁定升级为表级别的锁定,后者更加有效并且明显要快一些。
死锁
当一个进程锁定了另一个进程需要的页或者表的时候,而第二个进程又锁定了第一个进程需要的一页,这个时候就会发生死锁。死锁也叫抱死。SQL Server自动探测和解决死锁。如果找到一个死锁,服务器将终止完成了抱死的用户进程。
在每次数据修改之后,你的程序代码需要检查1205号消息,这个消息指出一个死锁。如果返回这个消息,就说明发生了一个死锁并且事务已经后滚。在这种情况下,你的应用程序必须重新开始这个事务。
死锁一般可以通过一些简单的技术加以避免:
-
在你的应用程序的各部分以同样的顺序访问表。
-
在每个表上使用分簇的索引以强制一个显式的行顺序。
- 使事务简短。
欲了解详细信息,请参阅Microsoft Knowledge Base文章“Detecting and Avoiding Deadlocks in Microsoft SQL Server”
远程事务
在Oracle中执行远程事务,你必须通过一个数据库连接访问远程数据库节点。在SQL Server中,你必须访问一个远程服务器。远程服务器是一台运行SQL Server的服务器,用户可以用他们的本地服务器访问该服务器。当某个服务器被设置为远程服务器,用户就可以在其上使用系统过程和存储过程而不需要显式的登录到上面。
远程服务器是成对设置的。你必须配置两台服务器,使它们互相把对方当作远程服务器。每台服务器的名字都必须用sp_addlinkedserver系统存储过程或者SQL Server Enterprise Manager加到伙伴服务器上。
设置完远程服务器以后,你可以用sp_addremotelogin系统存储过程或者SQL Server Enterprise Manager来为那些必须访问远程服务器的用户设置远程登录账号。在这一步完成以后,你还必须赋予他们执行存储过程的权限。
然后用EXECUTE语句来在远程服务器上执行过程。这个例子在远程服务器STUDSVR1上执行了validate_student存储过程,并且将指明成功或者失败的返回情况存储在@retvalue1中:
DECLARE @retvalue1 int
EXECUTE @retvalue = STUDSVR1.student_db.student_admin.validate_student '111111111'
|
欲了解详细信息,请参看SQL Server联机手册。
分布事务
如果修改两个或者更多的数据库节点上的表,Oracle就自动初始化一个分布式事务。SQL Server分布式事务使用包含在SQL Server中的微软分布事务协调器(Microsoft Distributed Transaction Coordinator,MS DTC)中的两步提交服务(two-phase commit services)。
缺省情况下,SQL Server必须被通知参与分布事务。SQL Server参与一个MS DTC事务可以用下面方式中的任一种来存储:
-
BEGIN DISTRIBUTED TRANSACTION语句。该语句开始一个新的MS DTC事务。
- 一个客户端应用程序直接调用DTC事务接口。
在下例中,注意对本地表GRADE和远程表CLASS的分布式更新(使用一个class_name过程):
BEGIN DISTRIBUTED TRANSACTION
UPDATE STUDENT_ADMIN.GRADE
SET GRADE = 'B+' WHERE SSN = '111111111' AND CCODE = '1234'
DECLARE @retvalue1 int
EXECUTE @retvalue1 = CLASS_SVR1.dept_db.dept_admin.class_name '1234', 'Basketweaving'
COMMIT TRANSACTION
GO
|
如果程序不能完成事务,则通过ROLLBACK TRANSACTION语句终止该事务。如果程序失败或者参与的资源管理器失败,MS DTC终止该事务。MS DTC不支持分布式的存储点或者是SAVE TRANSACTION语句。如果一个MS DTC事务失败或者后滚,则整个事务退回到分布式事务的起点,而不理会任何存储点。
两步提交处理
Oracle和MS DTC两步提交机制在操作上是相似的。在SQL Server两步提交的第一步,事务管理器请求每一个参与的资源管理器准备提交。如果有任何资源管理器没有准备好,事务管理器就向与事务相关的所有成员广播一个异常中断决定。
如果所有的资源管理器都能成功的准备,事务管理器就广播一个提交决定。这是提交处理的第二步。当一个资源管理器准备好后,事务究竟是提交了还是失败了,这一点还是拿不准。MS DTC维持了一个连续的日志,因此它的提交或者中断决定都是持久的。如果某个资源管理器或者事务管理器失败了,则当它们重新连接上的时候,就能在那个拿不准的事务上协调了。
SQL语言支持
本部分简要介绍了Transact-SQL和PL/SQL语言语法上的相似和不同之处,并且给出了转换策略。
SELECT和数据操作声明
当你把Oracle DML语句和PL/SQL程序移植到SQL Server上时,请按下列步骤进行:
-
检查所有SELECT、INSERT、UPDATE、和DELETE语句是否有效。做任何需要的修改。
-
把所有的外部节点改为SQL-92外部节点语法
-
用适当的SQL Server函数代替Oracle函数
-
检查所有的比较操作符
-
用“+”代替“||”做字符串串联操作符。
-
用Transact-SQL程序代替PL/SQL程序
-
把所有的PL/SQL游标改为无游标SELECT语句或者Transact-SQL游标。
-
用Transact-SQL过程代替PL/SQL过程、函数和封装。
-
把PL/SQL触发器转换为Transact-SQL触发器。
- 使用SET SHOWPLAN语句来调试你的查询以获得高的性能。
SELECT statements语句
Oracle和Microsoft SQL Server用的SELECT语句的语法是类似的。
Oracle |
Microsoft SQL |
SELECT [/*+ optimizer_hints*/] [ALL | DISTINCT] select_list [FROM {table_name | view_name | select_statement}] [WHERE clause] [GROUP BY group_by_expression] [HAVING search_condition] [START WITH … CONNECT BY] [{UNION | UNION ALL | INTERSECT | MINUS} SELECT …] [ORDER BY clause] [FOR UPDATE] |
SELECT select_list [INTO new_table_] FROM table_source [WHERE search_condition] [ GROUP BY [ALL] group_by_expression [,…n] [ WITH { CUBE | ROLLUP } ] [HAVING search_condition] [ORDER BY order_expression [ASC | DESC] ]
In addition:
UNION Operator COMPUTE Clause FOR BROWSE Clause OPTION Clause
|
SQL Server不支持面向Oracle的基于开销的优化器暗示,必须把这些暗示清除掉。建议使用SQL Server的基于开销的优化器。欲了解详细信息,请参阅本章后面的“调试SQL语句”部分。
SQL Server不支持Oracle的START WITH…CONNECT BY子句。在SQL Server中,你可以用创建一个执行同样任务的存储过程来代替。
SQL Server不支持Oracle的INTERSECT和MINUS集合。SQL Server的EXISTS和NOT EXISTS子句可以完成同样的任务。
下面的例子使用INTERSECT操作符来为所有有学生的班级找到课程代码和课程名称。注意EXISTS操作符是怎样代替INTERSECT操作符的。两者返回的数据是一样的。
Oracle |
Microsoft SQL |
SELECT CCODE, CNAME FROM DEPT_ADMIN.CLASS INTERSECT SELECT C.CCODE, C.CNAME FROM STUDENT_ADMIN.GRADE G, DEPT_ADMIN.CLASS C WHERE C.CCODE = G.CCODE |
SELECT CCODE, CNAME FROM DEPT_ADMIN.CLASS C WHERE EXISTS (SELECT 'X' FROM STUDENT_ADMIN.GRADE G WHERE C.CCODE = G.CCODE) |
下例使用MINUS操作符来找出那些没有学生的班级。
Oracle |
Microsoft SQL |
SELECT CCODE, CNAME FROM DEPT_ADMIN.CLASS MINUS SELECT C.CCODE, C.CNAME FROM STUDENT_ADMIN.GRADE G, DEPT_ADMIN.CLASS C WHERE C.CCODE = G.CCODE |
SELECT CCODE, CNAME FROM DEPT_ADMIN.CLASSC WHERE NOT EXISTS (SELECT 'X' FROM STUDENT_ADMIN.GRADE G WHERE C.CCODE = G.CCODE) |
INSERT语句
Oracle和Microsoft SQL Server用的INSERT语句的语法是类似的。
Oracle |
Microsoft SQL |
INSERT INTO {table_name | view_name | select_statement} [(column_list)] {values_list | select_statement} |
INSERT [INTO] { table_name [ [AS] table_alias] WITH ( <table_hint_limited> […n]) | view_name [ [AS] table_alias] | rowset_function_limited }
{ [(column_list)] { VALUES ( { DEFAULT | NULL | expression }[,…n] ) | derived_table | execute_statement } } | DEFAULT VALUES |
Transact-SQL语言支持插入表和视图,但是不支持SELECT语句中的INSERT操作。如果你的Oracle程序这么做了,则必须修改。
Oracle |
Microsoft SQL |
INSERT INTO (SELECT SSN, CCODE, GRADE FROM GRADE) VALUES ('111111111', '1111',NULL) |
INSERT INTO GRADE (SSN, CCODE, GRADE) VALUES ('111111111', '1111',NULL) |
Transact-SQL的values_list参数提供了SQL-92标准的关键字DEFAULT,但这个在Oracle中是不支持的。当执行插入操作的时候,这个关键字给出了要用到的列的缺省值。如果给定的列没有缺省值,则插入一个NULL。如果该列不允许NULL,则返回一个错误消息。如果该列是作为一个时间片数据类型定义的,则插入下一个连续值。
关键字DEFAULT不能用于标识列。要产生下一个连续值,则有IDENTITY属性的列一定不能列入column_list或者values_clause。你不一定非要用DEFAUL关键字来获得一列的缺省值。在Oracle中,如果该列没有被column_list引用并且它有一个缺省值,则缺省值将放入列中。这是执行移植时最兼容的方法。
一个有用的Transact-SQL选项(EXECute procedure_name)是执行一个过程并且用管道把它的输出值输出到一个目标表或者视图。Oracle不允许你这样做。
UPDATE语句
因为Transact-SQL支持Oracle的UPDATE绝大多数语法,所以只需要很小的修改。
Oracle |
Microsoft SQL |
UPDATE {table_name | view_name | select_statement} SET [column_name(s) = {constant_value | expression | select_statement | column_list | variable_list} {where_statement} |
UPDATE { table_name [ [AS] table_alias] WITH ( <table_hint_limited> […n]) view_name [ [AS] table_alias] | rowset_function_limited } SET {column_name = {expression | DEFAULT | NULL} | @variable = expression | @variable = column = expression } [,…n]
{{[FROM {<table_source>} [,…n] ]
[WHERE <search_condition>] } | [WHERE CURRENT OF { { [GLOBAL] cursor_name } | cursor_variable_name} ] } [OPTION (<query_hint> [,…n] )] |
Transact-SQL的UPDATE语句不支持依赖SELECT语句的更新操作。如果你的Oracle程序这样做了,你可以把SELECT语句变成一个视图,然后在SQL Server的UPDATE语句中使用这个视图名字。请参看前面“INSERT”语句中的例子。
Oracle的UPDATE命令只能使用一个PL/SQL块中的程序变量。Transact-SQL语言不要求在使用变量时使用块。
Oracle |
Microsoft SQL |
DECLARE VAR1 NUMBER(10,2); BEGIN VAR1 := 2500; UPDATE STUDENT_ADMIN.STUDENT SET TUITION_TOTAL = VAR1; END; |
DECLARE @VAR1 NUMERIC(10,2) SELECT @VAR1 = 2500 UPDATE STUDENT_ADMIN.STUDENT SET TUITION_TOTAL=@VAR1 |
在SQL Server中,关键字DEFAULT可以用来把一列设置为它的缺省值。你不能用Oracle的UPDATE命令来设置一列为它的缺省值。
Transact-SQL和Oracle SQL都支持在一个UPDATE语句中使用子查询。但是Transact-SQL的FROM子句可以用来创建一个基于节点的UPDATE。这个能力是你的UPDATE语法更加可读并且在某种意义上提高了性能。
Oracle |
Microsoft SQL |
UPDATE STUDENT_ADMIN.STUDENT S SET TUITION_TOTAL = 1500 WHERE SSN IN (SELECT SSN FROM GRADE G WHERE G.SSN = S.SSN AND G.CCODE = '1234') |
Subquery:
UPDATE STUDENT_ADMIN.STUDENT S SET TUITION_TOTAL = 1500 WHERE SSN IN (SELECT SSN FROM GRADE G WHERE G.SSN = S.SSN AND G.CCODE = '1234')
FROM clause:
UPDATE STUDENT_ADMIN.STUDENT S SET TUITION_TOTAL = 1500 FROM GRADE G WHERE S.SSN = G.SSN AND G.CCODE = '1234'
|
DELETE语句
在大多数情况下,你不需要修改DELETE语句。但是如果你在Oracle中执行依赖SELECT语句的删除操作,你就必须进行修改,因为在Transact-SQL不支持这种功能。
Transact-SQL支持在WHERE子句中使用子查询,FROM子句也一样。后者可以产生更有效的语句。请参看前面在“UPDATE语句”中的例子。
Oracle |
Microsoft SQL |
DELETE [FROM] {table_name | view_name | select_statement} [WHERE clause] |
DELETE [FROM ] { table_name [ [AS] table_alias] WITH ( <table_hint_limited> […n]) | view_name [ [AS] table_alias] | rowset_function_limited }
[ FROM {<table_source>} [,…n] ]
[WHERE { <search_condition> | { [ CURRENT OF { { [ GLOBAL ] cursor_name } cursor_variable_name } ] } ] [OPTION (<query_hint> [,…n])]
|
TRUNCATE TABLE语句
在Oracle和Microsoft SQL Server中TRUNCATE TABLE语句的语法是相似的。TRUNCATE TABLE语句用来把一个表中的所有行清除掉,并且这个操作是不能后滚的。该表的结构和索引仍然存在。DELETE触发器不会被执行。如果该表被一个FOREIGN KEY引用,则该表不能被砍掉。
Oracle |
Microsoft SQL |
TRUNCATE TABLE table_name [{DROP | REUSE} STORAGE] |
TRUNCATE TABLE table_name |
在SQL Server中,这个语句只能由表的所有者给出。在Oracle中,只有当你是表的所有者或者有DELETE TABLE系统特权时才能使用该语句。
Oracle的TRUNCATE TABLE命令可以随意的释放被表中的行占据的存储空间。SQL Server的 TRUNCATE TABLE则总是回收被表中的数据和与之关联的索引占据的空间。
在identity和timestamp列中操作数据
Oracle序列是一种和任何给定的表或者列都不直接相关的数据库对象。一列和一个序列的关系是在应用程序中实现的,方法是把一个序列的值分配给一个列。因此在同序列一起工作的时候,Oracle并没有强化任何规则。但是在Microsoft SQL Server的标识列中,值是不能被更新的并且也不能使用DEFAULT关键字。
缺省情况下,数据不能直接插入到一个标识列中。标识列为新插入表的每一行自动产生一个唯一的、顺序的数字。这个缺省设置可以用下面的SET语句覆盖。
SET IDENTITY_INSERT table_name ON |
当IDENTITY_INSERT设置为ON时,用户就可以在新行的标识列中插入任何值。为了防止输入重复的值,必须在该列上创建一个唯一的索引。这个语句的目的是允许用户为一行重新创建一个偶然被删除的值。@@IDENTITY可以用来获取最后一个标识值。
TRUNCATE TABLE语句把一个标识列重新设置为它原来的SEED值。如果你不想为一列重新设置标识值,可以用不带WHERE子句的DELETE子句来代替TRUNCATE TABLE语句。你必须估计这会给你的Oracle移植带来什么样的影响,因为ORACLE SEQUENCES不会跟着TRUNCATE TABLE命令重新设置。
对时间信息(timestamp)列,你只能执行插入或者删除操作。如果你试图更新一个时间信息列,你将收到这样的错误消息。
Msg 272, Level 16, State 1 Can't update a TIMESTAMP column. |
锁定被请求的行
Oracle用FOR UPDATE子句来锁定在SELECT命令中指定的行。在Microsoft SQL Server中,你不需要使用它的等价子句,因为这是一个缺省行为。
行合计和计算子句
SQL Server的COMPUTE子句用来生成行合计函数(SUM、AVG、MIN、MAX、以及COUNT),这些函数看起来好象是查询结果的附加行。这允许你看到一组结果的详情和汇总。你可以为一个子群(subgroups)计算汇总值,还可以为同一组计算更多的合计函数。
Oracle的SELECT命令语法不支持COMPUTE子句。但是,SQL Server的COMPUTE子句就像在Oracle的SQL*Plus查询工具中能找到的COMPUTE命令一样的工作。
连接子句(Join clauses)
Microsoft SQL Server 7.0允许在一个连接子句中连接多达256个表,包括临时的和永久的表。在Oracle中,没有连接限制。
在Oracle中使用外部连接时,外部连接操作符(+)典型的放置在子列(foreign key)的后面。(+)依靠少数几个唯一值来识别该列。除非子列允许空值,否则总是这样的。如果子列允许空值,则(+)被放置在父列(PRIMARY KEY或者UNIQUE约束)上。你不能把(+)同时放在等号(=)的两边。
用SQL Server,你可以使用(*=)和(=*)外部连接操作符。(*)用来标识一个有很多唯一值的列。如果子列不允许空值,则(*)被放在等号的父列一边。在Oracle中,(*)的放置正好相反。不能把(*)同时放在等号的两边。
(*=)和(=*)被认为是传统的连接操作符。SQL Server也支持下面列出的SQL-92标准的连接操作符。建议你使用这种语法。SQL-92标准语法比(*)操作符更强大,限制更小。
Join操作 |
描述 |
CROSS JOIN |
这是两个表的交叉产物。如果在一个旧式的连接中没有指定WHERE子句,则返回同一行。在Oracle中,这种类型的连接叫做笛卡儿连接。 |
INNER |
该连接指定返回所有的内部行。任何不匹配的行将被丢弃。该连接同一个标准的Oracle表连接是一样的。 |
LEFT[OUTER] |
这种类型的连接指定返回右边表的所有外部行,即使没有发现任何匹配行。该操作符同Oracle外部连接(+)是一样的。 |
RIGHT[OUTER] |
这种类型的连接指定返回左边表的所有外部行,即使没有发现任何匹配行。该操作符同Oracle外部连接(+)是一样的。 |
FULL [OUTER] |
如果来自任一表的一行不匹配选择标准,指定该行被包括到结果集中,并且它的符合其它表的输出列被设定为NULL。这和把Oracle外部连接操作符放在“=”的两端是一样的(col1(+) = col2(+)),而在Oracle中,这是不允许的。 |
下面的例子返回所有学生都要上的课程的一个清单。在学生表和年级表之间定义的外部连接允许显示所有的学生,甚至那些没有参加任何课程的学生。在课程表上也有一个外部连接,该连接返回课程名字。如果课程表上没有加上外部连接,则不会返回那些没有参加任何课程的学生,因为他们的课程代码(CCDOE)是空值。
Oracle |
Microsoft SQL Server |
SELECT S.SSN AS SSN, FNAME, LNAME FROM STUDENT_ADMIN.STUDENT S, DEPT_ADMIN.CLASS C, STUDENT_ADMIN.GRADE G WHERE S.SSN = G.SSN(+) AND G.CCODE = C.CCODE(+) |
SELECT S.SSN AS SSN, FNAME, LNAME FROM STUDENT_ADMIN.GRADE G RIGHT OUTER JOIN STUDENT_ADMIN.STUDENT S ON G.SSN = S.SSN LEFT OUTER JOIN DEPT_ADMIN.CLASS C ON G.CCODE = C.CCODE |
用SELECT语句做表名
Microsoft SQL Server和Oracle都支持在执行查询时用SELECT语句作为表源。SQL Server需要一个别名(alias);而在Oracle中是否使用别名是可选的。
Oracle |
Microsoft SQL |
SELECT SSN, LNAME, FNAME, TUITION_PAID, SUM_PAID FROM STUDENT_ADMIN.STUDENT, (SELECT SUM(TUITION_PAID) SUM_PAID FROM STUDENT_ADMIN.STUDENT) |
SELECT SSN, LNAME, FNAME, TUITION_PAID, SUM_PAID FROM STUDENT_ADMIN.STUDENT, (SELECT SUM(TUITION_PAID) SUM_PAID FROM STUDENT_ADMIN.STUDENT) SUM_STUDENT |
读取和修改BLOBs
Microsoft SQL Server用text和image列来实现二进制大对象(binary large objects,BLOBs)。Oracle则用LONG和LONG RAW列来实现BLOBs。在Oracle中,SELECT命令可以查询LONG和LONG RAW列的值。
在SQL Server中,你可以使用标准的Transact-SQL语句或者专门的READTEXT语句来读取text和image列中的数据。READTEXT语句允许你读取text和image列的一部分。Oracle没有为LONG 和LONG RAW提供等价的语句。
READTEXT语句利用了一个text_pointer,该指针可以用TEXTPTR函数获取。TEXTPTR函数返回一个指向特定行中的text或者image的指针,如果查询有多于一行返回的话,则返回指向最后一行中的text或者image的指针。由于TEXTPTR返回的是一个16字节的二进制字符串,所以最好是声明一个内部变量来保持该文本指针,然后在READTEXT中使用这个变量。
READTEXT语句说明了有几个字节要返回。位于@@TEXTSIZE函数中的值,限制了返回的字符或者字节的数量,如果该值小于READTEXT声明的值,就用后者来代替。
SET语句可以用TEXTSIZE参数来说明以字节为单位的由一个SELECT语句返回的文本数据的尺寸。如果你设置一个大小为0的TEXTSIZE,则该尺寸就重设为缺省值(4 KB)。设置TEXTSIZE对@@TEXTSIZE函数有影响。当SQL_MAX_LENGTH 语句选项改变的时候,SQL Server ODBC自动设置TEXTSIZE参数。
在Oracle中,用UPDATE和INSERT命令来改变LONG和LONG RAW列中的值。在SQL Server,你可以用标准的UPDATE和INSERT语句,或者也可以用UPDATETEXT和WRITETEXT语句。UPDATETEXT和WRITETEXT都允许一个nonlogged选项,而且UPDATETEXT还允许对文本或者图形列的部分更新。
UPDATETEXT可以用来代替已有的数据,删除已有的数据,或者插入新数据。新插入的数据可以是一个常数值,表名,列名或者文本指针。
WRITETEXT语句完全覆盖它所影响的列中的任何已有的数据。用WRITETEXT来替换文本数据,用UPDATETEXT来修改文本数据。因为UPDATETEXT语句只修改一个文本或者图形值的一部分而不是全部的值,所以UPDATETEXT语句更灵活一些。
欲了解详细信息,请参阅SQL Server联机手册。
函数
本节中的表显示了Oracle和SQL Server 的scalar-valued和合计函数之间的关系。尽管名字是一样的,很重要的一点是注意到函数参数的个数和类型之间的区别。那些只有Microsoft SQL Server提供的函数在这个清单中没有提及,因为本章限制在使现存的Oracle应用程序的移植工作更容易。例如,这些函数不被Oracle支持:角度(DEGREES),PI(PI),和随机数(RAND)
数字/数学函数
下面是Oracle支持的数字/数学函数以及它们的Microsoft SQL Server等价函数。
函数 |
Oracle |
Microsoft SQL Server |
绝对值 |
ABS |
ABS |
Arc cosine |
ACOS |
ACOS |
Arc sine |
ASIN |
ASIN |
Arc tangent of n |
ATAN |
ATAN |
Arc tangent of n and m |
ATAN2 |
ATN2 |
Smallest integer >= value |
CEIL |
CEILING |
Cosine |
COS |
COS |
Hyperbolic cosine |
COSH |
COT |
Exponential value |
EXP |
EXP |
Largest integer <= value |
FLOOR |
FLOOR |
Natural logarithm |
LN |
LOG |
Logarithm, any base |
LOG(N) |
N/A |
Logarithm, base 10 |
LOG(10) |
LOG10 |
Modulus (remainder) |
MOD |
USE MODULO (%) OPERATOR |
Power |
POWER |
POWER |
Random number |
N/A |
RAND |
Round |
ROUND |
ROUND |
Sign of number |
SIGN |
SIGN |
Sine |
SIN |
SIN |
Hyperbolic sine |
SINH |
N/A |
Square root |
SQRT |
SQRT |
Tangent |
TAN |
TAN |
Hyperbolic tangent |
TANH |
N/A |
Truncate |
TRUNC |
N/A |
Largest number in list |
GREATEST |
N/A |
Smallest number in list |
LEAST |
N/A |
Convert number if NULL |
NVL |
ISNULL |
字符函数
下面是Oracle支持的字符函数和它们的Microsoft SQL Server等价函数。
函数 |
Oracle |
Microsoft SQL Server |
把字符转换为ASCII |
ASCII |
ASCII |
字串连接 |
CONCAT |
(expression + expression) |
把ASCII转换为字符 |
CHR |
CHAR |
返回字符串中的开始字符(左起) |
INSTR |
CHARINDEX |
把字符转换为小写 |
LOWER |
LOWER |
把字符转换为大写 |
UPPER |
UPPER |
填充字符串的左边 |
LPAD |
N/A |
清除开始的空白 |
LTRIM |
LTRIM |
清除尾部的空白 |
RTRIM |
RTRIM |
字符串中的起始模式(pattern) |
INSTR |
PATINDEX |
多次重复字符串 |
RPAD |
REPLICATE |
字符串的语音表示 |
SOUNDEX |
SOUNDEX |
重复空格的字串 |
RPAD |
SPACE |
从数字数据转换为字符数据 |
TO_CHAR |
STR |
子串 |
SUBSTR |
SUBSTRING |
替换字符 |
REPLACE |
STUFF |
将字串中的每个词首字母大写 |
INITCAP |
N/A |
翻译字符串 |
TRANSLATE |
N/A |
字符串长度 |
LENGTH |
DATELENGTH or LEN |
列表中最大的字符串 |
GREATEST |
N/A |
列表中最小的字符串 |
LEAST |
N/A |
如果为NULL则转换字串 |
NVL |
ISNULL |
日期函数
下面是Oracle支持的日期函数和它们的Microsoft SQL Server等价函数。
函数 |
Oracle |
Microsoft SQL Server |
日期相加 |
(date column +/- value) or ADD_MONTHS |
DATEADD |
两个日期的差 |
(date column +/- value) or MONTHS_BETWEEN |
DATEDIFF |
当前日期和时间 |
SYSDATE |
GETDATE() |
一个月的最后一天 |
LAST_DAY |
N/A |
时区转换 |
NEW_TIME |
N/A |
日期后的第一个周日 |
NEXT_DAY |
N/A |
代表日期的字符串 |
TO_CHAR |
DATENAME |
代表日期的整数 |
TO_NUMBER (TO_CHAR)) |
DATEPART |
日期舍入 |
ROUND |
CONVERT |
日期截断 |
TRUNC |
CONVERT |
字符串转换为日期 |
TO_DATE |
CONVERT |
如果为NULL则转换日期 |
NVL |
ISNULL |
转换函数
下面是Oracle支持的转换函数和它们的Microsoft SQL Server等价函数。
函数 |
Oracle |
Microsoft SQL Server |
数字转换为字符 |
TO_CHAR |
CONVERT |
字符转换为数字 |
TO_NUMBER |
CONVERT |
日期转换为字符 |
TO_CHAR |
CONVERT |
字符转换为日期 |
TO_DATE |
CONVERT |
16进制转换为2进制 |
HEX_TO_RAW |
CONVERT |
2进制转换为16进制 |
RAW_TO_HEX |
CONVERT |
其它行级别的函数
下面是Oracle支持的其它行级别的函数以及它们的Microsoft SQL Server等价函数。
函数 |
Oracle |
Microsoft SQL Server |
返回第一个非空表达式 |
DECODE |
COALESCE |
当前序列值 |
CURRVAL |
N/A |
下一个序列值 |
NEXTVAL |
N/A |
如果exp1 = exp2, 返回null |
DECODE |
NULLIF |
用户登录账号ID数字 |
UID |
SUSER_ID |
用户登录名 |
USER |
SUSER_NAME |
用户数据库ID数字 |
UID |
USER_ID |
用户数据库名 |
USER |
USER_NAME |
当前用户 |
CURRENT_USER |
CURRENT_USER |
用户环境(audit trail) |
USERENV |
N/A |
在CONNECT BY子句中的级别 |
LEVEL |
N/A |
合计函数
下面是Oracle支持的合计函数和它们的Microsoft SQL Server等价函数。
函数 |
Oracle |
Microsoft SQL Server |
Average |
AVG |
AVG |
Count |
COUNT |
COUNT |
Maximum |
MAX |
MAX |
Minimum |
MIN |
MIN |
Standard deviation |
STDDEV |
STDEV or STDEVP |
Summation |
SUM |
SUM |
Variance |
VARIANCE |
VAR or VARP |
条件测试
Oracle的DECODE语句和Microsoft SQL Server的CASE表达式都执行条件测试。当test_value中的值和后面的任何表达式匹配的时候,相关的值就返回。如果没有找到任何匹配的值,就返回default_value。如果没有指定default_value,在没有匹配的时候,DECODE和CASE都返回一个NULL。下表显示了该语句的语法,同时给出了转换DECODE命令的示例。
Oracle |
Microsoft SQL |
DECODE (test_value, expression1, value1 [[,expression2, value2] […]] [,default_value] )
CREATE VIEW STUDENT_ADMIN.STUDENT_GPA (SSN, GPA) AS SELECT SSN, ROUND(AVG(DECODE(grade ,'A', 4 ,'A+', 4.3 ,'A-', 3.7 ,'B', 3 ,'B+', 3.3 ,'B-', 2.7 ,'C', 2 ,'C+', 2.3 ,'C-', 1.7 ,'D', 1 ,'D+', 1.3 ,'D-', 0.7 ,0)),2) FROM STUDENT_ADMIN.GRADE GROUP BY SSN
|
CASE input_expression WHEN when_expression THEN result_expression [[WHEN when_expression THEN result_expression] [...]] [ELSE else_result_expression] END
CREATE VIEW STUDENT_ADMIN.STUDENT_GPA (SSN, GPA) AS SELECT SSN, ROUND(AVG(CASE grade WHEN 'A' THEN 4 WHEN 'A+' THEN 4.3 WHEN 'A-' THEN 3.7 WHEN 'B' THEN 3 WHEN 'B+' THEN 3.3 WHEN 'B-' THEN 2.7 WHEN 'C' THEN 2 WHEN 'C+' THEN 2.3 WHEN 'C-' THEN 1.7 WHEN 'D' THEN 1 WHEN 'D+' THEN 1.3 WHEN 'D-' THEN 0.7 ELSE 0 END),2) FROM STUDENT_ADMIN.GRADE GROUP BY SSN
|
CASE表达式可以支持用SELECT语句执行布尔测试,这是DECODE命令所不允许的。欲了解关于CASE表达式的详细信息,请参阅SQL Server联机手册。
把值转换为不同的数据类型
Microsoft SQL Server的CONVERT和CAST函数都是多目标转换函数。它们提供了相似的功能,把一种数据类型的表达式转换为另一种数据类型的表达式,并且支持多种专门数据的格式。
-
CAST(expression AS data_type)
- CONVERT (data type[(length)], expression [, style])
CAST是一个SQL-92标准的函数。这些函数执行同Oracle的TO_CHAR、TO_NUMBER、TO_DATE、HEXTORAW以及RAWTOTEXT函数相同的功能。
这里所指的数据类型是任何表达式将被转换成为的系统数据类型。不能使用用户定义的数据类型。长度参数是可选的,该参数用于char、varchar、binary以及varbinary数据类型。允许的最大长度是8000。
转换 |
Oracle |
Microsoft SQL Server |
字符到数字 |
TO_NUMBER('10') |
CONVERT(numeric, '10') |
数字到字符 |
TO_CHAR(10) |
CONVERT(char, 10) |
字符到日期 |
TO_DATE('04-JUL-97') TO_DATE('04-JUL-1997', 'dd-mon-yyyy') TO_DATE('July 4, 1997', 'Month dd, yyyy') |
CONVERT(datetime, '04-JUL-97') CONVERT(datetime, '04-JUL-1997') CONVERT(datetime, 'July 4, 1997') |
日期到字符 |
TO_CHAR(sysdate) TO_CHAR(sysdate, 'dd mon yyyy') TO_CHAR(sysdate, 'mm/dd/yyyy') |
CONVERT(char, GETDATE()) CONVERT(char, GETDATE(), 106) CONVERT(char, GETDATE(), 101) |
16进制到2进制 |
HEXTORAW('1F') |
CONVERT(binary, '1F') |
2进制到16进制 |
RAWTOHEX (binary_column) |
CONVERT(char, binary_column) |
请注意字符串是怎样转换为日期的。在Oracle中,缺省的日期格式模型是“DD-MON-YY”如果你使用任何其它格式,你必须提供一个合适的日期格式模型。CONVERT函数自动转换标准日期格式,不需要任何格式模型。
从日期转换到字符串时,CONVERT函数的缺省输出是“dd mon yyyy hh:mm:ss:mmm(24h)”。用一个数字风格代码来格式化输出,使它能输出为其它类型的日期格式模型。欲了解CONVERT函数的详细信息,请参阅SQL Server联机手册。
下表显示了Microsoft SQL Server日期的缺省输出。
Without Century |
With Century |
Standard |
Output |
- |
0 or 100 (*) |
Default |
mon dd yyyy hh:miAM (or PM) |
1 |
101 |
USA |
mm/dd/yy |
2 |
102 |
ANSI |
yy.mm.dd |
3 |
103 |
British/French |
dd/mm/yy |
4 |
104 |
German |
dd.mm.yy |
5 |
105 |
Italian |
dd-mm-yy |
6 |
106 |
- |
dd mon yy |
7 |
107 |
- |
mon dd, yy |
8 |
108 |
- |
hh:mm:ss |
- |
9 or 109 (*) |
Default milliseconds |
mon dd yyyy hh:mi:ss:mmm (AM or PM) |
10 |
110 |
USA |
mm-dd-yy |
11 |
111 |
Japan |
yy/mm/dd |
12 |
112 |
ISO |
yymmdd |
- |
13 or 113 (*) |
Europe default |
dd mon yyyy hh:mm:ss:mmm(24h) |
14 |
114 |
- |
hh:mi:ss:mmm(24h) |
用户定义函数
Oracle PL/SQL函数可以在Oracle SQL语句中使用。在Microsoft SQL Server中一般可以通过其它方式来实现同样的功能。
在SQL Server中可以用表中给出的查询来代替。
Oracle |
Microsoft SQL Server |
SELECT SSN, FNAME, LNAME, ) TUITION_PAID, TUITION_PAID/GET_SUM_ MAJOR(MAJOR) AS PERCENT_MAJOR FROM STUDENT_ADMIN.STUDENT |
SELECT SSN, FNAME, LNAME, TUITION_PAID, TUITION_PAID/SUM_MAJOR AS PERCENT_MAJOR FROM STUDENT_ADMIN.STUDENT, (SELECT MAJOR, SUM(TUITION_PAID) SUM_MAJOR FROM STUDENT_ADMIN.STUDENT GROUP BY MAJOR) SUM_STUDENT WHERE STUDENT.MAJOR = SUM_STUDENT.MAJOR |
CREATE OR REPLACE FUNCTION GET_SUM_MAJOR (INMAJOR VARCHAR2) RETURN NUMBER AS SUM_PAID NUMBER; BEGIN SELECT SUM(TUITION_PAID) INTO SUM_PAID FROM STUDENT_ADMIN.STUDENT WHERE MAJOR = INMAJOR; RETURN(SUM_PAID); END GET_SUM_MAJOR; |
No CREATE FUNCTION syntax is required; use CREATE PROCEDURE syntax. |
比较操作符
Oracle和Microsoft SQL Server的比较操作符几乎是一样的。
算符 |
Oracle |
Microsoft SQL Server |
等于 |
(=) |
(=) |
大于 |
(>) |
(>) |
小于 |
(<) |
(<) |
大于或等于 |
(>=) |
(>=) |
小于或等于 |
(<=) |
(<=) |
不等于 |
(!=, <>, ^=) |
(!=, <>, ^=) |
不大于,不小于 |
N/A |
!> , !< |
在集合中任意成员中 |
IN |
IN |
不在集合中的任何成员中 |
NOT IN |
NOT IN |
集合中的任意值 |
ANY, SOME |
ANY, SOME |
提交集合中的所有值 |
!= ALL, <> ALL, < ALL, > ALL, <= ALL, >= ALL, != SOME, <> SOME, < SOME, > SOME, <= SOME, >= SOME |
!= ALL, <> ALL, < ALL, > ALL, <= ALL, >= ALL, != SOME, <> SOME, < SOME, > SOME, <= SOME, >= SOME |
像模式(Like pattern) |
LIKE |
LIKE |
不像模式(Not like pattern) |
NOT LIKE |
NOT LIKE |
X和y之间的值 |
BETWEEN x AND y |
BETWEEN x AND y |
不在x和y之间的值 |
NOT BETWEEN |
NOT BETWEEN |
值存在 |
EXISTS |
EXISTS |
值不存在 |
NOT EXISTS |
NOT EXISTS |
值{为|不为}空 |
IS NULL, IS NOT NULL |
Same. Also = NULL, != NULL for backward compatibility (not recommended). |
模式匹配
SQL Server的LIKE关键字提供了有用的通配符搜索功能,这个功能在Oracle中不支持。除了所有的RDBMS都支持的(%)和(_)通配符以外,SQL Server还支持([ ])和([^])通配符。
([ ])字符用来查询在一个范围内的所有单个字符。例如,如果你需要查询包含一个从a到f的字符的数据,你可以这样写:“LIKE '[a-f]'”或者“LIKE '[abcdef]'”。这些附加的通配符的有效性在下表中给出。
Oracle |
Microsoft SQL |
SELECT * FROM STUDENT_ADMIN.STUDENT WHERE LNAME LIKE 'A%' OR LNAME LIKE 'B%' OR LNAME LIKE 'C%' |
SELECT * FROM STUDENT_ADMIN.STUDENT WHERE LNAME LIKE '[ABC]%' |
[^]通配符用来标记那些不在特定范围内的字符。例如,如果除了a到f以外的所有字符都是可以接受的,你可以这样书写:LIKE '[^a - f]'或者LIKE '[^abcdef]'。
欲了解关于LIKE关键字的详细信息,请参阅SQL Server联机手册。
在比较中使用NULL
尽管Microsoft SQL Server传统上支持SQL-92标准的和一些非标准的NULL行为,但是它还是支持Oracle中的NULL的用法。
为了支持分布式查询,SET ANSI_NULLS必须设定为ON。
在进行连接的时候,SQL Server的SQL Server ODBC驱动程序和OLE DB提供者自动把SET ANSI_NULLS设定为ON。这个设置可以在ODBC数据源、ODBC连接属性、或者是在连接到SQL Server之前在应用程序中设置的OLE DB连接属性中进行配置。在从DB-Library应用程序中连接时,SET ANSI_NULLS缺省为OFF。
当SET ANSI_DEFAULTS为ON时,SET ANSI_NULLS被允许。
欲了解关于NULL用法的详细信息,请参阅SQL Server联机手册。
字串连接
Oracle使用两个管道符号(||)来作为字串连接操作符,SQL Server则使用加号(+)。这个差别要求你在应用程序中做小小的修改。
Oracle |
Microsoft SQL |
SELECT FNAME||' '||LNAME AS NAME FROM STUDENT_ADMIN.STUDENT |
SELECT FNAME +' '+ LNAME AS NAME FROM STUDENT_ADMIN.STUDENT |
流控制(Control-of-Flow)语言
流控制语言控制SQL语句执行流,语句块以及存储过程。PL/SQL和Transact-SQL提供了多数相同的结构,但是还是有一些语法差别。
关键字
这是两个RDBMS支持的关键字。
语句 |
Oracle PL/SQL |
Microsoft SQL Server Transact-SQL |
声明变量 |
DECLARE |
DECLARE |
语句块 |
BEGIN...END; |
BEGIN...END |
条件处理 |
IF…THEN, ELSIF…THEN, ELSE ENDIF; |
IF…[BEGIN…END] ELSE <condition> [BEGIN…END] ELSE IF <condition> CASE expression |
无条件结束 |
RETURN |
RETURN |
无条件结束当前程序块后面的语句 |
EXIT |
BREAK |
重新开始一个WHILE循环 |
N/A |
CONTINUE |
等待指定间隔 |
N/A (dbms_lock.sleep) |
WAITFOR |
循环控制 |
WHILE LOOP…END LOOP;
LABEL…GOTO LABEL; FOR…END LOOP; LOOP…END LOOP;
|
WHILE <condition> BEGIN… END
LABEL…GOTO LABEL
|
程序注释 |
/* … */, -- |
/* … */, -- |
打印输出 |
RDBMS_OUTPUT.PUT_ LINE |
PRINT |
引发程序错误(Raise program error) |
RAISE_APPLICATION_ ERROR |
RAISERROR |
执行程序 |
EXECUTE |
EXECUTE |
语句终止符 |
Semicolon (;) |
N/A |
声明变量
Transact-SQL和PL/SQL的变量是用DECLARE关键字创建的。Transact-SQL变量用@标记,并且就像PL/SQL一样,在第一次创建时,用空值初始化。
Oracle |
Microsoft SQL |
DECLARE VSSN CHAR(9); VFNAME VARCHAR2(12); VLNAME VARCHAR2(20); VBIRTH_DATE DATE; VLOAN_AMOUNT NUMBER(12,2); |
DECLARE @VSSN CHAR(9), @VFNAME VARCHAR2(12), @VLNAME VARCHAR2(20), @VBIRTH_DATE DATETIME, @VLOAN_AMOUNT NUMERIC(12,2) |
Transact-SQL不支持%TYPE和%ROWTYPE变量数据类型定义。一个Transact-SQL变量不能在DECLARE命令中初始化。在Microsoft SQL Server数据类型定义中也不能使用Oracle的NOT NULL和CONSTANT关键字。
像Oracle的LONG和LONG RAW数据类型一样。文本和图形数据类型不能被用做变量定义。此外,Transact-SQL不支持PL/SQL风格的记录和表的定义。
给变量赋值
Oracle和Microsoft SQL Server提供了下列方法来为本地变量赋值。
Oracle |
Microsoft SQL |
Assignment operator (:=) |
SET @local_variable = value |
SELECT...INTO syntax for selecting column values from a single row |
SELECT @local_variable = expression [FROM…] for assigning a literal value, an expression involving other local variables, or a column value from a single row |
FETCH…INTO syntax |
FETCH…INTO syntax |
这里有一些语法示例
Oracle |
Microsoft SQL |
DECLARE VSSN CHAR(9); VFNAME VARCHAR2(12); VLNAME VARCHAR2(20); BEGIN VSSN := '123448887'; SELECT FNAME, LNAME INTO VFNAME, VLNAME FROM STUDENTS WHERE SSN=VSSN; END; |
DECLARE @VSSN CHAR(9), @VFNAME VARCHAR(12), @VLNAME VARCHAR(20) SET @VSSN = '12355887' SELECT @VFNAME=FNAME, @VLNAME=LNAME FROM STUDENTS WHERE SSN = @VSSN |
语句块
Oracle PL/SQL和Microsoft SQL Server Transact-SQL都支持用BEGIN…END术语来标记语句块。Transact-SQL不需要在DECLARE语句后使用一个语句块。如果在Microsoft SQL Server 中的IF语句和WHILE循环中有多于一个语句被执行,则需要使用BEGIN…END语句块。
Oracle |
Microsoft SQL |
DECLARE DECLARE VARIABLES ... BEGIN -- THIS IS REQUIRED SYNTAX PROGRAM_STATEMENTS ... IF ...THEN STATEMENT1; STATEMENT2; STATEMENTN; END IF; WHILE ... LOOP STATEMENT1; STATEMENT2; STATEMENTN; END LOOP; END; -- THIS IS REQUIRED SYNTAX |
DECLARE DECLARE VARIABLES ... BEGIN -- THIS IS OPTIONAL SYNTAX PROGRAM_STATEMENTS ... IF ... BEGIN STATEMENT1 STATEMENT2 STATEMENTN END WHILE ... BEGIN STATEMENT1 STATEMENT2 STATEMENTN END END -- THIS IS REQUIRED SYNTAX |
条件处理
Microsoft SQL Server Transact-SQL的条件语句包括IF和ELSE,但不包括Oracle PL/SQL中的ELSEIF语句。可以用嵌套多重IF语句来到达同样的效果。对于广泛的条件测试,用CASE表达式也许更容易和可读一些。
Oracle |
Microsoft SQL |
DECLARE VDEGREE_PROGRAM CHAR(1); VDEGREE_PROGRAM_NAME VARCHAR2(20); BEGIN VDEGREE_PROGRAM := 'U'; IF VDEGREE_PROGRAM = 'U' THEN VDEGREE_PROGRAM_NAME := 'Undergraduate'; ELSIF VDEGREE_PROGRAM = 'M' THEN VDEGREE_PROGRAM_ NAME := 'Masters'; ELSIF VDEGREE_PROGRAM = 'P' THEN VDEGREE_PROGRAM_ NAME := 'PhD'; ELSE VDEGREE_PROGRAM_ NAME := 'Unknown'; END IF; END; |
DECLARE @VDEGREE_PROGRAM CHAR(1), @VDEGREE_PROGRAM_NAME VARCHAR(20) SELECT @VDEGREE_PROGRAM = 'U' SELECT @VDEGREE_PROGRAM_ NAME = CASE @VDEGREE_PROGRAM WHEN 'U' THEN 'Undergraduate' WHEN 'M' THEN 'Masters' WHEN 'P' THEN 'PhD'. ELSE 'Unknown' END |
重复执行语句(循环)
Oracle PL/SQL提供了无条件的LOOP和FOR LOOP。Transact-SQL则提供了WHILE循环和GOTO语句。
WHILE Boolean_expression
{sql_statement | statement_block}
[BREAK] [CONTINUE]
|
WHILE循环需要测试一个布尔表达式来决定一个或者多个语句的重复执行。只要给定的表达式结果为真,这个(些)语句就一直重复执行下去。如果有多个语句需要执行,则这些语句必须放在一个BEGIN…END块中。
Oracle |
Microsoft SQL |
DECLARE COUNTER NUMBER; BEGIN COUNTER := 0 WHILE (COUNTER <5) LOOP COUNTER := COUNTER + 1; END LOOP; END; |
DECLARE @COUNTER NUMERIC SELECT@COUNTER = 1 WHILE (@COUNTER <5) BEGIN SELECT @COUNTER = @COUNTER +1 END |
语句的执行可以在循环的内部用BREAK和CONTINUE关键字控制。BREAK关键字使WHILE循环无条件的结束,而CONTINUE关键字使WHILE循环跳过后面的语句重新开始。BREAK关键字同Oracle PL/SQL中的EXIT关键字是等价的。而在Oracle中没有和CONTINUE等价的关键字
GOTO语句
Oracle和Microsoft SQL Server都有GOTO语句,但是语法不同。GOTO语句使Transact-SQL跳到指定的标号处运行,在GOTO语句后指定标号之间的任何语句都不会被执行。
Oracle |
Microsoft SQL |
GOTO label; <<label name here>> |
GOTO label |
PRINT语句
Transact-SQL的PRINT语句执行同PL/SQL的RDBMS_OUTPUT.put_line过程同样的操作。该语句用来打印用户给定的消息。
用PRINT语句打印的消息上限是8,000个字符。定义为char或者varchar数据类型的变量可以嵌入打印语句。如果使用其它数据类型的变量,则必须使用CONVERT或者CAST函数。本地变量、全局变量可以被打印。可以用单引号或者双引号来封闭文本。
从存储过程返回
Microsoft SQL Server和Oracle都有RETURN语句。RETURN使你的程序从查询或者过程中无条件的跳出。RETURN是立即的、完全的、并且可以用于从过程、批处理或者语句块的任意部分跳出。在REUTRN后面的语句将不会被执行。
Oracle |
Microsoft SQL |
RETURN expression: |
RETURN [integer_expression] |
引发程序错误(Raising program errors)
Transact-SQL的RAISERROR返回一个用户定义的错误消息,并且设置一个系统标志来记录发生了一个错误。这个功能同PL/SQL的raise_application_error异常处理器的功能是相似的。
RAISERROR语句允许客户重新取得sysmessages表的一个入口,或者用用户指定的严重性和状态信息动态的建立一条消息。在被定义后,消息被送回客户端作为系统错误消息。
RAISERROR ({msg_id | msg_str}, severity, state
[, argument1 [, argument2]])
[WITH options]
|
在转换你的PL/SQL程序时,也许用不着使用RAISERROR语句。在下面的示例代码中。PL/SQL程序使用raise_application_error异常处理器,但是Transact-SQL程序则什么也没用。包括raise_application_error异常处理器是为了防止PL/SQL返回不明确的未经处理的异常错误消息。作为代替,当一个不可预见的问题发生的时候,异常处理器总是返回Oracle错误消息。
当一个Transact-SQL失败时,它总是返回一个详细的错误消息给客户程序。因此,除非需要某些特定的错误处理,一般是不需要RAISERROR语句的。
Oracle |
Microsoft SQL |
CREATE OR REPLACE FUNCTION DEPT_ADMIN.DELETE_DEPT (VDEPT IN VARCHAR2) RETURN NUMBER AS BEGIN DELETE FROM DEPT_ADMIN.DEPT WHERE DEPT = VDEPT; RETURN(SQL%ROWCOUNT); EXCEPTION WHEN OTHER THEN RAISE_APPLICATION_ERROR (-20001,SQLERRM); END DELETE_DEPT; / |
CREATE PROCEDURE DEPT_ADMIN.DELETE_DEPT @VDEPT VARCHAR(4) AS DELETE FROM DEPT_DB.DBO.DEPT WHERE DEPT = @VDEPT RETURN @@ROWCOUNT GO |
实现游标
Oracle在使用SELECT语句时总是需要游标,不管从数据库中请求多少行。在Microsoft SQL Server,SELECT语句并不把在返回客户的行上附加游标作为缺省的结果集合。这是一种返回数据给客户应用程序的有效的方法。
SQL Server为游标函数提供了两种接口。当在Transact-SQL批处理或者存储过程中使用游标的时候,SQL语句可用来声明、打开、和从游标中抽取,就像定位更新和删除一样。当使用来自DB-Library、ODBC、或者OLEDB程序的游标时,SQL Server显式的调用内建的服务器函数来更有效的处理游标。
当从Oracle输入一个PL/SQL过程时,首先判断是否需要在Transact-SQL中采用游标来实现同样的功能。如果游标仅仅返回一组行给客户程序,就使用非游标的SELECT语句来返回缺省的结果集合。如果游标用来从行中一次取得一个数据给本地过程变量,你就必须在Transact-SQL中使用游标。
语法
下表显示了使用游标的语法。
操作 |
Oracle |
Microsoft SQL Server |
声明一个游标 |
CURSOR cursor_name [(cursor_parameter(s))] IS select_statement; |
DECLARE cursor_name CURSOR [LOCAL | GLOBAL] [FORWARD_ONLY | SCROLL] [STATIC | KEYSET | DYNAMIC | FAST_FORWARD] [READ_ONLY | SCROLL_LOCKS | OPTIMISTIC] [TYPE_WARNING] FOR select_statement [FOR UPDATE [OF column_name [,…n]]] |
打开一个游标 |
OPEN cursor_name [(cursor_parameter(s))]; |
OPEN cursor_name |
从游标中提取(Fetching) |
FETCH cursor_name INTO variable(s) |
FETCH [[NEXT | PRIOR | FIRST | LAST | ABSOLUTE {n | @nvar} | RELATIVE {n | @nvar}] FROM] cursor_name [INTO @variable(s)] |
更新提取行 |
UPDATE table_name SET statement(s)… WHERE CURRENT OF cursor_name; |
UPDATE table_name SET statement(s)… WHERE CURRENT OF cursor_name |
删除提取行 |
DELETE FROM table_name WHERE CURRENT OF cursor_name; |
DELETE FROM table_name WHERE CURRENT OF cursor_name |
关闭游标 |
CLOSE cursor_name; |
CLOSE cursor_name |
清除游标数据结构 |
N/A |
DEALLOCATE cursor_name |
声明一个游标
尽管Transact-SQL DECLARE CURSOR语句不支持游标参数的使用,但它确实支持本地变量。当游标打开的时候,它就使用这些本地变量的值。Microsoft SQL Server在其DECLARE CURSOR中提供了许多附加的功能。
INSENSITIVE选项用来定义一个创建数据的临时拷贝以被游标使用的游标。游标的所有请求都由这个临时表来应答。因此,对原表的修改不会反映到那些由fetch返回的用于该游标的数据上。这种类型的游标访问的数据是不能被修改的。
应用程序可以请求一个游标类型然后执行一个不被所请求的服务器游标类型支持的Transact-SQL语句。SQL Server返回一个错误,指出该游标类型被改变了,或者给出一组参数,隐式的转换游标。欲取得一个触发SQL Server 7.0隐式的把游标从一种类型转换为另一种类型的参数的完整列表,请参阅SQL Server联机手册。
SCROLL选项允许除了前向的抽取以外,向后的、绝对的和相对的数据抽取。一个滚动游标使用一种键集合的游标模型,在该模型中,任何用户提交的对表的删除和更新都将影响后来的数据抽取。只有在游标没有用INSENSITIVE选项声明时,上面的特性才起作用。
如果选择了READ ONLY选项,对游标中的行的更新就被禁止。该选项将覆盖游标的缺省选项棗允许更新。
UPDATE [OF column_list]语句用来在游标中定义一个可更新的列。如果提供了[OF column_list],那么仅仅是那些列出的列可以被修改。如果没有指定任何列。则所有的列都是可以更新的,除非游标被定义为READ ONLY。
重要的是,注意到一个SQL Server游标的名字范围就是连接自己。这和本地变量的名字范围是不同的。不能声明一个与同一个用户连接上的已有的游标相同名字的游标,除非第一个游标被释放。
打开一个游标
Transact-SQL不支持向一个打开的游标传递参数,这一点和PL/SQL是不一样的。当一个Transact-SQL游标被打开以后,结果集的成员和顺序就固定下来了。其它用户提交的对原表的游标的更新和删除将反映到对所有未加INSENSITIVE选项定义的游标的数据抽取上。对一个INSENSITIVE游标,将生成一个临时表。
抽取数据
Oracle游标只能向前移动棗没有向后或者相对滚动的能力。SQL Server游标可以向前或者向后滚动,具体怎么滚动,要由下表给出的数据抽取选项来决定。只有在游标是用SCROLL选项声明的前提下,这些选项才能使用。
卷动选项 |
描述 |
NEXT |
如果这是对游标的第一次提取,则返回结果集合的第一行;否则,在结果结合内移动游标到下一行。NEXT是在结果集合中移动的基本方法 。NEXT是缺省的游标提取(fetch)。 |
PRIOR |
返回结果集合的前一行。 |
FIRST |
把游标移动到结果集合的第一行,同时返回第一行。 |
LAST |
把游标移动到结果集合的最后一行,同时返回最后一行。 |
ABSOLUTE n |
返回结果集合的第n行。如果n为负数,则返回倒数第n行 |
RELATIVE n |
返回当前提取行后的第n行,如果n是负数,则返回从游标相对位置起的倒数第n行。 |
Transact-SQL的FETCH语句不需要INTO子句。如果没有指定返回变量,行就自动作为一个单行结果集合返回给客户。但是,如果你的过程必须把行给客户,一个不带游标的SELECT语句更有效一些。
在每一个FETCH后面,@@FETCH_STATUS函数被更新。这和在PL/SQL中使用CURSOR_NAME%FOUND和CURSOR_NAME%NOTFOUND变量是相似的。@@FETCH_STATUS函数在每一次成功的数据抽取以后被设定为0。如果数据抽取试图读取一个超过游标末尾的数据,则返回一个为-1的值。如果请求的行在游标打开以后从表上被删除了,@@FETCH_STATUS函数就返回一个为-2的值。只有游标是用SCROLL选项定义的情况下,才会返回-2值。在每一次数据抽取之后都必须检查该变量,以确保数据的有效性。
SQL Server不支持Oracle的游标FOR循环语法。
CURRENT OF子句
更新和删除的CURRENT OF子句语法和函数在PL/SQL和Transact-SQL中是一样的。在给定游标中,在当前行上执行定位的UPDATE和DELETE。
关闭一个游标
Transact-SQL的CLOSE CURSOR语句关闭游标,但是保留数据结构以备重新打开。PL/SQL 的CLOSE CURSOR语句关闭并且释放所有的数据结构。
Transact-SQL需要用DEALLOCATE CURSOR语句来清除游标数据结构。DEALLOCATE CURSOR语句同CLOSE CURSOR是不一样的,后者保留数据结构以备重新打开。DEALLOCATE CURSOR释放所有与游标相关的数据结构并且清除游标的定义。
游标示例
下面的例子显示了在PL/SQL和Transact-SQL等价的游标语句。
Oracle |
Microsoft SQL |
DECLARE VSSN CHAR(9); VFNAME VARCHAR(12); VLNAME VARCHAR(20); |
DECLARE @VSSN CHAR(9), @VFNAME VARCHAR(12), @VLNAME VARCHAR(20) |
CURSOR CUR1 IS SELECT SSN, FNAME, LNAME FROM STUDENT ORDER BY LNAME; BEGIN OPEN CUR1; FETCH CUR1 INTO VSSN, VFNAME, VLNAME; WHILE (CUR1%FOUND) LOOP FETCH CUR1 INTO VSSN, VFNAME, VLNAME; END LOOP; CLOSE CUR1; END; |
DECLARE curl CURSOR FOR SELECT SSN, FNAME, LNAME FROM STUDENT ORDER BY SSN OPEN CUR1 FETCH NEXT FROM CUR1 INTO @VSSN, @VFNAME, @VLNAME WHILE (@@FETCH_STATUS <> -1) BEGIN FETCH NEXT FROM CUR1 INTO @VSSN, @VFNAME, @VLNAME END CLOSE CUR1 DEALLOCATE CUR1 |
调试SQL语句
本节提供了一些SQL Server工具的信息,你可以用这些工具来调试Transact-SQL语句。欲了解关于调试SQL Server数据库的详细信息,请参阅本卷前面的“性能调节部分”
SQL Server Query Analyzer(SQL Server查询分析器)
你可以利用SQL Server查询分析器的图形化特性来更多的了解优化器是如何处理你的语句的。
SQL Server Profiler
该图形化工具实时捕获服务器活动的连续记录。SQL Server Profier监视许多不同的服务器事件和事件类别,用用户指定的标准来过滤这些事件,并且输出到一个显示在屏幕上的轨迹,一个文件,或者另一个SQL Server上。
SQL Server Profiler 可以用来:
-
监视SQL Server的性能。
-
调试Transact-SQL语句和存储过程。
-
识别执行很慢的查询。
-
通过捕捉导致一个特殊问题的所有事件查找SQL Server中的问题,然后在一个测试系统上重放这一系列事件来重现并且孤立问题。
-
通过单步执行语句,在项目的开发阶段测试SQL语句和存储过程,确保代码像预期的那样工作。
- 在产品系统上捕捉事件,并且在测试系统上重放捕捉的事件,以此为测试或者调试的目的重现产品环境中发生的事情。在独立的系统上重放捕捉的事件可以让用户继续使用产品系统,不会造成冲突。
SQL Server Profiler为一系列扩展存储过程提供了一个图形用户界面。你也可以直接使用这些扩展存储界面。因此,你可以创建自己的利用SQL Server Profiler扩展存储过程的应用程序来监视SQL Server。
SET语句
SET语句可以在你的工作会话、运行中的触发器或者存储过程的生命期内设定SQL Server查询处理选项。
SET FORCEPLAN ON语句强制优化器按照表在FROM子句出现的顺序处理连接,同Oracle优化器所用的ORDERED提示是类似的。
SET SHOWPLAN_ALL和SET SHOWPLAN_TEXT语句只返回查询或者语句执行方案的信息,而不会执行查询或者语句本身。要执行查询或者语句。需要把适当的显示方案的语句设定为OFF。然后就可以执行查询或者语句了。SHOWPLAN语句的结果跟Oracle的EXPLAIN PLAN工具是类似的。
如果SET STATISTICS PROFILE是ON,则每一个执行的查询除了返回正常的结果集合,还加上一个额外的结果集合,该集合显示了一个查询执行的快照。其它选项包括SET STATISTICS IO和SET STATISTICS TIME。
Transact-SQL语句处理包括两个阶段,编译和执行。NOEXEC选项编译每一个查询但不执行该查询。在NOEXEC设定为ON以后,后来的语句将不再执行,一直到NOEXEC设定为OFF。
SET SHOWPLAN ON
SET NOEXEC ON
go
SELECT * FROM DEPT_ADMIN.DEPT,
STUDENT_ADMIN.STUDENT
WHERE MAJOR = DEPT
go
STEP 1
The type of query is SETON
STEP 1
The type of query is SETON
STEP 1
The type of query is SELECT
FROM TABLE
DEPT_ADMIN.DEPT
Nested iteration
Table Scan
FROM TABLE
STUDENT_ADMIN.STUDENT
Nested iteration
Table Scan
|
查询优化
Oracle需要使用提示来影响它的基于开销的优化器的操作和性能。Microsoft SQL Server的基于开销的优化器不需要使用提示来帮助其查询评估过程。但是,它们仍然可以在某些情况下使用。
INDEX = {index_name | index_id}为那个表指定要用的索引名称或者ID。如果index_id为0,则强制该表进行扫描,而为1的index_id则强制使用一个分簇的索引,如果有的话。这跟Oracle中使用的索引提示是类似的。
SQL Server的FASTFIRSTROW指示优化器使用一个未分簇的索引,如果它的列顺序同ORDER BY子句相匹配的话。该提示同Oralce中的FIRST_ROWS提示是类似的
使用ODBC
本部分提供Oracle和SQL Server使用ODBC的方法的信息和关于开发和移植使用ODBC的应用程序的信息。
推荐的转换策略
如果要把你的应用程序从Oracle转换到SQL Server上,推荐采用下面的过程:
-
如果你的应用程序使用Oracle Pro*C或者Oracle调用接口(Oracle Call Interface,OCI)的话,考虑把它转换到ODBC。
-
理解SQL Server缺省的结果集合和游标选项,然后选择针对你的应用程序的最有效的提取策略。(fetching strategy)。
-
重新映射Oracle ODBC SQL数据类型到合适的SQL Server ODBC SQL数据类型。
-
使用ODBC Extended SQL扩展来创建类属的SQL语句。
- 决定SQL Server应用程序是否需要手工提交模式。
测试你的应用程序的性能,并对程序做必要的修改。
|
相关推荐
把Oracle数据库移植到Microsoft_SQL_Server_7.0
■把数据库对象输出到角本文件 Object Browser向您提供将数据库对象的定义输出到SQL角本文件的机能.输出的方式也有很多,比如将多个对象的定义输出成一个文件,输出不带用户名的...
在Windows NT 推出后,Microsoft与Sybase 在SQL Server 的开发上就分道扬镳了,Microsoft 将SQL Server 移植到Windows NT系统上,专注于开发推广SQL Server 的Windows NT 版本。Sybase 则较专注于SQL Server在UNIX...
通过FireDAC在更多设备上访问更多数据库:FireDAC使您能从Delphi中原生高速直接访问InterBase、SQLite、MySQL、SQL Server、Oracle、PostgreSQL、DB2、SQL Anywhere、Advantage DB、Firebird、Access、Informix、...
轻量级PHP数据库框架可加速开发产品特点轻量级-小于100 KB,仅可移植一个文件容易-极易学习和使用,友好的结构强大-支持各种常见和复杂SQL查询,数据映射,并防止SQL注入兼容-支持所有SQL数据库,包括MySQL,MSSQL,...
DirectUI移植到MFC中实现。 MFCHtml 调用脚本 如题。 MFC使用COM加载WMI服务,另类获取系统服务详细 大家都知道,现在流行的检测硬件软件视乎很神秘,我们要获得各种信息好像比较难.但大多数这种软件或多或少的使用了...
DirectUI移植到MFC中实现。 MFCHtml 调用脚本 MFC使用COM加载WMI服务,另类获取系统服务详细 大家都知道,现在流行的检测硬件软件视乎很神秘,我们要获得各种信息好像比较难.但大多数这种软件或多或少的使用了WMI,如果...
DirectUI移植到MFC中实现。 MFCHtml 调用脚本 MFC使用COM加载WMI服务,另类获取系统服务详细 大家都知道,现在流行的检测硬件软件视乎很神秘,我们要获得各种信息好像比较难.但大多数这种软件或多或少的使用了WMI,如果...
DirectUI移植到MFC中实现。 MFCHtml 调用脚本 MFC使用COM加载WMI服务,另类获取系统服务详细 大家都知道,现在流行的检测硬件软件视乎很神秘,我们要获得各种信息好像比较难.但大多数这种软件或多或少的使用了WMI,如果...
DirectUI移植到MFC中实现。 MFCHtml 调用脚本 MFC使用COM加载WMI服务,另类获取系统服务详细 大家都知道,现在流行的检测硬件软件视乎很神秘,我们要获得各种信息好像比较难.但大多数这种软件或多或少的使用了WMI,如果...
DirectUI移植到MFC中实现。 MFCHtml 调用脚本 MFC使用COM加载WMI服务,另类获取系统服务详细 大家都知道,现在流行的检测硬件软件视乎很神秘,我们要获得各种信息好像比较难.但大多数这种软件或多或少的使用了WMI,如果...