博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
储存过程、游标与触发器
阅读量:6573 次
发布时间:2019-06-24

本文共 14433 字,大约阅读时间需要 48 分钟。

什么是存储过程(Stored Procedure)?

一段存储在数据库的“子程序”,下面对这两个部分进行分开的阐释。

子程序(Subprogram):
一段可以被调用的子程序(subprogram)要么是一个过程(Procedrue),要么是一个函数(function).
过程(Procerue)和函数(function)的区别之一是:函数总是只返回一个单个的值, 而过程并不是。(摘自Oracle Database concepts guide)
数据库的存储过程通常是指一系列的SQL语句构成的”SQL代码块”, 和普通的SQL语句的不同点是, 代码块中包含了原生SQL所没有的元素, 例如cursor, variable,if, else, loop。
“存储在数据库”
SQL语句都是需要通过数据库相应组件编译后才能执行,最常见的例子是通过JDBC, 或ODBC创建数据区连接,发送SQL语句给数据库执行,并在程序中获得数据库返回的结果。
存储过程则是把经常会被重复使用的SQL语句逻辑块封装起来,编译好,存储在数据库服务器端(这里的客户端是需要连接数据库的应用程序,但该应用程序本身也可能是一个Web服务器)。 这样当存储过程再次被调用时,就无须编译了。 而调用的过程也无须发送SQL语句,只需要发送一个存储过程的标识, 数据库就可以找到相应的存储过程予以调用。

