Oracle存储过程编写注意事项

环境:centos7.6.1810
归类:database/oracle/11.2.0.4.0/plsql
简介:整理记录编写存储过程需要注意的一些知识点

1.游标

1.1.普通游标

--打印scott.emp雇员表中所有雇员信息(雇员编号和雇员姓名)
create or replace procedure proc_emp_test(out_msg out varchar2)
is
  cursor emps is select * from emp;
begin
  for emp in emps loop
    dbms_output.put_line(emp.empno||'-'||emp.ename);
  end loop;
  out_msg := 'OK';
exception
  when others then
    out_msg := sqlcode || '-' || sqlerrm;
end proc_emp_test;  
--执行存储过程
declare
  v_out_msg varchar2(1000);
begin
  proc_emp_test(v_out_msg);
end;
  • 游标不可以在begin之后定义

1.2.嵌套游标

--打印scott.emp雇员表中所有雇员及部门信息(雇员编号、雇员姓名、部门编号、部门名称)
create or replace procedure proc_emp_test2(out_msg out varchar2)
is
  cursor emps is select * from emp;
  cursor depts is select * from dept;
begin
  for emp in emps loop
    for dept in depts loop
      if emp.deptno = dept.deptno then
        dbms_output.put_line(emp.empno || '-' || emp.ename || ',' || dept.deptno || '-' || dept.dname);
      end if;
    end loop;
  end loop;
  out_msg := 'OK';
exception
  when others then
    out_msg := sqlcode || '-' || sqlerrm;
end proc_emp_test2; 
--执行存储过程
declare
  v_out_msg varchar2(1000);
begin
  proc_emp_test2(v_out_msg);
end;

1.3.带参游标

--根据输入的雇员编号打印scott.emp雇员表中所有雇员信息(雇员编号、雇员姓名)
create or replace procedure proc_emp_test3(in_empno in varchar2,out_msg out varchar2)
is
  cursor emps(v_emp_no varchar2) is select * from emp where empno = v_emp_no;
begin
  for emp in emps(in_empno) loop
    dbms_output.put_line(emp.empno || '-' || emp.ename);
  end loop;
  out_msg := 'OK';
exception
  when others then
    out_msg := sqlcode || '-' || sqlerrm;
end proc_emp_test3;  
--执行存储过程
declare
  v_emp_no  varchar2(32);
  v_out_msg varchar2(1000);
begin
  v_emp_no := '7839';
  proc_emp_test3(v_emp_no,v_out_msg);
end;
  • 注意输入参数和游标参数的互传,游标参数必须定义数据类型。游标参数v_emp_no看似可以用in_empno代替,但是不建议这样做。

2.变量

2.1.输入输出参数

输入输出参数不建议和存储过程中出现的表字段一样,否则会出现一些意想不到的异常。

oracle数据库的存储过程里,如果变量名和表的字段名相同了,会优先当做字段名来看待。

--将empno=7369的smith,工作由CLERK调整为MANAGER
create or replace procedure proc_emp_test4(job out varchar2,out_msg out varchar2)
is
begin
  job := 'MANAGER'; --job是输出参数,直接赋值以后,不能用
  update emp t set t.job = job where t.empno = '7369'; --此时相当于t.job=t.job,不变化
  commit;
  out_msg := 'OK';
exception
  when others then
    rollback;
    out_msg := sqlcode || '-' || sqlerrm;
end proc_emp_test4;
declare
  v_job  varchar2(32);
  v_out_msg varchar2(1000);
begin
  proc_emp_test4(v_job,v_out_msg);
end;
  • update没有被正确执行,数据没有任何变化,也不会报错。
--根据输入的empno,将其职位调整为MANAGER
create or replace procedure proc_emp_test5(empno in varchar2,out_msg out varchar2)
is
begin
  update emp t set t.job = 'MANAGER' where t.empno = empno; --此时相当于 where 1=1; 全表
  commit;
  out_msg := 'OK';
exception
  when others then
    rollback;
    out_msg := sqlcode || '-' || sqlerrm;
end proc_emp_test5;
declare
  v_empno  varchar2(32);
  v_out_msg varchar2(1000);
begin
  v_empno := '7369';
  proc_emp_test5(v_empno,v_out_msg);
end;
  • update没有被正确执行,所有职员的职位全部变成了MANAGER。

输入参数不能被加工赋值。

