储存过程、游标与触发器

什么是存储过程(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.2453               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 :标示操作该行之后,这一行的值。



原文地址:https://www.cnblogs.com/xiaotang5051729/p/10150225.html