储存过程:CREATE OR REPLACE PROCEDURE 存储过程名ISBEGINNULL;END;行1:CREATE OR REPLACE PROCEDURE 是一个SQL语句通知Oracle数据库去创建一个叫做"存储过程名"存储过程, 如果存在就覆盖它;行2:IS关键词表明后面将跟随一个PL/SQL体。行3:BEGIN关键词表明PL/SQL体的开始。行4:NULL PL/SQL语句表明什么事都不做,这句不能删去,因为PL/SQL体中至少需要有一句;行5:END关键词表明PL/SQL体的结束存储过程创建语法一:create or replace procedure 存储过程名(param1 in type,param2 out type)as变量1 类型(值范围); –vs_msg VARCHAR2(4000);变量2 类型(值范围);BeginSelect count(*) into 变量1 from 表A where列名=param1;If (判断条件) then  Select 列名 into 变量2 from 表A where列名=param1;  Dbms_output.Put_line(‘打印信息’);Elsif (判断条件) then  Dbms_output。Put_line(‘打印信息’);ElseRaise 异常名(NO_DATA_FOUND);  End if;ExceptionWhen others thenRollback;End;注意事项:1, 存储过程参数不带取值范围,in表示传入,out表示输出类型可以使用任意Oracle中的合法类型。2, 变量带取值范围,后面接分号3, 在判断语句前最好先用count(*)函数判断是否存在该条操作记录4, 用select 。。。into。。。给变量赋值5, 在代码中抛异常用 raise 异常名存储过程创建语法二:CREATE OR REPLACE PROCEDURE存储过程名(–定义参数is_ym IN CHAR(6) ,the_count OUT NUMBER,)AS–定义变量vs_msg VARCHAR2(4000); –错误信息变量vs_ym_beg CHAR(6); –起始月份vs_ym_end CHAR(6); –终止月份vs_ym_sn_beg CHAR(6); –同期起始月份vs_ym_sn_end CHAR(6); –同期终止月份–定义游标(简单的说就是一个可以遍历的结果集)CURSOR cur_1 ISSELECT 。。。FROM 。。。WHERE 。。。GROUP BY 。。。;BEGIN–用输入参数给变量赋初值,用到了Oralce的SUBSTR,TO_CHAR,ADD_MONTHS,TO_DATE 等很常用的函数。vs_ym_beg := SUBSTR(is_ym,1,6);vs_ym_end := SUBSTR(is_ym,7,6);vs_ym_sn_beg := TO_CHAR(ADD_MONTHS(TO_DATE(vs_ym_beg,’yyyymm’), -12),’yyyymm’);vs_ym_sn_end := TO_CHAR(ADD_MONTHS(TO_DATE(vs_ym_end,’yyyymm’), -12),’yyyymm’);–先删除表中特定条件的数据。DELETE FROM 表名 WHERE ym = is_ym;–然后用内置的DBMS_OUTPUT对象的put_line方法打印出影响的记录行数,其中用到一个系统变量SQL%rowcountDBMS_OUTPUT.put_line(‘del上月记录=’||SQL%rowcount||’条‘);INSERT INTO表名(area_code,ym,CMCODE,rmb_amt,usd_amt)SELECT area_code,is_ym,CMCODE,SUM(rmb_amt)/10000,SUM(usd_amt)/10000FROM BGD_AREA_CM_M_BASE_TWHERE ym >= vs_ym_begAND ym <= vs_ym_endGROUP BY area_code,CMCODE;DBMS_OUTPUT.put_line(‘ins当月记录=’||SQL%rowcount||’条‘);–遍历游标处理后更新到表。遍历游标有几种方法,用for语句是其中比较直观的一种。FOR rec IN cur_1 LOOPUPDATE 表名 SET rmb_amt_sn = rec.rmb_amt_sn,usd_amt_sn = rec.usd_amt_snWHERE area_code = rec.area_codeAND CMCODE = rec.CMCODEAND ym = is_ym;END LOOP;COMMIT;–错误处理部分。OTHERS表示除了声明外的任意错误。SQLERRM是系统内置变量保存了当前错误的详细信息。EXCEPTIONWHEN OTHERS THENvs_msg := ’ERROR IN xxxxxxxxxxx_p(‘||is_ym||’):’||SUBSTR(SQLERRM,1,500);ROLLBACK;–把当前错误记录进日志表。INSERT INTO LOG_INFO(proc_name,error_info,op_date)VALUES(‘xxxxxxxxxxx_p’,vs_msg,SYSDATE);COMMIT;RETURN;END;oracle存储过程语法1 、判断语句:if 比较式 then begin end; end if;create or replace procedure test(x in number) isbeginif x >0 thenbeginx := 0 - x;end;end if;if x = 0 thenbeginx: = 1;end;end if;end test;2 、For 循环For … in … LOOP– 执行语句end LOOP;(1) 循环遍历游标create or replace procedure test() asCursor cursor is select name from student; name varchar(20);beginfor name in cursor LOOPbegindbms_output.putline(name);end;end LOOP;end test;(2) 循环遍历数组create or replace procedure test(varArray in myPackage.TestArray) as–( 输入参数varArray 是自定义的数组类型,定义方式见标题6)i number;begini := 1; – 存储过程数组是起始位置是从1 开始的,与java 、C 、C 等语言不同。因为在Oracle 中本是没有数组的概念的,数组其实就是一张– 表(Table), 每个数组元素就是表中的一个记录,所以遍历数组时就相当于从表中的第一条记录开始遍历for i in 1..varArray.count LOOPdbms_output.putline(‘The No.’|| i || ’record in varArray is:’||varArray(i));end LOOP;end test;3 、While 循环while 条件语句 LOOPbeginend;end LOOP;E.gcreate or replace procedure test(i in number) asbeginwhile i < 10 LOOPbegini:= i 1;end;end LOOP;end test;4 、数组首先明确一个概念:Oracle 中本是没有数组的概念的,数组其实就是一张表(Table), 每个数组元素就是表中的一个记录。使用数组时,用户可以使用Oracle 已经定义好的数组类型,或可根据自己的需要定义数组类型。(1) 使用Oracle 自带的数组类型x array; – 使用时需要需要进行初始化e.g:create or replace procedure test(y out array) isx array;beginx := new array();y := x;end test;(2) 自定义的数组类型 ( 自定义数据类型时,建议通过创建Package 的方式实现,以便于管理)create or replace package myPackage isPublic type declarations type info is record( name varchar(20), y number);type TestArray is table of info index by binary_integer;– 此处声明了一个TestArray 的类型数据,其实其为一张存储Info 数据类型的Table 而已,及TestArray 就是一张表,有两个字段,一个是name ,一个是y 。需要注意的是此处使用了Index by binary_integer 编制该Table 的索引项,也可以不写,直接写成:type TestArray istable of info ,如果不写的话使用数组时就需要进行初始化:varArray myPackage.TestArray; varArray := new myPackage.TestArray();end TestArray;

实例:

1 CREATE OR REPLACE PROCEDURE CSSS_PROC_SHEET_TO_HIS IS 2        ------- 查询待归档工单 3        CURSOR mycusor IS SELECT A.TASK_PARAMS 4               FROM COMMON_TASK_TD A 5               WHERE A.TASK_TYPE = 'FLOW_END' 6               AND A.VERSION = 0 7               AND A.IS_FINISHED = 'N' 8              -- and A.Task_Params like 'CSS201807%' 9              AND ROWNUM <10000;10           /*CURSOR mycusor IS SELECT t.sheet_id TASK_PARAMS  FROM pg_j_sheet t WHERE t.deal_flag IN (1,2,4,5,6,9) AND ROWNUM<501;*/11 err_num NUMBER; /*申明变量*/12 err_msg varchar2(4000); /*申明变量*/13 seq NUMBER;/*申明变量*/14        CURSOR excusor IS SELECT A.EXPERT_SHEET_ID15               FROM EX_EXPERT_DEAL_SHEET A16               WHERE A.EXPERT_SHEET_STATUS > 117               AND ROWNUM < 20;18       19 BEGIN20     FOR MYROW IN mycusor LOOP   /*循环遍历游标*/21         BEGIN22                   ----------- 参数日志表23                   INSERT INTO csss_his.pg_j_paralog SELECT * FROM pg_j_paralog a WHERE a.serial_id IN (SELECT24                   b.serial_id FROM PG_J_BUSILOG b WHERE b.SHEET_ID = MYROW.TASK_PARAMS);25                   --INSERT INTO csss_his.pg_j_paralog SELECT * FROM pg_j_paralog a WHERE a.serial_id=serow.serial_id;26                   DELETE FROM pg_j_paralog a  WHERE a.serial_id IN (SELECT27                   b.serial_id FROM PG_J_BUSILOG b WHERE b.SHEET_ID = MYROW.TASK_PARAMS);28               --------- 业务日志表29               INSERT INTO csss_his.pg_j_busilog SELECT * FROM pg_j_busilog a WHERE a.sheet_id=MYROW.TASK_PARAMS;30               DELETE FROM pg_j_busilog a WHERE a.sheet_id=MYROW.TASK_PARAMS;31               --------- 接口日志表32               --INSERT INTO csss_his.cm_j_interfacelog SELECT * FROM cm_j_interfacelog a WHERE a.sheet_id=MYROW.TASK_PARAMS;33               --DELETE FROM cm_j_interfacelog a WHERE a.sheet_id=MYROW.TASK_PARAMS;34               --------- 表附件表35               INSERT INTO csss_his.pg_j_table_file SELECT * FROM pg_j_table_file a WHERE a.sheet_id=MYROW.TASK_PARAMS;36               DELETE FROM pg_j_table_file a WHERE a.sheet_id=MYROW.TASK_PARAMS;37               FOR exrow IN excusor LOOP38                   BEGIN39                         -------- 专席工单表40                         INSERT INTO csss_his.EX_EXPERT_DEAL_SHEET SELECT * FROM EX_EXPERT_DEAL_SHEET a WHERE a.EXPERT_SHEET_ID=exrow.expert_sheet_id;41                         DELETE FROM EX_EXPERT_DEAL_SHEET a WHERE a.expert_sheet_id=exrow.expert_sheet_id;42                   END;43               END LOOP;44               --------- 客户信息表45               INSERT INTO csss_his.pg_j_custiinfo SELECT * FROM pg_j_custiinfo a WHERE a.sheet_id=MYROW.TASK_PARAMS;46               DELETE FROM pg_j_custiinfo a WHERE a.sheet_id=MYROW.TASK_PARAMS;47               ------工单日志48               INSERT INTO csss_his.pg_j_sheet_log SELECT * FROM pg_j_sheet_log a WHERE a.sheet_id=MYROW.TASK_PARAMS;49               DELETE FROM pg_j_sheet_log a WHERE a.sheet_id=MYROW.TASK_PARAMS;50               --------- 预处理工单表51               INSERT INTO csss_his.pg_j_sheet SELECT * FROM pg_j_sheet a WHERE a.sheet_id=MYROW.TASK_PARAMS;52               --------- 未统一编码工单表(新增备注2018.07.24)53               INSERT INTO csss_his.pg_j_sheet_unicode54                  SELECT a.*55                    FROM pg_j_sheet a, pg_c_phenomena_v_desc b56                   WHERE a.sheet_id = MYROW.TASK_PARAMS57                     and a.ycl_obligate7_flag is null58                     and a.phe_type_id = b.PHE_TYPE_ID59                     and b.COM_TYPE is not null60                     and a.deal_flag != 9;61                ----存放一个月数据62               INSERT INTO pg_j_sheet_month SELECT * FROM pg_j_sheet a WHERE a.sheet_id=MYROW.TASK_PARAMS;63               DELETE FROM pg_j_sheet a WHERE a.sheet_id=MYROW.TASK_PARAMS;64               DELETE FROM COMMON_TASK_TD WHERE TASK_PARAMS=MYROW.TASK_PARAMS;65               dbms_output.put_line('info--------------');66               COMMIT;67               EXCEPTION68               WHEN OTHERS THEN69                            err_num:= SQLCODE;70                            err_msg:= SUBSTR(SQLERRM, 1, 3000);71                            select MY_SEQENCE.NEXTVAL into seq from dual;72                            insert into PUB_ERROR_LOG values(seq,sysdate,err_msg||':::'||MYROW.TASK_PARAMS,err_num);73                 goto v_continue;74                 <
>75 NULL;76 END;77 END LOOP;78 END CSSS_PROC_SHEET_TO_HIS;