create or replace procedure proc_emp_test6(v_date in varchar2,out_msg out varchar2)
is
  v_count number;
begin
  --如果输入日期为空,则默认为1984年1月1日
  if v_date is null then
    v_date := to_char('1984-01-01','yyyy-mm-dd'); --输入参数不能被赋值,编译报错
  end if;

  --根据输入日期参数统计入职时间在输入日期之后的职员人数
  select count(1)
   into v_count
  from emp t where t.hiredate > to_date(v_date,'yyyy-mm-dd');
  dbms_output.put_line(v_count);
  out_msg := 'OK';
exception
  when others then
    out_msg := sqlcode || '-' || sqlerrm;
end proc_emp_test6;

--上述存储过程编译报错,根据需求描述,可修改成以下
create or replace procedure proc_emp_test6(v_date in varchar2,out_msg out varchar2)
is
  v_count number;
  v_in_date varchar2(32); --定义变量解决v_date输入参数不能赋值
begin
  --如果输入日期为空,则默认为1984年1月1日
  if v_date is null then
    v_in_date := to_char('1984-01-01','yyyy-mm-dd');
  else
    v_in_date := v_date;
  end if;
  
  --根据输入日期参数统计入职时间在输入日期之后的职员人数
  select count(1)
    into v_count
   from emp t where t.hiredate > to_date(v_in_date,'yyyy-mm-dd');
  dbms_output.put_line(v_count);
  out_msg := 'OK';
exception
  when others then
    out_msg := sqlcode || '-' || sqlerrm;
end proc_emp_test6;

2.2.自定义变量

自定义变量在into使用时,一定要注意判空和判唯一。

--v_job输入的job不唯一或者不存在
create or replace procedure proc_emp_test7(v_job varchar2,out_msg out varchar2)
is
  v_empno varchar2(32);
    v_ename varchar2(32);
begin
  select t.empno,t.ename
   into v_empno,v_ename
  from emp t where t.job = v_job;
  dbms_output.put_line('empno:' || v_empno || ',v_ename:' || v_ename);
  out_msg := 'OK';
exception
  when others then
    out_msg := sqlcode || '-' || sqlerrm;
        dbms_output.put_line(out_msg);
end proc_emp_test7;
declare
  v_job  varchar2(32);
  v_out_msg varchar2(1000);
begin
  --不唯一
  v_job := 'CLERK'; ---1422-ORA-01422: exact fetch returns more than requested number of rows
  --不存在 
  --v_job := 'xxx'; ---100-ORA-01403: no data found
  proc_emp_test7(v_job,v_out_msg);
end;
--建议修改,在into到v_empno,v_ename之前进行一次判断
create or replace procedure proc_emp_test7(v_job varchar2,out_msg out varchar2)
is
  v_count number;
  v_empno varchar2(32);
  v_ename varchar2(32);
begin
  select count(1)
   into v_count
  from emp t
  where t.job = v_job;

  if v_count = 1 then
    select t.empno,t.ename
     into v_empno,v_ename
    from emp t where t.job = v_job;
    dbms_output.put_line('empno:' || v_empno || ',v_ename:' || v_ename);
    out_msg := 'OK';
  elsif v_count = 0 then
    out_msg := '输入的job:' || v_job || ',查询不到职员信息';
  elsif v_count > 1 then
    out_msg := '输入的job:' || v_job || ',查询到多个职员信息';
  end if;

  dbms_output.put_line(out_msg);
exception
  when others then
    out_msg := sqlcode || '-' || sqlerrm;
    dbms_output.put_line(out_msg);
end proc_emp_test7;

3.远程授权

通过dblink远程访问的表,必须得到远程用户显式的授权,存储过程中才能进行查询。

  • 创建testa用户,初始赋予CONNECT、RESOURCE角色权限,不能访问scott用户下的emp雇员信息表,然后赋予DBA角色权限,可以访问scott.emp,但是在存储过程中是不能直接访问的,会报错PL/SQL:ORA-00942:table or view does not exist,需要显式的对testa授权访问才能在存储过程中访问scott.emp。
/* sys用户下操作,生产环境下建议创建dba角色的用户,不要随意用sys用户操作 */
--创建用户
create user testa identified by testa;
--赋予初始角色权限,允许登录和增删改查表视图等
grant connect,resource to testa;
--此时无权限查询scott.emp,赋予dba角色
grant dba to testa;

