什么是存储过程(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 存储过程名 IS BEGIN NULL; 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 类型(值范围); Begin Select 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(‘打印信息’); Else Raise 异常名(NO_DATA_FOUND); End if; Exception When others then Rollback; 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 IS SELECT 。。。 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%rowcount DBMS_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)/10000 FROM BGD_AREA_CM_M_BASE_T WHERE ym >= vs_ym_beg AND ym <= vs_ym_end GROUP BY area_code,CMCODE; DBMS_OUTPUT.put_line(‘ins当月记录=’||SQL%rowcount||’条‘); –遍历游标处理后更新到表。遍历游标有几种方法,用for语句是其中比较直观的一种。 FOR rec IN cur_1 LOOP UPDATE 表名 SET rmb_amt_sn = rec.rmb_amt_sn,usd_amt_sn = rec.usd_amt_sn WHERE area_code = rec.area_code AND CMCODE = rec.CMCODE AND ym = is_ym; END LOOP; COMMIT; –错误处理部分。OTHERS表示除了声明外的任意错误。SQLERRM是系统内置变量保存了当前错误的详细信息。 EXCEPTION WHEN OTHERS THEN vs_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) is begin if x >0 then begin x := 0 - x; end; end if; if x = 0 then begin x: = 1; end; end if; end test; 2 、For 循环 For … in … LOOP – 执行语句 end LOOP; (1) 循环遍历游标 create or replace procedure test() as Cursor cursor is select name from student; name varchar(20); begin for name in cursor LOOP begin dbms_output.putline(name); end; end LOOP; end test; (2) 循环遍历数组 create or replace procedure test(varArray in myPackage.TestArray) as –( 输入参数varArray 是自定义的数组类型,定义方式见标题6) i number; begin i := 1; – 存储过程数组是起始位置是从1 开始的,与java 、C 、C 等语言不同。因为在Oracle 中本是没有数组的概念的,数组其实就是一张 – 表(Table), 每个数组元素就是表中的一个记录,所以遍历数组时就相当于从表中的第一条记录开始遍历 for i in 1..varArray.count LOOP dbms_output.putline(‘The No.’|| i || ’record in varArray is:’||varArray(i)); end LOOP; end test; 3 、While 循环 while 条件语句 LOOP begin end; end LOOP; E.g create or replace procedure test(i in number) as begin while i < 10 LOOP begin i:= 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) is x array; begin x := new array(); y := x; end test; (2) 自定义的数组类型 ( 自定义数据类型时,建议通过创建Package 的方式实现,以便于管理) create or replace package myPackage is Public 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 is table 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_ID 15 FROM EX_EXPERT_DEAL_SHEET A 16 WHERE A.EXPERT_SHEET_STATUS > 1 17 AND ROWNUM < 20; 18 19 BEGIN 20 FOR MYROW IN mycusor LOOP /*循环遍历游标*/ 21 BEGIN 22 ----------- 参数日志表 23 INSERT INTO csss_his.pg_j_paralog SELECT * FROM pg_j_paralog a WHERE a.serial_id IN (SELECT 24 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 (SELECT 27 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 LOOP 38 BEGIN 39 -------- 专席工单表 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_unicode 54 SELECT a.* 55 FROM pg_j_sheet a, pg_c_phenomena_v_desc b 56 WHERE a.sheet_id = MYROW.TASK_PARAMS 57 and a.ycl_obligate7_flag is null 58 and a.phe_type_id = b.PHE_TYPE_ID 59 and b.COM_TYPE is not null 60 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 EXCEPTION 68 WHEN OTHERS THEN 69 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 <<v_continue>> 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 delete on eosoperator FOR EACH ROW --每一行触发一次 declare i 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 :标示操作该行之后,这一行的值。