转自:http://www.51testing.com/?uid-357760-action-viewspace-itemid-813791
由于业务的需求,首次接触了Oracle的存储过程。现在将整个从需求到实现的过程 总结一下,以备今后参考。
一、需求:
按照新利跟不同商户谈下来的手续费费率,将订单表中的手续费字段,定期的更新计算。
二、需求分析:
1。涉及到两张表:商户信息表(pmmerinfo) 和 订单表(ofordertradea);
2。根据商户信息表,修改订单表中的手续费字段;
3。由于手续费费率,可能不定期的改变,导致了不能在订单入库的时候,就把手续费算出来。也就是说,可能在某一个时间段利用这个费率,而在另一个时间段内用另外一个费率。要求跟系统时间对应。
三、存储过程:(procedure)
考虑到需求的目的不会变,而且都是对数据库的操作,所以准备采用存储过程。
1。存储过程的结构:
A:带参数的存储过程:
create or replace procedure 存储过程名(var_1 in varchar2 , var_2 in number , var_3 in date) is
定义变量1 变量类型 := 赋初值;
定义变量2 变量类型 := 赋初值;
begin
存储过程体。。。。
。。。。。。。。。。。。。
end 存储过程名 ;
例子:
create or replace procedure pro_test(var_1 in varchar2 , var_2 in number , var_3 in date) is
v1 number := 1;
v2 date := sysdate;(系统时间)
begin
存储过程体。。。。
。。。。。。。。。。。。。
end pro_test ;
B: 无参数的存储过程:
create or replace procedure 存储过程名 is
定义变量1 变量类型 := 赋初值;
定义变量2 变量类型 := 赋初值;
begin
存储过程体。。。。
。。。。。。。。。。。。。
end 存储过程名 ;
例子:
create or replace procedure pro_test is
v1 number := 1;
v2 date := sysdate;(系统时间)
begin
存储过程体。。。。
。。。。。。。。。。。。。
end pro_test ;
2。存储过程的游标:
在取得集合时,用存储过程的游标 Cursor 很重要。利用它可以遍历查询的结果集。
游标的定义、打开、使用和关闭:
Cursor cur_name is select *|某几列 from table_name where ... ;
open cur_name;
for cur_result in cur_name LOOP
begin
.............................................................
当使用游标中的字段时可以这样: cur_result.列名
end;
end LOOP;
close cur_name;
3。本需求生成的存储过程代码:
create or replace procedure update_merinfo_fee_rate is
Cursor cusor_1 is select * from pmmerinfo ; --取得商户表的游标,目的是遍历取得 商户号 和 费率
fee_rate float := 1.5; --定义费率变量,并赋初值
begin
open cusor_1;----打开游标
for cur_result in cusor_1 LOOP--遍历游标
begin--取得一个商户的费率,并修改订单信息表中的 手续费
fee_rate := to_number(cur_result.transfeerate);
update ofordertradea ab set ab.transfee = (ab.transamt*fee_rate) where ab.merchantid = cur_result.merchantid and ab.transtime >= to_char((sysdate-1) , 'yyyymmdd') and ab.transtime < to_char((sysdate) , 'yyyymmdd');
end;
end LOOP;--遍历游标结束
close cusor_1; --关闭游标
end update_merinfo_fee_rate;--存储过程结束
4。 存储过程调试:
用pl/sql developer debug
连接数据库后建立一个Test WINDOW
在窗口输入调用SP的代码,F9开始debug,CTRL+N单步调试
四、Oracle定时任务:(job)
名称 | 类型 | 注释 |
Job | BINARY_INTEGER | 任务的唯一识别号 |
What | VARCHAR2 | 作为任务执行的PL/SQL代码 |
Next_date | VARCHAR2 | 任务下一次运行的时间 |
Interval | VARCHAR2 | 日期表达式,用来计算下一次任务运行的时间 |
参数job是一个整数,用来唯一地标示一个任务。该参数既可由用户指定也可由系统自动赋予,这完全取决于提交任务时选用了那一个任务提交过程。DBMS_JOB.SUBMIT过程通过获得序列SYS.JOBSEQ的下一个值来自动赋予一个任务号。该任务号是作为一个OUT参数返回的,所以调用者随后可以识别出提交的任务。而DBMS_JOB.ISUBMIT过程则由调用者给任务指定一个识别号,这时候,任务号的唯一性就完全取决于调用者了。
除了删除或者重新提交任务,一般来说任务号是不能改变的。即使当数据库被导出或者被导入这样极端的情况,任务号也将被保留下来。所以在执行含有任务的数据的导入/导出操作时很可能会发生任务号冲突的现象。
what参数是一个可以转化为合法PL/SQL调用的字符串,该调用将被任务队列自动执行。在what参数中,如果使用文字字符串,则该字符串必须用单引号括起来。 what参数也可以使用包含我们所需要字符串值的VARCHAR2变量。实际的PL/SQL调用必须用分号隔开。在PL/SQL调用中如果要嵌入文字字符串,则必须使用两个单引号。
what参数的长度在Oracle7.3中限制在2000个字节以内,在Oracle 8.0以后,扩大到了4000个字节,这对于一般的应用已完全足够。该参数的值一般情况下都是对一个PL/SQL存储过程的调用。在实际应用中,尽管可以使用大匿名Pl/SQL块,但建议大家最好不要这样使用。还有一个实际经验就是最好将存储过程调用封装在一个匿名块中,这样可以避免一些比较莫名错误的产生。我来举一个例子,一般情况下,what参数可以这样引用:
what =>’my_procedure(parameter1);’ |
但是比较安全的引用,应该这样写:
what =>’begin my_procedure(parameter1); end;’ |
任何时候,我们只要通过更改what参数就可以达到更改任务定义的目的。但是有一点需要注意,通过改变what参数来改变任务定义时,用户当前的会话设置也被记录下来并成为任务运行环境的一部分。如果当前会话设置和最初提交任务时的会话设置不同,就有可能改变任务的运行行为。意识到这个潜在的副作用是非常重要的,无论何时只要应用到任何DBMS_JOB过程中的what参数时就一定要确保会话设置的正确。
Next_date参数是用来调度任务队列中该任务下一次运行的时间。这个参数对于DBMS_JOB.SUBMIT和DBMS_JOB.BROKEN这两个过程确省为系统当前时间,也就是说任务将立即运行。
当将一个任务的next_date参数赋值为null时,则该任务下一次运行的时间将被指定为4000年1月1日,也就是说该任务将永远不再运行。在大多数情况下,这可能是我们不愿意看到的情形。但是,换一个角度来考虑,如果想在任务队列中保留该任务而又不想让其运行,将next_date设置为null却是一个非常简单的办法。
Next_date也可以设置为过去的一个时间。这里要注意,系统任务的执行顺序是根据它们下一次的执行时间来确定的,于是将next_date参数设置回去就可以达到将该任务排在任务队列前面的目的。这在任务队列进程不能跟上将要执行的任务并且一个特定的任务需要尽快执行时是非常有用的。
Internal参数是一个表示Oracle合法日期表达式的字符串。这个日期字符串的值在每次任务被执行时算出,算出的日期表达式有两种可能,要么是未来的一个时间要么就是null。这里要强调一点:很多开发者都没有意识到next_date是在一个任务开始时算出的,而不是在任务成功完成时算出的。
当任务成功完成时,系统通过更新任务队列目录表将前面算出的next_date值置为下一次任务要运行的时间。当由interval表达式算出next_date是null时,任务自动从任务队列中移出,不会再继续执行。因此,如果传递一个null值给interval参数,则该任务仅仅执行一次。
通过给interval参数赋各种不同的值,可以设计出复杂运行时间计划的任务。本文后面的“任务间隔和日期算法”将对interval表达式进行详细讨论,并给出一个实际有用interval表达式的例子。
一些简单的interval参数设置例子
描述 | Interval参数值 |
每天运行一次 | 'SYSDATE + 1' |
每小时运行一次 | 'SYSDATE + 1/24' |
每10分钟运行一次 | 'SYSDATE + 10/(60*24)' |
每30秒运行一次 | 'SYSDATE + 30/(60*24*60)' |
每隔一星期运行一次 | 'SYSDATE + 7' |
不再运行该任务并删除它 | NULL |
定时到特定日期或时间的任务例子
描述 | INTERVAL参数值 |
每天午夜12点 | 'TRUNC(SYSDATE + 1)' |
每天早上8点30分 | 'TRUNC(SYSDATE + 1) + (8*60+30)/(24*60)' |
每星期二中午12点 | 'NEXT_DAY(TRUNC(SYSDATE ), ''TUESDAY'' ) + 12/24' |
每个月第一天的午夜12点 | 'TRUNC(LAST_DAY(SYSDATE ) + 1)' |
每个季度最后一天的晚上11点 | 'TRUNC(ADD_MONTHS(SYSDATE + 2/24, 3 ), 'Q' ) -1/24' |
每星期六和日早上6点10分 | 'TRUNC(LEAST(NEXT_DAY(SYSDATE, ''SATURDAY"), NEXT_DAY(SYSDATE, "SUNDAY"))) + (6×60+10)/(24×60)' |
5。对应本存储过程的定时任务:(每隔1分钟执行一次)
begin
sys.dbms_job.change(job => 21,
what => 'update_merinfo_fee_rate;',
next_date => to_date('08-06-2010 10:54:28', 'dd-mm-yyyy hh24:mi:ss'),
interval => 'sysdate+1/1440');
commit;
end;
/
6。手动执行定时任务:(在 “command window” 窗口里)
SQL> begin
2 dbms_job.run(21);
3 end;
4 /
7。针对Oracle 10g 创建定时任务时的注意点:
what 里面写 存储过程的名字。一定要以 ";"号结尾;
这样,整个过程就完成了。。。。。。。。