/* testa用户下操作 */
--查询
select * from scott.emp;

--创建存储过程,编译报错
create or replace procedure proc_emp_test(out_msg out varchar2)
is
  cursor emps is select * from scott.emp;
begin
  for emp in emps loop
    dbms_output.put_line(emp.empno||'-'||emp.ename);
  end loop;
  out_msg := 'OK';
exception
  when others then
    out_msg := sqlcode || '-' || sqlerrm;
end proc_emp_test;

/* sys用户下操作 */
grant select on scott.emp to testa;

/* testa用户下操作 */
--重新编译存储过程,编译成功,不再报错

4.中断循环

return、goto、exit、continue在存储过程中的使用及区别(10g版本无continue方法):

continue:结束本次循环,后面不再执行,继续本次循环的下一次循环

exit:跳出本次循环,转而执行本循环的上一层循环的下一次循环

return:直接跳出PLSQL执行,后面都不再执行,直接结束

--构造两个序列,f_1为11,12,13;f_2为21,22,23
begin
  for f_1 in (select '1'||level lv1 from dual connect by level < 4) loop
    for f_2 in (select '2'||level lv2 from dual connect by level < 4) loop
      if f_2.lv2 = '22' then
        null;
        --continue;
        --exit;
        --return;
       end if;
    dbms_output.put_line(f_1.lv1 || '--' || f_2.lv2);
    end loop;
  end loop;
end;
--null
11--21
11--22
11--23
12--21
12--22
12--23
13--21
13--22
13--23

--continue
11--21
11--23
12--21
12--23
13--21
13--23

--exit
11--21
12--21
13--21

--return
11--21

goto是跳转语句,从一个位置直接跳转到指定位置,<<标志>>不能直接与exception、end loop等关键字相邻,需要用null间隔一下。

begin
  for f_1 in (select '1'||level lv1 from dual connect by level < 4) loop
    for f_2 in (select '2'||level lv2 from dual connect by level < 4) loop
      if f_2.lv2 = '22' then
        goto my_handle; --oracle10g实现continue的效果
       end if;
       dbms_output.put_line(f_1.lv1 || '--' || f_2.lv2);
       <<my_handle>>
       NULL;
    end loop;
  end loop;
end;
11--21
11--23
12--21
12--23
13--21
13--23

5.定时器

dbms_jobs定义日期的时候,注意细化时分秒,便于在固定日期重复执行。

--创建日期:2021-08-01,下次执行日期2021-08-02 02:20:00,以后每天2:20执行
declare
   jobno number;
begin
  sys.dbms_job.submit(jobno,
  'declare
     yeste_date varchar2(48);
   begin
     yeste_date:=to_char(sysdate,''yyyy-mm-dd'');
     pkg_rpt_stat.run_day_stat(yeste_date);
   end;',
  to_date('2021-08-02 02:20:00','yyyy-mm-dd hh24:mi:ss'),,
  'to_date(to_char(sysdate+1,''yyyy-mm-dd'')||''02:20:00'',''yyyy-mm-dd hh24:mi:ss'')');
 commit;
end;
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,684评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,143评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,214评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,788评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,796评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,665评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,027评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,679评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,346评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,664评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,766评论 1 331
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,412评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,015评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,974评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,073评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,501评论 2 343

推荐阅读更多精彩内容

  • 一、定义 所谓存储过程,就是一组用于完成特定数据库功能的SQL语句集,该SQL语句集经过编译后存储在数据库系统中。...
    lifeline张阅读 4,591评论 0 1
  • 一. 存储过程和存储函数的定义 定义:存储在数据库中,供所有用户程序调用的子程序叫做存储过程/存储函数。复杂点的解...
    GuangHui阅读 10,480评论 0 22
  • 存储过程优点 运行T-SQL语句进行编程有两种办法,一种是把T-SQL语句全部写在应用程序中,并存储在本地;另一种...
    不知名的蛋挞阅读 1,860评论 0 0
  • 存储过程的概念 存储过程Procedure是一组为了完成特定功能的SQL语句集合,经编译后存储在数据库中,用户通...
    轮子呵呵阅读 452评论 0 1
  • 创建和使用存储过程的注意事项: 1、在创建存储过程中,有多个参数时,有默认值的参数放在存储过程参数列表的最后 例如...
    肉肉要次肉阅读 2,236评论 0 0