<2>、ORACLE端语法说明
1、语法:
CREATE [OR REPLACE] TRIGGER trigger_name
{ BEFORE | AFTER } triggering_event ON table_name
[ FOR EACH ROW ]
[ WHEN trigger_condition ]
trigger_body ;
2、使用说明与示例:
(1)、上语法中,trigger_event 是对应于DML的三条语句INSERT、UPDATE、
DELETE;table_name是与触发器相关的表名称;FOR EACH ROW是可选
子句,当使用时,对每条相应行将引起触发器触发;condition是可选的
ORACLE BOOLEAN条件,当条件为真时触发器触发;trigger_body是触发
器触发时执行的PL/SQL块。
(2)、ORACLE触发器有以下两类:
1> 语句级(Statement-level)触发器,在CREATE TRIGGER语句中不
包含FOR EACH ROW子句。语句级触发器对于触发事件只能触发一次,
而且不能访问受触发器影响的每一行的列值。一般用语句级触发器处理
有关引起触发器触发的SQL语句的信息——例如,由谁来执行和什么时
间执行。
2> 行级(Row-level)触发器,在CREATE TRIGGER语句中
包含FOR EACH ROW子句。行级触发器可对受触发器影响的每一行触
发,并且能够访问原列值和通过SQL语句处理的新列值。行级触发器的
典型应用是当需要知道行的列值时,执行一条事务规则。
(3)在触发器体内,行级触发器可以引用触发器触发时已存在的行的列值,这些
值倚赖于引起触发器触发的SQL语句。
1> 对于INSERT语句,要被插入的数值包含在new.column_name,这里的
column_name是表中的一列。
2> 对于UPDATE语句,列的原值包含在old.column_name中,数据列的新
值在new.column_name中。
3> 对于DELETE语句,将要删除的行的列值放在old.column_name中。
触发语句
:old
:new
INSERT
无定义——所有字段都是NULL
当该语句完成时将要插入的数值
UPDATE
在更新以前的该行的原始取值
当该语句完成时将要更新的新值
DELETE
在删除行以前的该行的原始取值
未定义——所有字段都是NULL
4> 在触发器主体中,在new和old前面的“:”是必需的。而在触发器的
WHEN子句中,:new和:old记录也可以在WHEN子句的condition内部
引用,但是不需要使用冒号。例如,下面CheckCredits触发器的主体仅
当学生的当前成绩超过20时才会被执行:
CREATE OR REPLACE TRIGGER CheckCredits
BEFORE INSERT OR UPDATE OF current_credits ON students
FOR EACH ROW
WHEN (new.current_credits > 20)
BEGIN
/*Trigger body goes here. */
END ;
但CheckCredits也可以按下面方式改写:
CREATE OR REPLACE TRIGGER CheckCredits
BEFORE INSERT OR UPDATE OF current_credits ON students
FOR EACH ROW
BEGIN
IF :new.current_credits > 20 THEN
/*Trigger body goes here. */
END IF ;
END ;
注意,WHEN子句仅能用于行级触发器,如果使用了它,那么触发器主体
仅仅对那些满足WHEN子句指定的条件的行进行处理。
(4)触发器的主体是一个PL/SQL块,在PL/SQL块中可以使用的所有语句在触
发器主体中都是合法的,但是要受到下面的限制:
1> 触发器不能使用事务控制语句,包括COMMIT、ROLLBACK或
SAVEPOINT。ORACLE保持这种限制的原因是:如果触发器遇到错误时,
由触发器导致的所有数据库变换均能被回滚(roll back)取消;但如果
触发器确认(commit)了对数据库进行的部分变换,ORACLE就不能完全
回滚(roll back)整个事务。
2> 在触发器主体中调用到的存储过程的实现语句里也不能使用事务控制语
句。
3> 触发器主体不能声明任何LONG或LONG RAW变量。而且,:new和:old
不能指向定义触发器的表中的LONG或LONG RAW列。
4> 当声明触发器的表中有外键约束时,如果将定义触发器的表和需要作为
DELETE CASCADE参考完整性限制的结果进行更新的表称为变化表,
将外键相关联的表称为限制表,则在此触发器主体中的SQL语句不允许
读取或修改触发语句的任何变化表,也不允许读取或修改限制表中的主
键、唯一值列或外键列。
(5)以下是建立一个事前插入触发器的示例:
CREATE OR REPLACE TRIGGER Credit_Charge_Log_Ins_Before
BEFORE insert ON Credit_Charge_Log
FOR EACH ROW
DECLARE
Total_for_past_3days number ;
BEGIN
-- Check the credit charges for the past 3 days.
-- If they total more than $1000.00, log this entry
-- int the Credit_Charge_Attempt_Log for further handling.
select sum ( amount ) into total_for_past_3days
from Credit_Charge_Log
where Card_Number = :new.Card_Number
and Transaction_Date >= sysdate – 3;
IF total_for_past_3days > 1000.00 THEN
insert into credit_Charge_Attemp_Log
(Card_Number, Amount, Vendor_ID, Transaction_Date)
values
(:new.Card_Number, :new.Amount,
:new.Vendor_ID, :new.Transaction_Date);
END IF ;
END ;
<3>、从SQL SERVER向ORACLE的迁移方案
1、通过比较上面SQL语法的不同并考虑现有SQL SERVER的实际编程风格,在从
T-SQL向PL/SQL迁移时,要遵守下面规则:
1> 在CREATE TRIGGER定义中采用AFTER关键字,即调整为事后触发器。
2> 在CREATE TRIGGER定义中采用FOR EACH ROW关键字,即调整为行级触发
器。
3> 将触发器主体中的“inserted”调整为“:new”,将“deleted”调整为“:old”。
4> 在触发器主体中禁用CURSOR操作:new与:old。
5> 在触发器主体中禁用COMMIT、ROLLBACK、SAVEPOINT等事务控制语句。
2、用触发器解决ID列向SEQUENCE迁移的问题:
下面的GenerateStudentID触发器使用了:new。这是一个before INSERT触
发器,其目的是使用student_sequence序列所产生的数值填写
students表的ID字段。
例:
CREATE OR REPLACE TRIGGER GenerateStudentID
BEFORE INSERT ON students
FOR EACH ROW
BEGIN
SELECT student_sequence.nextval
INTO :new.ID
FROM dual;
END;
在上面的触发器主体中,GenerateStudentID实际上修改了:new.ID的值。这
是:new最有用的一个特性——当该语句真正被执行时,:new中的存储内容就
将被使用。有了这个触发器,我们就可以使用下面这样的INSERT语句,而不
会产生错误:
INSERT INTO students (first_name, last_name)
VALUES (‘LUO’, ‘TAO’) ;
尽管我们没有为主键列ID(这是必需的)指定取值,触发器将会提供所需要
的取值。事实上,如果我们为ID指定了一个取值,它也将会被忽略,因为触
发器修改了它。如果我们使用下面的语句:
INSERT INTO students (ID, first_name, last_name)
VALUES (-789, ‘LUO’, ‘TAO’) ;
其处理结果还是相同的。无论在哪种情况下,student_sequence.nextval都
将用作ID列值。
由此讨论,可以采用这种方法处理SQL SERVER中ID列向ORACLE的SEQUENCE
转换的问题。
另外,由于上面的原因,我们不能在after行级触发器中修改 :new,因为该
语句已经被处理了。通常,:new仅仅在before行级触发器中被修改,而:old
永远不会被修改,仅仅可以从它读出数据。
此外,:new和:old记录仅仅在行级触发器内部是有效的。如果试图要从语句
级触发器进行引用,将会得到一个编译错误。因为语句级触发器只执行一次
——尽管语句要处理许多行——所以:new和:old是没有意义的,因为怎么确
定它们引用的会是哪一行呢?
<七> 常用SQL语法与函数
<1>、SQL SERVER端常用语法说明
1、使用局部变量:
1> 变量定义:
DECLARE @variable_name datatype [,…]
例:
declare
@name varchar(30),
@type int
2> 给变量赋值:
方法一:
例:
declare @int_var int
select @int_var = 12
方法二:
例:
declare
@single_auth varchar(40),
@curdate datetime
select @single_auth = au_lname,
@curdate = getdate()
from authors
where au_id = ‘123-45-6789’
2、使用T-SQL标准控制结构:
1> 定义语句块
语法:
BEGIN
Statements
END
2> IF ... ELSE语句
语法:
IF boolean_expression
{ statement | statement_block }
ELSE
{ statement | statement_block }
示例:
if (select avg(price) from titles where type = ‘business’) > $19.95
print ‘The average price is greater then $19.95’
else
print ‘The average price is less then $19.95’
3> IF EXISTS语句
语法:
IF [not] EXISTS (select_statement)
{ statement | statement_block }
[ELSE
{ statement | statement_block }]
示例:
declare
@lname varchar(40),
@msg varchar(255)
select @lname = ‘Smith’
if exists(select * from titles where au_lname = @lname)
begin
select @msg = ‘There are authors named’ + @lname
print @msg
end
else
begin
select @msg = ‘There are no authors named’ + @lname
print @msg
end
4> 循环语句:
WHILE
语法:
WHILE boolean_condition
[{ statement | statement_block }]
[BREAK]
[condition]
示例:
declare
@avg_price money,
@max_price money,
@count_rows int,
@times_thru_the_loop int
select @avg_price = avg(price),
@max_price = max(price),
@count_rows = count(*),
@times_thru_the_loop = 0
from titles
while @avg_price < $25 and (@count_rows < 10 or @max_price < $50)
begin
select @avg_price = avg(price) * 1.05,
@max_price = max(price) * 1.05,
@time_thru_the_loop = @time_thru_the_loop + 1
end
if @time_thru_the_loop = 0
select @time_thru_the_loop = 1
update titles
set price = price * power(1.05, @time_thru_the_loop)
4> GOTO语句
语法:
GOTO label
...
label:
示例:
begin transaction
insert tiny(c1) values(1)
if @@error != 0 goto error_handler
commit transaction
return
error_handler:
rollback transaction
return
5> RETURN语句
语法:
RETURN
(1)用于无条件退出一个批处理、存储过程或触发器。
示例:
if not exists(select 1 from inventory
where item_num = @item_num)
begin
raiseerror 51345 ‘Not Found’
return
end
print ‘No error found’
return
(2)用于存储过程中返回状态值。
示例:
create procedure titles_for_a_pub
(@pub_name varchar(40) = null)
as
if @pub_name is null
return 15
if not exists(select 1 from publishers
where pub_name = @pub_name)
return –101
select t.tile from publishers p, titles t
where p.pub_id = t.pub_id
and pub_name = @pub_name
return 0
3、T-SQL中的游标提取循环语句:
(1)FETCH [NEXT FROM] cursor_name INTO @variable_1, ...@variable_n
(2)WHILE @@FETCH_STATUS = 0
BEGIN
Other_statements
FETCH [NEXT FROM] cursor_name INTO @variable_1, ...@variable_n
END
(3)CLOSE cursor_name
4、T-SQL中的事务处理语句:
1> 开始一个事务:
BEGIN TRAN[SACTION [transaction_name]]
2> 提交一个事务:
COMMIT TRAN[SACTION [transaction_name]]
3> 回滚一个事务:
ROLLBACK TRAN[SACTION [transaction_name]]
4> 使用事务保存点:
BEGIN TRAN[SACTION [transaction_name]]
SAVE TRAN[SACTION] savepoint_name
ROLLBACK TRAN[SACTION] savepoint_name
COMMIT TRAN[SACTION [transaction_name]]
5、T-SQL中可用于错误判断或其它处理的全局变量:
1> @@rowcount: 前一条命令处理的行数
2> @@error: 前一条SQL语句报告的错误号
3> @@trancount: 事务嵌套的级别
4> @@transtate: 事务的当前状态
5> @@tranchained: 当前事务的模式(链接的(chained)或非链接的)
6> @@servername: 本地SQL SERVER的名称
7> @@version : SQL SERVER和O/S的版本级别
8> @@spid: 当前进程的id
9> @@identity: 上次insert操作中使用的identity值
10> @@nestlevel: 存储过程/触发器中的嵌套层
11> @@fetch_status: 游标中上条fetch语句的状态
6、使用标准内置错误消息发送函数:
函数说明:
RAISERROR ({msg_id | msg_str}, severity, state
[, argument1 [,argument2][,...] )
[WITH LOG]
其中,msg_id表示错误号,用户定义错误消息的错误号在50001到2147483647之
间,特定的消息会引起错误50000。msg_str是错误消息正文,最多可有255个字
符。Severity描述了与这个消息关联的用户定义的严重性级别,取值包括0和10
至25之间的任何整数。State描述了错误的“调用状态”,它是1到127之间的整
数值。Argument定义用于代替在msg_str中定义的变量或对应与msg_id的消息的
参数。WITH LOG表示要在服务器错误日志和事件日志中记录错误。
例1:
RAISEERROR( ‘Invalid customer id in order.’, 16, 1)
则返回:
Msg 50000, Level 16, State 1
Invalid customer id in order.
例2:
sp_addmessage 52000, 16, ‘Invalid customer id %s in order’
RAISEERROR( 52000, 16, 1, ‘ID52436’)
则返回:
Msg 52000, Level 16, State 1
Invalid customer id ID52436 in order.
<2>、ORACLE端常用语法说明
1、使用局部变量:
1> 定义变量:
VARIABLE_NAME DATA TYPE [ := INITIAL VALUE ] ;
例:定义变量
v_Num number;
v_string varchar2(50);
例:定义变量并赋初值
v_Num number := 1 ;
v_string varchar2(50) := ‘Hello world!’ ;
2> 给变量赋值:
方法一:
例:
v_Num := 1;
v_string := ‘Hello world!’;
方法二:
例:
SELECT first_name INTO v_String
FROM students
WHERE id = v_Num ;
2、使用PL/SQL标准控制结构:
1> 定义语句块
语法:
BEGIN
Statements ;
END ;
2> IF ... THEN ... ELSE语句
语法:
IF boolean_expression THEN
{ statement | statement_block } ;
[ELSIF boolean_expression THEN /*注意此处的写法—— ELSIF */
{ statement | statement_block } ;]
...
[ELSE
{ statement | statement_block } ;]
END IF ;
示例:
v_NumberSeats rooms.number_seats%TYPE;
v_Comment VARCHAR2(35);
BEGIN
/* Retrieve the number of seats in the room identified by ID 99999.
Store the result in v_NumberSeats. */
SELECT number_seats
INTO v_NumberSeats
FROM rooms
WHERE room_id = 99999;
IF v_NumberSeats < 50 THEN
v_Comment := 'Fairly small';
ELSIF v_NumberSeats < 100 THEN
v_Comment := 'A little bigger';
ELSE
v_Comment := 'Lots of room';
END IF;
END;
3> 循环语句:
(1)简单循环语句:
语法:
LOOP
{ statement | statement_block } ;
[EXIT [WHEN condition] ;]
END LOOP ;
其中,语句EXIT [WHEN condition];等价于
IF condition THEN
EXIT ;
END IF ;
示例1:
v_Counter BINARY_INTEGER := 1;
BEGIN
LOOP
-- Insert a row into temp_table with the current value of the
-- loop counter.
INSERT INTO temp_table
VALUES (v_Counter, 'Loop index');
v_Counter := v_Counter + 1;
-- Exit condition - when the loop counter > 50 we will
-- break out of the loop.
IF v_Counter > 50 THEN
EXIT;
END IF;
END LOOP;
END;
示例2:
v_Counter BINARY_INTEGER := 1;
BEGIN
LOOP
-- Insert a row into temp_table with the current value of the
-- loop counter.
INSERT INTO temp_table
VALUES (v_Counter, 'Loop index');
v_Counter := v_Counter + 1;
-- Exit condition - when the loop counter > 50 we will
-- break out of the loop.
EXIT WHEN v_Counter > 50;
END LOOP;
END;
(2)WHILE循环语句:
语法:
WHILE condition LOOP
{ statement | statement_block } ;
END LOOP ;
示例1:
v_Counter BINARY_INTEGER := 1;
BEGIN
-- Test the loop counter before each loop iteration to
-- insure that it is still less than 50.
WHILE v_Counter <= 50 LOOP
INSERT INTO temp_table
VALUES (v_Counter, 'Loop index');
v_Counter := v_Counter + 1;
END LOOP;
END;
示例2:
v_Counter BINARY_INTEGER;
BEGIN
-- This condition will evaluate to NULL, since v_Counter
-- is initialized to NULL by default.
WHILE v_Counter <= 50 LOOP
INSERT INTO temp_table
VALUES (v_Counter, 'Loop index');
v_Counter := v_Counter + 1;
END LOOP;
END;
(3)数字式FOR循环语句:
语法:
FOR loop_counter IN [REVERSE] low_bound..high_bound LOOP
{ statement | statement_block } ;
END LOOP ;
这里,loop_counter是隐式声明的索引变量。
示例1:
FOR循环的循环索引被隐式声明为BINARY_INTEGER。在循环前面没有
必要声明它,如果对它进行了声明,那么循环索引将屏蔽外层的声明,
如下所示
v_Counter NUMBER := 7;
BEGIN
-- Inserts the value 7 into temp_table.
INSERT INTO temp_table (num_col)
VALUES (v_Counter);
-- This loop redeclares v_Counter as a BINARY_INTEGER, which
-- hides the NUMBER declaration of v_Counter.
FOR v_Counter IN 20..30 LOOP
-- Inside the loop, v_Counter ranges from 20 to 30.
INSERT INTO temp_table (num_col)
VALUES (v_Counter);
END LOOP;
-- Inserts another 7 into temp_table.
INSERT INTO temp_table (num_col)
VALUES (v_Counter);
END;
示例2:
如果在FOR循环中有REVERSE关键字,那么循环索引将从最大值向最
小值进行循环。请注意语法是相同的——仍然首先书写的是最小值,
如下所示
BEGIN
FOR v_Counter IN REVERSE 10..50 LOOP
-- v_Counter will start with 50, and will be decremented
-- by 1 each time through the loop.
NULL;
END LOOP;
END;
示例3:
FOR循环中的最大值和最小值没有必要必须是数字型文字,它们可以
是能够被转换为数字值的任何表达式,如下所示
v_LowValue NUMBER := 10;
v_HighValue NUMBER := 40;
BEGIN
FOR v_Counter IN REVERSE v_LowValue..v_HighValue LOOP
INSER INTO temp_table
VALUES (v_Counter, ‘Dynamically sqecified loop range’);
END LOOP;
END;
4> GOTO语句
语法:
GOTO label;
...
<<label>>
...
示例:
v_Counter BINARY_INTEGER := 1;
BEGIN
LOOP
INSERT INTO temp_table
VALUES (v_Counter, 'Loop count');
v_Counter := v_Counter + 1;
IF v_Counter > 50 THEN
GOTO l_EndOfLoop;
END IF;
END LOOP;
<<l_EndOfLoop>>
INSERT INTO temp_table (char_col)
VALUES ('Done!');
END;
5> EXIT语句
语法:
EXIT;
参见上面的PL/SQL标准控制结构之循环语句说明部分。
3、PL/SQL中的游标提取循环语句:
1> 简单循环
此循环采用简单的循环语法(LOOP..END LOOP),如下所示
-- Declare variables to hold information about the students
-- majoring in History.
v_StudentID students.id%TYPE;
v_FirstName students.first_name%TYPE;
v_LastName students.last_name%TYPE;
-- Cursor to retrieve the information about History students
CURSOR c_HistoryStudents IS
SELECT id, first_name, last_name
FROM students
WHERE major = 'History';
BEGIN
-- Open the cursor and initialize the active set
OPEN c_HistoryStudents;
LOOP
-- Retrieve information for the next student
FETCH c_HistoryStudents INTO v_StudentID, v_FirstName, v_LastName;
-- Exit loop when there are no more rows to fetch
EXIT WHEN c_HistoryStudents%NOTFOUND;
-- Process the fetched rows. In this case sign up each
-- student for History 301 by inserting them into the
-- registered_students table. Record the first and last
-- names in temp_table as well.
INSERT INTO registered_students (student_id, department, course)
VALUES (v_StudentID, 'HIS', 301);
INSERT INTO temp_table (num_col, char_col)
VALUES (v_StudentID, v_FirstName || ' ' || v_LastName);
END LOOP;
-- Free resources used by the cursor
CLOSE c_HistoryStudents;
-- Commit our work
COMMIT;
END;
请注意,EXIT WHEN语句的位置是紧跟在FETCH语句的后边。在检索完最后一
个行以后,c_HistoryStudents%NOTFOUND变为TRUE,该循环退出。EXIT WHEN
语句的位置也在数据处理部分的前面,这样做是为了确保该循环过程不处理任
何重复行。
2> WHILE循环
此循环采用WHILE .. LOOP的循环语法,如下所示
-- DECLARE cursor to retrieve the information about History students
CURSOR c_HistoryStudents IS
SELECT id, first_name, last_name
FROM students
WHERE major = 'History';
-- Declare a record to hold the fetched information.
v_StudentData c_HistoryStudents%ROWTYPE;
BEGIN
-- Open the cursor and initialize the active set
OPEN c_HistoryStudents;
-- Retrieve the first row, to set up for the WHILE loop
FETCH c_HistoryStudents INTO v_StudentData;
-- Continue looping while there are more rows to fetch
WHILE c_HistoryStudents%FOUND LOOP
-- Process the fetched rows, in this case sign up each
-- student for History 301 by inserting them into the
-- registered_students table. Record the first and last
-- names in temp_table as well.
INSERT INTO registered_students (student_id, department, course)
VALUES (v_StudentData.ID, 'HIS', 301);
INSERT INTO temp_table (num_col, char_col)
VALUES (v_StudentData.ID,
v_StudentData.first_name || ' '
|| v_StudentData.last_name);
-- Retrieve the next row. The %FOUND condition will be checked
-- before the loop continues again.
FETCH c_HistoryStudents INTO v_StudentData;
END LOOP;
-- Free resources used by the cursor
CLOSE c_HistoryStudents;
-- Commit our work
COMMIT;
END;
请注意,FETCH语句出现了两次——一次是在循环的前面,另一次是在循环处
理的后面,这样做是为了使循环条件(c_HistoryStudents%FOUND)对每一次循
环叠代都求值以确保该循环过程不处理任何重复行。
3> 游标式FOR循环
因与迁移关系不大,此处略。
4、PL/SQL中的事务处理语句:
在PL/SQL中,事务的开始位置是从前一个事务结束以后执行的第一条SQL语句,
或者在连接到该数据库以后所执行的第一条SQL语句。事务的结束是使用COMMIT
或ROLLBACK语句标识的。
1> COMMIT的语法是:
COMMIT [work];
可选的关键字work用来提高可读性。
2> ROLLBACK的语法是:
ROLLBACK [work];
可选的关键字work用来提高可读性。
3> ROLLBACK语句会撤消整个事务,如果使用SAVEPOINT命令,那么只有部分的事
务需要被撤消,其语法是:
SAVEPOINT name;
这里name是保存点的名字。
4> 示例:
v_NumIterations NUMBER;
BEGIN
-- Loop from 1 to 500, inserting these values into temp_table.
-- Commit every 50 rows.
FOR v_LoopCounter IN 1..500 LOOP
INSERT INTO temp_table (num_col) VALUES (v_LoopCounter);
v_NumIterations := v_NumIterations + 1;
IF v_NumIterations = 50 THEN
COMMIT;
v_NumIterations := 0;
END IF;
END LOOP;
END;
5、使用标准内置错误消息发送函数:
与T-SQL中RAISEERROR对应,PL/SQL中有这样一个内置函数
函数说明:
RAISE_APPLICATION_ERROR (error_number, error_message, [keep_errors]) ;
这里,error_number是从-20,000到-20,999之间的参数;error_message是与此
错误相关的正文,error_message必须不多于512个字节;而keep_errors是一个
可选的布尔值参数,其为TRUE则新的错误将被添加到已经引发的错误列表中(如
果有的话),其为FALSE(这是缺省的设置)则新的错误将替换错误的当前列表。
例:
RAISE_APPLICATION_ERROR(-20000, ‘Can’t find any record.’) ;
<3>、T-SQL与PL/SQL常用函数比较(以下的exp为expression的缩写)
T-SQL
PL/SQL
字符类函数
Ascii(char_exp)
Ascii(str_exp)
Char(int_exp)
Chr(int_exp)
Datalength(char_exp)
Length(str_exp)
Substring(exp, start, length)
Substr(exp, start, length)
Upper(char_exp)
Upper(str_exp)
Lower(char_exp)
Lower(str_exp)
Stuff(char_exp1,start,length,
Char_exp2)
Translate(str_exp,from_str,to_str)
Ltrim(char_exp)
Ltrim(str_exp1 [,str_exp2])
Rtrim(char_exp)
Rtrim(str_exp1 [,str_exp2])
日期类函数
Getdate()
Sysdate
数学类函数
Abs(numeric_exp)
Abs(number_exp)
Ceiling(numeric_exp)
Ceil(number_exp)
Exp(float_exp)
Exp(number_exp)
Floor(numeric_exp)
Floor(number_exp)
Power(numeric_exp,int_exp)
Power(number_exp1,number_exp2)
Round(numeric_exp,int_exp)
Round(number_exp1 [,number_exp2])
Sign(int_exp)
Sign(number_exp)
Sqrt(float_exp)
Sqrt(number_exp)
转换函数
Convert(datatype[(length)],exp,format)
To_char(datatype,str_format)
Convert(datatype[(length)],exp,format)s
To_date(str_exp,date_format)
Convert(datatype[(length)],exp,format)
To_number(str_exp,num_format)
其它函数
AVG([ALL | DISTINCT] col)
AVG([ALL | DISTINCT] col)
COUNT({[ALL | DISTINCT] col] | *})
COUNT({[ALL | DISTINCT] col} | *))
MAX([ALL | DISTINCT] col)
MAX([ALL | DISTINCT] col)
MIN([ALL | DISTINCT] col)
MIN([ALL | DISTINCT] col)
SUM([ALL | DISTINCT] col)
SUM([ALL | DISTINCT] col)
STDEV(col)
STDDEV(col)
VAR(col)
VARIANCE(col)
ISNULL(check_exp, replace_value)
NVL(check_exp, replace_value)
CASE
DECCODE
<4>MSSQL与ORACLE比较注意几个语法转换
(1)ISNULL与 NVL
在MSSQL中为了替换空值常用ISNULL函数,如ISNULL(@dno,”00”)表示当变量@dno的值为空时,则用”00”替换其值;在ORACLE 中,同样的功能用NVL实现,如上述例可以用NVL(dno,”00”)来替换。
(2)CASE 与 DECCODE
CASE在MSSQL中可以用以对某个值进行多个判断分支进行处理,简化了代码如下:
Update Student set Class = (Case inyear when “1993” then “8” when “1994” then “7” when “1995 then “6” else “0”)
相同的功能在ORACLE中可以用DECCODE来实现,如上例在ORACLE应作如下处理:
Update Student set class=deccode (inyeare,’1993’,’8’,’1994’,’7’,’1995’,’6’,’0 ‘) ;
(3)日期运算
在MSSQL中对于日期的处理不能数值型有很大区,其主要实现的函数有DATEADD、DATEDIFF;而在ORACLE中把日期当作数值来处理,其主要处理函数有ADD_MONTH、MONTH_BETWEEN、D1(+-*/)D2等,下面列出其替换方法
DATEADD(YEAR,1,pubdate)
ADD_MONTHS(D1,12)
DATEADD(MONTH,3,pubdate)
ADD_MONTHS(D1,3)
DATEADD(DAY,13,pubdate)
D1 + 13
DATEADD(DAY,-3,pubdate)
D1 – 3
DATEADD(HOUR,6,pubdate)
D1 + 6/24
DATEADD(MINUTES,24,pubdate)
D1 + 24/1440
DATEDIFF(minute, D1, D2)
(D2-D1)*1440
DATEDIFF(hour, D1, D2)
(D2-D1)*24
DATEDIFF(month, D1, D2)
MONTHS_BETWEEN(D1,D2)
DATENAME(month, getdate())
TO_CHAR(sysdate,’MONTH’)
DATENAME(year, getdate())
TO_CHAR(sysdate,’YEAR’)
DATENAME(day,getdate())
TO_CHAR(sysdate,’DAY’)
DATEDART(month,getdate())
TO_CHAR(sysdate,’MM’)
DATEDART(year,getdate())
TO_CHAR(sysdate,’YYYY’)
DATEDART(day,getdate())
TO_CHAR(sysdate,’DD’)
GETDATE()
sysdate
<4>、从T-SQL向PL/SQL迁移方案
通过上述讨论,在从T-SQL向PL/SQL迁移时,在常用语法与函数方面要逐一细致比较
后再行调整,特别要注意常用函数怎么进行替换和主体控制结构怎么进行调整。
(1)将所有的GETDATE全部转换成为SYSDATE;
(2)将所有的selct @var = column from table where condition 改成
select column into var from table where condition;
将所有的selct @var1 = @var2 改成
var1 :=var2;
(3)将所有的convert全部转换成为 to_char 或 trunc
例一:
declare rq1 datetime,rq2 datetime
…
select je from sr where rq > = convert (char(10),rq1,111)
and rq < convert(char(10),rq2,111)
应改成:
date rq1;
date rq2;
select sr into je where rq > = trunc(rq1) and rq < trunc(rq2);
例二:
declare rq1 datetime,rq2 datetime
…
select je from sr where convert(char(10),rq,111) > = convert (char(10),rq1,111)
and rq < convert(char(10),rq2,111)
应改成:
date rq1;
date rq2;
select sr into je where trunc(rq)> = trunc(rq1) and trunc(rq) < trunc(rq2);
或:
date rq1;
date rq2;
select sr into je where to_char(rq,’yyyy/mm/dd’)> =to_char(rq1,’yyyy/mm/dd’) and to_char(rq,’yyyy/mm/dd’) < to_char(rq2,’yyyy/mm/dd’);
(3)PL/SQL不支付时间字段与规则字符串的直接比例
如在T-SQL中的
select @je = sr where rq > ‘2001.01.01’是可以的。
而在PL/SQL中
select sr into je where rq > ‘2001.01.01’;是行不通的,如果要实现比例,则应改成;
select sr into je where rq > to_date(‘2001.01.01’,’yyyy.mm.dd’);或
select sr into je where to_char(rq,’yyyy.mm.dd’) > ‘2001.01.01’;
(4)将T-SQL中的DATEDIFF全部改成TRUNC(D1-D2),MONTHS_BETWEEN
如select @ts = datediff(day,date1,date2),在PL/SQL中应改为:
ts = trunc(date2 – date1);
如select @ys = datediff(month,date1,date2),在PL/SQL中应改为:
ts = months_between(date1 – date2);
(5)DateAdd全部改为D+n 或Add_Months
如select date2 = dateadd(day,date1,3),在PL/SQL中应改为:
date2 :=date1 + 3;
如select date2 = dateadd(month,date1,6),在PL/SQL中应改为:
date2 :=add_months(date1 ,6);
(6)
<八> 临时表问题
ORALCE8i以上的版本才支持临时表,其创建语法为:
CREATE GLOBAL TEMPORARY TABLE table_name
(clomn1 type,column2 type);而对于ORALCE8i以下的版本不支持,综合考虑,在从SQL SERVER向ORACLE迁移时,对于临时表,采用以下方案:
1、将T-SQL语句中的临时表在后台实际化——即将之创建为正式表,在其本身的列中增加一列作为序号用于标识不同的操作。
2、在将临时表实际化时对所有这样的表都要加“tmp_”前缀。
<九> ORACLE特别处
<1>、dual的使用
在ORACLE中可以用这样的语法从后台服务器提取时间值:
select sysdate into :varible from dual ;
<2>、ORACLE不能在存储过程中建数据表
<十>连接远程的数据库(包括不同服务器)
数据库链接(Database Link)与分布式数据库功能紧密相连。数据库链接允许用户处理远程数据库而不用知道数据是在什么地方。当建立了一个数据库链接之后,提供对远程数据的登录信息。每当使用数据库链接时,在分布式网络上初始化一个对话(Session),以解决对远程数据库对象的引用。
(1) 先创建一个远程数据库的别名,用ORACLE8 NET EASY CONFIG,也在在文件TNSNAMES.ORA中加入以下格式内容:
别名.WORLD =
(DESCRIPTION =
(ADDRESS = (PROTOCOL = NMP)(SERVER = 远程数据服务器名称)(PIPE = ORAPIPE))
(CONNECT_DATA = (SID = 远程数据库名称)))
(2) 创建一数据库链接
语法:
CREATE OR REPLACE [public] DATABASE LINK connect_name
CONNECT TO username IDENTIFIED BY password
USING ‘connect_string’;
创建数据库链接时具体使用的语法取决于下面两个条件:
n 数据库链接的“公共”或“私有”状态;
n 使用缺省还是显式方式登录远程数据库。
如果建立的数据库链接是公共所用,则在创建时应指明关键字 PUBLIC;无此参数系统默认为私有;
可以为数据库链接指定用户和密码,如下:
create or replace database link db_link_1
connect to ‘scott’ identified by ‘tiger’
using ‘hq’
则在当前数据库创建了一个指向远程数据库”hq”的链接,连接时所用的用户名和密码为“tiger”;
如果不指定用户和密码,则在应用数据库链接,系统会使用当时的用户进行尝试连接,创建过程如下:
create or replace database link db_link_1
using ‘hq’
(3)远程数据库对象的引用
创建数据库链接以后,在当前数据库就可以访问远程数据库中具有访问权限的对象,引用的方法为将数据库链接名称添加到任何可以访问远程帐号的表或视图上。当将数据库链接名称加添加到表或视图名称上时,必须在数据库链接名称之前使用一个“@”,如:
SELECT * FROM worker@remote_connect;
(3) 使用同义词对远程对象进行访问
对于已创建了数据库链接的远程数据中对象,可以在本地数据库创建其同义词,达到在访问在逻辑上相当天本地数据库对象,使语法简洁,如下:
CREATE SYNONYM worker_syn
创建后,对于远程数据库remote_connect的worker表的访问就可以变成如下:
select * from worker_syn;