美文网首页数据库
DB2存储过程语法

DB2存储过程语法

作者: RLM233 | 来源:发表于2017-12-12 15:48 被阅读2295次

    1. DB2存储过程示例:

    • 语法参数说明
      • procedure-name: 存储过程的名字,在同一个数据库的同一模式下,不能存在存储过程名相同参数数目相同的存储过程,即使参数的类型不同也不行。
      • (IN | OUT | INOUT parameter-name data-type,...) :传入参数 IN:输入参数OUT:输出参数INOUT:作为输入输出参数 parameter-name:参数名字,在此存储过程中唯一的标识符。data-type:参数类型,可以接收SQL类型和创建的表。不支持LONG VARCHAR, LONG VARGRAPHIC, DATALINK, REFERENCE和用户自定义类型。
      • SPECIFIC specific-name:唯一的特定名称(别名),可以用存储过程名代替,这个特定名称用于dorp存储过程,或者给存储过程添加注释用,但不能调用存储过程。如果不指定,则数据库会自动生成一个yymmddhhmmsshhn时间戳的名字。推荐给出别名。
      • DYNAMIC RESULT SETS integer:指定存储过程返回结果的最大数量。存储过程中虽然没有return语句,但是却能返回结果集。
      • CONTAINS SQL, READS SQL DATA, MODIFIES SQL DATA: 指定存储过程中的SQL访问级别 CONTAINS SQL: 表示存储过程可以执行中,既不可读取 SQL 数据,也不可修改 SQL 数据。READS SQL DATA: 表示存储过程可以执行中,可读取SQL,但不可修改 SQL 数据。MODIFIES SQL DATA: 表示存储过程可以执行任何 SQL 语句。可以对数据库中的数据进行增加、删除和修改。
      • DETERMINISTIC or NOT DETERMINISTIC:表示存储过程是动态或者非动态的。动态的返回的值是不确定的。非动态的存储过程每次执行返回的值是相同的。
      • CALLED ON NULL INPUT:表示可以调用存储过程而不管任何的输入参数是否为 NULL,并且,任何的OUT或者INOUT参数可以返回一个NULL或者非空值。检验参数是否为NULL是在过程中进行的。
      • INHERIT SPECIAL REGISTERS:表示继承专用寄存器。
      • OLD SAVEPOINT LEVEL or NEW SAVEPOINT LEVEL:建立存储点。OLD SAVEPOINT LEVEL是默认的存储点。
      • LANGUAGE SQL:指定程序的主体用的是SQL语言。
      • EXTERNAL ACTION or NO EXTERNAL ACTION:表示存储过程是否执行一些改变理数据库状态的活动,而不通过数据库管理器管。默认是 EXTERNAL ACTION。如果指定为 NO EXTERNAL ACTION ,则数据库会确定最最佳优化方案。
      • PARAMETER CCSID:指定所有输出字符串数据的编码,默认为UNICODE编码数据库为PARAMETER CCSID UNICODE ,其他的数据库默认为PARAMETER CCSID 3 ASCII。
      • SQL-procedure-body:存储过程的主体
    CREATE PROCEDURE procedure-name(IN | OUT | INOUT parameter-name data-type,...) )--存储过程可以设定输入参数和输出参数
    LANGUAGE SQL--DB2可以用多种语言编写存储过程,这里用的是纯SQL
    BEGIN--开始
    DECLARE vID smallint;--定义变量,和Oracle一样 DECLARE   变量名 变量的数据类型;
    FOR V AS SELECT BRND_CD FROM TMP_BRND_CD--for循环 tmp_brnd_cd预先创建好
    DO--循环体开始
    SET vID=BRND_CD;--对vID赋值,db2可以用set赋值,也可以用values赋值,这里可以写成values(BRND_CD) into vID
    INSERT INTO WWM_FORINSERT_TEST VALUES(vID);--往wwm_forinsert_test 插入数据
    END FOR;--循环体结束
    END @--存储过程结束
    
    CREATE PROCEDURE UPDATE_SAL (IN empNum CHAR(6),INOUT rating SMALLINT)
    LANGUAGE SQL
    BEGIN
        IF rating = 1 THEN
            UPDATE employee SET salary = salary * 1.10, bonus = 1000 WHERE empno = empNum;
        ELSEIF rating = 2 THEN
            UPDATE employee SET salary = salary * 1.05, bonus = 500 WHERE empno = empNum;
        ELSE
            UPDATE employee SET salary = salary * 1.03, bonus = 0 WHERE empno = empNum;
        END IF;
    END
    
    • 使用关键字SET给变量赋值
    SET total = 100;
    VALUES(100,200,200+1)INTO var1,var2,var3; --并行赋值,效率高
    SET total = NULL;
    SET total = (select sum(c1) from T1);
    SET sch = CURRENT SCHEMA;
    

    2. 游标

    • 定义游标:DECLARE 游标名 CURSOR FOR SELECT 语句
    DECLARE mycur1 CURSOR 
      FOR SELECT e.empno, e.lastname, e.job
          FROM employee e, department d
          WHERE e.workdept = d.deptno
            AND deptname ='PLANNING';
    
    • 在 SQL 过程中,游标除了迭代结果集中的行,还可以将结果集返回给调用程序或其他过程
      • WITHOUT RETURN/WITH return:选项指定游标的结果表是否用于作为从一个过程中返回的结果集。
      • WITH RETURN TO CALLER:选项指定将来自游标的结果集返回给调用者,后者可以是另一个过程或一个客户机应用程序。这是默认选项。
      • WITH RETURN TO CLIENT:选项指定将来自游标的结果集返回给客户机应用程序,绕过任何中间的嵌套过程。
    --下面是游标声明的几个例子:
    DECLARE c1 CURSOR FOR select * from staff;
    --(DECLARE关键字,cl游标名称,CURSOR是必须有的,指通过c1的游标来操作staff里所有的数据)最常用的最普通的。
    2.DECLARE c1 CURSOR WITH HOLD FOR select * form staff;
    3.DECLARE c1 CURSOR WITH RETURN TO CALLER FOR select * form  staff;
    4.DECLARE c1 CURSOR WITH RETURN TO CLIENT FOR select * form staff;
    
    • 若要从一个过程中返回结果集,需要:
      • 创建一个过程,创建时指定 DYNAMIC RESULT SETS 子句中声明存储过程将要返回结果集的数量。
      • 声明游标,声明时指定 WITH RETURN 子句。
      • 打开该游标,并使之保持 open 状态,如果关闭该游标,则结果集将不能返回给调用者应用程序。
    --返回一个结果集的游标的声明 
    CREATE PROCEDURE emp_from_dept()
     DYNAMIC RESULT SETS 1 --返回的结果集超出定义的数量会返回一个警告
     P1: BEGIN
      DECLARE c_emp_dept CURSOR WITH RETURN
       FOR SELECT empno, lastname, job, salary, comm.
           FROM employee
           WHERE workdept = ‘E21’;
    
       OPEN c_emp_dept;
      END P1
    

    3. 条件判断

    • if 语句
    IF years_of_serv > 30 THEN 
        SET gl_sal_increase = 15000;
    ELSEIF years_of_serv > 20 THEN
        SET gl_sal_increase = 12000; 
    ELSE 
        SET gl_sal_increase = 10000;
    END IF; 
    
    • CASE 语句:支持两种类型的 CASE 语句,以根据一个条件的状态实现逻辑的分支
      • simple CASE 语句用于根据一个字面值进入某个逻辑
      • searched CASE 语句用于根据一个表达式的值进入某个逻辑
    --使用 searched CASE 语句的存储过程
    CREATE PROCEDURE sal_increase_lim1 (empid CHAR(6))
    BEGIN
       DECLARE years_of_serv INT DEFAULT 0;
       DECLARE v_incr_rate DEC(9,2) DEFAULT 0.0;
       
       SELECT YEAR(CURRENT DATE) - YEAR(hiredate)
         INTO years_of_serv
         FROM empl1
         WHERE empno = empid;
         
       CASE  
          WHEN  years_of_serv > 30 THEN 
           SET v_incr_rate = 0.08;
          WHEN  years_of_serv > 20 THEN 
           SET v_incr_rate = 0.07; 
          WHEN  years_of_serv > 10 THEN 
           SET v_incr_rate = 0.05;
          ELSE
           SET v_incr_rate = 0.04;
        END CASE;
        
        UPDATE empl1
            SET salary = salary+salary*v_incr_rate
        WHERE empno = empid;
    END
    

    4. 循环

    • LOOP 循环,简单的循环
    CREATE PROCEDURE LEAVE_LOOP (DEPTIN char(3), OUT p_counter INTEGER)
    Ll: BEGIN
       DECLARE v_at_end , v_counter INTEGER DEFAULT 0;
       DECLARE v_lastname VARCHAR(15);
       DECLARE v_birthd, v_hired DATE;
    
       DECLARE c1 CURSOR
         FOR SELECT lastname, hiredate, birthdate FROM employee
             WHERE WORKDEPT = deptin;
    
       DECLARE CONTINUE HANDLER FOR NOT FOUND SET v_at_end = 1;
    
       OPEN c1;
       FETCH_LOOP: LOOP
       FETCH c1 INTO v_lastname, v_hired, v_birthd;
         IF v_at_end <> 0 THEN    -- loop until last row of the cursor
           LEAVE FETCH_LOOP;
          END IF;
         SET v_counter = v_counter + 1;
         INSERT INTO REPORT_INFO_DEPT 
             values(v_lastname, v_hired, v_birthd);     
       END LOOP FETCH_LOOP;
       SET p_counter = v_counter;
      END Ll
    
    • WHILE 循环,进去循环前检查条件是否满足
    CREATE PROCEDURE DEPT_REPT (DEPTIN char(3), OUT p_counter INTEGER)
    Pl: BEGIN
       DECLARE v_at_end , v_counter INTEGER DEFAULT 0;
       DECLARE v_lastname VARCHAR(15);
       DECLARE v_birthd, v_hired DATE;
    
       DECLARE c1 CURSOR
         FOR SELECT lastname, hiredate, birthdate FROM employee
             WHERE WORKDEPT = deptin;
    
       DECLARE CONTINUE HANDLER FOR NOT FOUND SET v_at_end = 1; 
    
       OPEN c1;
       FETCH c1 INTO v_lastname, v_hired, v_birthd;
       WHILE (v_at_end = 0) 
       DO  
         INSERT INTO REPORT_INFO_DEPT
                    values(v_lastname, v_hired, v_birthd); 
         SET v_counter = v_counter + 1;         
         FETCH c1 INTO v_lastname, v_hired, v_birthd;   
       END WHILE;
       SET p_counter = v_counter;
      END P1
    
    • REPEAT 循环,退出前检查条件
    • FOR 循环
    CREATE PROCEDURE DEPT_REPT1 (DEPTIN char(3), OUT p_counter INT)
    P1:BEGIN
       DECLARE v_counter INT DEFAULT 0;
       FOR dept_loop AS
          SELECT lastname, hiredate, birthdate FROM employee
             WHERE WORKDEPT = deptin 
       DO   
         INSERT INTO REPORT_INFO_DEPT values
    (dept_loop.lastname,dept_loop.hiredate,dept_loop.birthdate); 
         SET v_counter = v_counter + 1;     
       END FOR;
       SET p_counter = v_counter;
      END P1
    

    5. 临时表空间

    http://www.51cto.com/specbook/49/3606.htm

    • 要使用临时表,数据库中必须存在一个用户临时表空间(默认情况下没有),创建表空间:
    create user temporary tablespace usertemp1 
        managed by system using ('usertemp1')
    
    • 可使用 DECLARE GLOBAL TEMPORARY TABLE 语句来定义临时表。DB2的临时表是基于会话的,且在会话之间是隔离的。当会话结束时,临时表的数据被删除,临时表被隐式卸下。对临时表的定义不会在 SYSCAT.TABLES 中出现。
    /*
    NOT LOGGED:不记录事务日志
    */
    DECLARE GLOBAL TEMPORARY TABLE gbl_temp
          LIKE empltabl
          ON COMMIT DELETE ROWS
          NOT LOGGED IN usr_tbsp
    
    
    /*
    方法1:
    ON COMMIT DELETE ROWS:在提交的时候清空数据,默认属性
    */ 
    DECLARE GLOBAL TEMPORARY TABLE 表名  
    (   --参数列表
        NAME VARCHAR(10),---姓名  
        DEPT SMALLINT,---部门  
        SALARY DEC(7,2)---工资  
    )  
    ON COMMIT DELETE ROWS;  
    
    
    /*   
    方法2:
    1. ON COMMIT PRESERVE ROWS:一直到会话结束
    2. LIKE 引用表名 INCLUDING COLUMN DEFAULTS WITH REPLACE:字段和类型引用其他表
    */ 
    DECLARE GLOBAL TEMPORARY TABLE 表名  
    LIKE 引用表名 INCLUDING COLUMN DEFAULTS  
    WITH REPLACE  
    ON COMMIT PRESERVE ROWS;  
    
    
    /*
    方法3:
    DEFINITION ONLY WITH REPLACE:程序结束后自动删除该临时表
    */
    DECLARE GLOBAL TEMPORARY TABLE 表名 AS  
    (  
        SELECT * FROM staff WHERE <condition>  
    )  
    DEFINITION ONLY WITH REPLACE; 
    

    此语句创建一个名为 gbl_temp 的用户临时表。定义此用户临时表所使用的列的名称和说明与 empltabl 的列的名称和说明完全相同。隐式定义只包括列名、数据类型、可为空特性和列缺省值属性。未定义所有其他列属性,包括唯一约束、外部关键字约束、触发器和索引。执行 COMMIT 操作时,若未对该表打开 WITH HOLD 游标,则该表中的所有数据都被删除。不记录对用户临时表所作的更改。用户临时表被放在指定的用户临时表空间中。此表空间必须存在,否则此表的声明将失败。

    • 用户定义临时表不支持:
      • LOB 类型的列(或基于 LOB 的单值类型列)
      • 用户定义类型列
      • LONG VARCHAR 列
    • 使用表空间
    CONNECT TO SAMPLE@
    -- Declare a temporary table from CLP 
    --创建表空间
    DECLARE GLOBAL TEMPORARY TABLE temp_employee LIKE employee ON COMMIT PRESERVE ROWS@
    
    -- create a procedure which references the temporary table. 
    CREATE PROCEDURE p1 
    RESULT SETS 1
    
    BEGIN
        --声明游标
        DECLARE cur CURSOR WITH HOLD WITH RETURN TO CLIENT FOR 
            SELECT empno, firstnme, lastname FROM SESSION.temp_employee FETCH FIRST 5 ROWS ONLY;
    
        OPEN cur;
    END
    @
    
    -- Now, from the CLP (an application)
    -- INSERT into the temp table
    INSERT INTO SESSION.temp_employee SELECT * FROM EMPLOYEE@
    
    -- CALL the procedure, which returns an open cursor back to the 
    -- CLP, proving that the temp table data was received.
    CALL p1@
    

    在上面的例子中,创建 CLP 连接后声明了一个临时表。然后创建了引用临时表的过程 p1。这里要注意的关键是,即使在过程体中没有声明临时表,该过程也被成功创建。

    6. 异常处理

    • begin 前声明异常 DECLARE handler-type HANDLER FOR SQLEXCEPTION\SQLWARNING\NOT FOUND ,SQLEXCEPTION\SQLWARNING\NOT FOUND 为异常类型,handler-type处理类型有以下几种:
      • UNDO 在处理器操作执行之前,DB2会回滚存储过程中执行的SQL操作。在处理器操作完成之后,存储过程会终止,并将控制返回给调用者
      • EXIT 在处理器操作完成之后,存储过程会终止,并将控制返回给调用者
      • CONTINUE 在处理器操作完成之后,会继续执行产生这个异常语句之后的下一条语句
    DECLARE CONTINUE HANDLER FOR not_found, SQLEXCEPTION
        SET at_end = 1;
    DECLARE CONTINUE HANDLER FOR trunc BEGIN
        SET truncated = 1;
        SET msg=’message’;
      END;
    
    • 需要注意的问题:
      • DB2存储过程会在SQL执行完成后,将返回信息赋值给SQLCODE和SQLSTATE。这两个DB2内置变量在使用前需要定义。
      • 定义UNDO类型的异常处理,在定义后面可以跟上操作语句。
      • UNDO类型的异常处理,必须是处理原子操作的SQL块,DB2默认不是原子操作的。在BEGIN跟上ATOMIC即可。
    CREATE OR REPLACE PROCEDURE TEST (  
        IN num INTEGER,  
        OUT P_ERRORCODE VARCHAR(256), --SQL返回码  
        OUT P_ERRORDESC VARCHAR(1024)) -- SQL返回信息  
    BEGIN ATOMIC  
      
      DECLARE SQLCODE INTEGER DEFAULT 0;  
      DECLARE SQLSTATE CHAR(5) DEFAULT '00000';  
      -- 定义一个UNDO类型的异常处理  
      DECLARE UNDO HANDLER FOR SQLEXCEPTION set P_ERRORCODE = to_char(sqlcode), P_ERRORDESC = '处理失败';  
        
      update userinfo t set t.usercode = '000000000003' where t.usercode='000000000002';  
      update userinfo t set t.usercode = '00000000000100' where t.usercode='000000000001'; --这里会因usercode字段超长出现异常  
        
      set P_ERRORCODE = to_char(abs(SQLCODE));  
      set P_ERRORDESC = '处理成功';  
        
    END;  
    

    相关文章

      网友评论

        本文标题:DB2存储过程语法

        本文链接:https://www.haomeiwen.com/subject/pvrnixtx.html