zoukankan      html  css  js  c++  java
  • 储存过程、游标与触发器

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



  • 相关阅读:
    9- 遍历map集合的方法
    linux下修改了tomcat端口之后无法访问
    汪汪
    无题
    python之禅
    kettle连接oracle出现Error connecting to database: (using class oracle.jdbc.driver.OracleDriver)
    Android camera
    网站部署,网站要求需要支持mb_substring
    oracle笔记
    CSS jQuery 图片全屏切换
  • 原文地址:https://www.cnblogs.com/xiaotang5051729/p/10150225.html
Copyright © 2011-2022 走看看