什么是游标?

①从表中检索出结果集,从中每次指向一条记录进行交互的机制。

 ②关系数据库中的操作是在完整的行集合上执行的。

    由 SELECT 语句返回的行集合包括满足该语句的 WHERE 子句所列条件的所有行。由该语句返回完整的行集合叫做结果集。
    应用程序,尤其是互动和在线应用程序,把完整的结果集作为一个单元处理并不总是有效的。

    这些应用程序需要一种机制来一次处理一行或连续的几行。而游标是对提供这一机制的结果集的扩展。

  游标是通过游标库来实现的。游标库是常常作为数据库系统或数据访问 API 的一部分而得以实现的软件,

  用来管理从数据源返回的数据的属性(结果集)。这些属性包括并发管理、在结果集中的位置、返回的行数,

  以及是否能够在结果集中向前和/或向后移动(可滚动性)。

    游标跟踪结果集中的位置,并允许对结果集逐行执行多个操作,在这个过程中可能返回至原始表,也可能不返回至原始表。

       换句话说,游标从概念上讲基于数据库的表返回结果集。

   由于它指示结果集中的当前位置 ,就像计算机屏幕上的光标指示当前位置一样,“游标”由此得名。

2,游标有什么作用?

 ①指定结果集中特定行的位置。 
 ②基于当前的结果集位置检索一行或连续的几行。 
 ③在结果集的当前位置修改行中的数据。 
 ④对其他用户所做的数据更改定义不同的敏感性级别。
 ⑤可以以编程的方式访问数据库。

3,为什么避免使用游标?

 ①在创建游标时,最需要考虑的事情是,“是否有办法避免使用游标?”
  因为游标的效率较差,如果游标操作的数据超过1万行,那么就应该改写;
  如果使用了游标,就要尽量避免在游标循环中再进行表连接的操作。

4,Oracle游标的类型?

 ①静态游标:结果集已经确实(静态定义)的游标。分为隐式和显示游标。
  ⑴隐式游标:所有DML语句为隐式游标,通过隐式游标属性可以获取SQL语句信息。
  ⑵显示游标:用户显示声明的游标,即指定结果集。当查询返回结果超过一行时,就需要一个显式游标。
 ②REF游标:动态关联结果集的临时对象。

游标的用法

