把Oracle数据库移植到Microsoft SQL Server 7.0(4)
关键词:Sql Server, Oracle
引用完整性
下表提供了一个用来定义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小时。
在这里
DELAY
指示Microsoft SQL Server等待,直到给定数量的时间过去以后才执行,最多可以设置到24小时。
'time'
需要等待的时间,时间可以是任何可接受的datetime数据类型的格式,或者可以作为一个本地变量给出。
但是,不能指定datetime值的日期部分。
TIME
指示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
}
READ COMMITTED
缺省的SQL Server孤立级别。如果你使用这种选择,你的应用程序将不能读取其他事务还没有提交的数据。
在这种模式下,一旦数据从页上读出,仍然要释放共享锁定。如果应用程序在同一个事务中重新读取同一个
的数据区,将会看到别的用户做的修改。
SERIALIZABLE
如果设定了这种选择,事务将同其他事务孤立起来。如果你不希望在查询中看到其他用户做的修改,你可以
设置事务的孤立级别为SERIALIZABLE。SQL Server将占据所有的共享锁定,直到事务结束。你可以通过在
SELECT语句中表名的后面使用HOLDLOCK暗示来在一个更小的级别上取得同样的效果。
READ UNCOMMITTED
如果设定为这种选择,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