-- 声明游标;CURSOR cursor_name IS select_statement--For 循环游标--(1)定义游标--(2)定义游标变量--(3)使用for循环来使用这个游标declare       --类型定义       cursor c_job       is       select empno,ename,job,sal       from emp       where job='MANAGER';       --定义一个游标变量v_cinfo c_emp%ROWTYPE ,该类型为游标c_emp中的一行数据类型       c_row c_job%rowtype;begin       for c_row in c_job loop         dbms_output.put_line(c_row.empno||'-'||c_row.ename||'-'||c_row.job||'-'||c_row.sal);       end loop;end;      --Fetch游标--使用的时候必须要明确的打开和关闭declare        --类型定义       cursor c_job       is       select empno,ename,job,sal       from emp       where job='MANAGER';       --定义一个游标变量       c_row c_job%rowtype;begin       open c_job;         loop           --提取一行数据到c_row           fetch c_job into c_row;           --判读是否提取到值,没取到值就退出           --取到值c_job%notfound 是false            --取不到值c_job%notfound 是true           exit when c_job%notfound;            dbms_output.put_line(c_row.empno||'-'||c_row.ename||'-'||c_row.job||'-'||c_row.sal);         end loop;       --关闭游标      close c_job;end;--1:任意执行一个update操作,用隐式游标sql的属性%found,%notfound,%rowcount,%isopen观察update语句的执行情况。       begin         update emp set ENAME='ALEARK' WHERE EMPNO=7469;         if sql%isopen then           dbms_output.put_line('Openging');           else             dbms_output.put_line('closing');             end if;          if sql%found then            dbms_output.put_line('游标指向了有效行');--判断游标是否指向有效行            else              dbms_output.put_line('Sorry');              end if;              if sql%notfound then                dbms_output.put_line('Also Sorry');                else                  dbms_output.put_line('Haha');                  end if;                   dbms_output.put_line(sql%rowcount);                   exception                      when no_data_found then                       dbms_output.put_line('Sorry No data');                       when too_many_rows then                         dbms_output.put_line('Too Many rows');                         end;declare       empNumber emp.EMPNO%TYPE;       empName emp.ENAME%TYPE;       begin         if sql%isopen then           dbms_output.put_line('Cursor is opinging');           else             dbms_output.put_line('Cursor is Close');             end if;             if sql%notfound then               dbms_output.put_line('No Value');               else                 dbms_output.put_line(empNumber);                 end if;                 dbms_output.put_line(sql%rowcount);                 dbms_output.put_line('-------------');                                  select EMPNO,ENAME into  empNumber,empName from emp where EMPNO=7499;                 dbms_output.put_line(sql%rowcount);                                 if sql%isopen then                dbms_output.put_line('Cursor is opinging');                else                dbms_output.put_line('Cursor is Closing');                end if;                 if sql%notfound then                 dbms_output.put_line('No Value');                 else                 dbms_output.put_line(empNumber);                 end if;                 exception                    when no_data_found then                     dbms_output.put_line('No Value');                     when too_many_rows then                       dbms_output.put_line('too many rows');                       end;

什么是触发器?

每当一个特定的数据操作语句(insert、update、delete)在指定的表上发出时,Oracle自动地执行触发器中定义的语句序列。

注意:select语句不在上述的操作范围内。

create or replace trigger synch_userid after insert or deleteon eosoperatorFOR EACH ROW --每一行触发一次declarei number; --申明变量j number; --申明变量begin   case  --插入 when inserting then   select sys_staff_seq.nextval into i from dual; --插入工号i  insert into DN.sys_staff@YCL_VISITING_DN(staff_id,user_id,operator_name,org_id,password,duration) values(i,:new.userid,:new.operatorname,5,'13ee51a026df8a8265f79dad03dcfa2a',120);  --人员机构表插入  insert into DN.sys_staff_attr@YCL_VISITING_DN(staff_id) values(i);  --人员权限表插入  insert into DN.sys_staff_role_rel@YCL_VISITING_DN(role_id,staff_id) values(5,i);  when deleting then    --查询主键    select staff_id into j from DN.sys_staff@YCL_VISITING_DN where user_id=:old.userid;    delete DN.sys_staff_attr@YCL_VISITING_DN where staff_id=j;    delete DN.sys_staff_role_rel@YCL_VISITING_DN where staff_id=j;    delete DN.sys_staff@YCL_VISITING_DN where user_id =:old.userid; end case;END;

注意:

1、:old \ :new :代表的是同一条记录。

2、:old :表示操作该行之前,这一行的值。

3、:new :标示操作该行之后,这一行的值。

 

转载于:https://www.cnblogs.com/xiaotang5051729/p/10150225.html

你可能感兴趣的文章
POJ2127 LICS模板
查看>>
Python笔记8----DataFrame(二维)
查看>>
JavaScript 特殊效果代码
查看>>
【?】codeforces721E Road to Home(DP+单调队列)
查看>>
MySQL 仅保留7天、一个月数据
查看>>
OGG 11g Checkpoint 详解
查看>>
PHP中使用socket通信响应速度慢的原因与解决办法
查看>>
Win7下安装Mysql(解压缩版)
查看>>
UVA 11992 Fast Matrix Operations (降维)
查看>>
Asp.net core Identity + identity server + angular 学习笔记 (第一篇)
查看>>
暂时不想读研的几点理由
查看>>
增加临时表空间组Oracle11g单实例
查看>>
Diff Two Arrays
查看>>
stark组件(1):动态生成URL
查看>>
169. Majority Element
查看>>
大整数加法
查看>>
下拉菜单
查看>>
[清华集训2014]玛里苟斯
查看>>
Doctype作用?严格模式与混杂模式如何区分?它们有何意义
查看>>
0029-求最小的数
查看>>