zoukankan      html  css  js  c++  java
  • Oracle学习--->7、PL/SQL子程序

    1、PL/SQL可以分为匿名块和命名块:

    • 匿名块的典型特色就是以BEGIN或DECLARE开始,每次执行时都必须重新编译,它们不能被存储到数据库字典中,因此其他的语句块不能像调用普通的函数一样调用匿名块
    • 命名块包含PL/SQL子程序(存储过程或函数)、包和触发器。命名块没有匿名块的这些限制,它们可以存储到数据库中,可以被其他的块调用,不需要再每次执行时都重新编译

    2、子程序简介:一般来说存储过程和函数被称为子程序

    • 存储过程是一段不具有返回值的代码块,而函数会返回一个值
    • 子程序与匿名块最大的不同是它可以存储到数据库字典中,以便重用
    • 由于子程序属于命名块,因此在定义时需要指定一个名字,子程序其他的部分与匿名块非常相似
    创建存储过程☆★☆★☆★☆★☆★☆★☆★☆★☆★☆★☆★☆★☆★☆★☆★
    CREATE OR REPLACE PROCEDURE newemployeer (
       p_departId     EMPLOYEER.DEPARTMENT_ID%TYPE,
       p_empId        EMPLOYEER.EMPLOYEER_ID%TYPE,
       p_empName      EMPLOYEER.EMPLOYEER_NAME%TYPE,
       p_empSalary    EMPLOYEER.EMPLOYEER_SALARY%TYPE)
    AS
       v_empId   NUMBER;
    BEGIN
       SELECT COUNT (*) INTO v_empId FROM EMPLOYEER WHERE employeer_id = p_empId;
       IF v_empId > 0
       THEN
          raise_application_error (-20002, '该员工工号已经存在');
       END IF;
       INSERT INTO employeer VALUES (p_departId, p_empId, p_empName, p_empSalary, SYSDATE);
       COMMIT;
    END newemployeer;
    
    调用存储过程☆★☆★☆★☆★☆★☆★☆★☆★☆★☆★☆★☆★☆★☆★☆★
    BEGIN
       newemployeer ('10', '800021', '小蛮', 9200);
    EXCEPTION
       WHEN OTHERS
       THEN
          DBMS_OUTPUT.put_line ('产生错误' || SQLERRM);
    END;

    3、创建存储过程

    • CREATE OR REPLACE Procedure_name
    • 使用CREATE在数据库字典中创建一个独立的过程,OR REPLACE表示创建时将替换现有的过程定义,通常使用OR REPLACE以便在过程创建之后进行修改,可以直接替换掉原有的过程
    • PROCEDURE表示将要创建一个过程,一般在包中定义过程时会省略掉CREATE OR REPLACE子句
    • Procedure_name是存储过程的名字,名字不能重复
    • 存储过程参数名称统一以p开头,这只是一种约定,并不是原则
    • IN关键字用来指明参数为输入参数,参数的类型并没有指定长度
    • AS之后的语句是局部定义区,在示例中定义一个变量和异常。类似于匿名块的DECLARE
    CREATE OR REPLACE PROCEDURE newemployeer (p_departId    IN VARCHAR2,
                                              p_empId       IN VARCHAR2,
                                              p_empName     IN VARCHAR2,
                                              p_empSalary   IN NUMBER)
    AS
       v_empId             NUMBER;
       e_duplication_emp   EXCEPTION;
    BEGIN
       SELECT COUNT (*) INTO v_empId FROM EMPLOYEER WHERE employeer_id = p_empId;
       IF v_empId > 0
       THEN
          RAISE e_duplication_emp;
       END IF;
       INSERT INTO employeer VALUES (p_departId, p_empId, p_empName, p_empSalary, SYSDATE);
       COMMIT;
    EXCEPTION
       WHEN e_duplication_emp
       THEN
          ROLLBACK;
          raise_application_error (-20002, '该员工工号已经存在');
    END newemployeer;

    4、创建函数

    • 创建函数和存储过程 非常类似,区别在于函数会具有返回值,二存储过程仅仅是为了执行一系列的行为。
    • 调用时,函数可以作为表达式的一部分进行调用,而存储过程只能作为PL/SQL语句进行调用
    • CREATE OR REPLACE FUNCTION function_name
    • 创建函数关键字使用FUNCTION
    • RETURN返回指定类型
    创建函数
    CREATE
    OR REPLACE FUNCTION getraisesalary ( p_empId EMPLOYEER.EMPLOYEER_ID%TYPE) RETURN NUMBER IS v_sal EMPLOYEER.EMPLOYEER_SALARY%TYPE; v_salaryration NUMBER (10, 2); BEGIN SELECT employeer_salary INTO v_sal FROM employeer WHERE employeer_id = p_empId; v_salaryration := 1.25; RETURN ROUND (v_sal * v_salaryration, 2); EXCEPTION WHEN NO_DATA_FOUND THEN RETURN 0; END getraisesalary;
    调用函数
    DECLARE v_salaryration NUMBER (10, 2); BEGIN v_salaryration := getraisesalary ('800001'); DBMS_OUTPUT.put_line ('800001员工调薪后为:' || v_salaryration); EXCEPTION WHEN OTHERS THEN DBMS_OUTPUT.put_line ('发生错误' || SQLERRM); END;

    5、RETURN

    • 存储过程也可以使用RETURN,但是这个是作为退出存储过程的执行
    • 函数的RETURN是返回值得

    6、查看和删除子程序(存储过程和函数)

    • 查看子程序
    SELECT object_name,created,last_ddl_time,status FROM user_objects WHERE object_type IN ('FUNCTION', 'PROCEDURE')
    •   删除子程序
    DROP PROCEDURE procedure_name;
    
    DROP FUNCTION function_name;

     7、子程序参数

      7.1、形参和实参

    •   形式参数:在定义子程序时,在定义语句中定义的参数称为形式参数,简称形参
    •   实际参数:在调用子程序时,传入的具体参数值称为实际参数,简称实参
    CREATE OR REPLACE PROCEDURE newemployeer (
       p_departId     EMPLOYEER.DEPARTMENT_ID%TYPE,   --定义形式参数
       p_empId        EMPLOYEER.EMPLOYEER_ID%TYPE,
       p_empName      EMPLOYEER.EMPLOYEER_NAME%TYPE,
       p_empSalary    EMPLOYEER.EMPLOYEER_SALARY%TYPE)
    
    DECLARE
       v_salaryration   varchar2 (10);
    BEGIN
       v_salaryration := get_week ('20160101000000');    --实际参数
       DBMS_OUTPUT.put_line ('年周:' || v_salaryration);
    EXCEPTION
       WHEN OTHERS
       THEN
          DBMS_OUTPUT.put_line ('发生错误' || SQLERRM);
    END;

       7.2、参数模式(3种类型模式)

      分为3种模式:IN、OUT和IN OUT,如果没有指定形式参数的模式,默认为IN

    • IN模式:称为输入参数,这是默认的参数模式
    CREATE OR REPLACE PROCEDURE newemployeer (
       p_departId     IN NUMBER :=25,   --定义形式参数,并且赋初值
       p_empId        IN NUMBER,        --定义形式参数
       p_empName      IN VARCHAR2 := 'ABC',
       p_empSalary    IN VARCHAR2)
    •  OUT:称为输出参数,输出参数将会改变参数的值,因此实际参数不能用文字或者常量来表示
    创建有返回值的存储过程
    CREATE
    OR REPLACE PROCEDURE getSalary ( p_empId EMPLOYEER.EMPLOYEER_ID%TYPE, p_raiseSalary OUT NUMBER) --定义一个输出变量 IS v_sal EMPLOYEER.EMPLOYEER_SALARY%TYPE; v_salaryration NUMBER (10, 2); BEGIN SELECT employeer_salary INTO v_sal FROM employeer WHERE employeer_id = p_empId; v_salaryration := 1.25; p_raiseSalary := v_sal * v_salaryration; EXCEPTION WHEN NO_DATA_FOUND THEN DBMS_OUTPUT.PUT_LINE ('没有找到数据'); END getSalary; 调用有返回值的存储过程 DECLARE v_salaryration NUMBER (10, 2); --定义一个变量保存输出值 BEGIN getSalary ('800002', v_salaryration); --调用函数,传入实际参数,并且获取返回值 DBMS_OUTPUT.put_line ('变更后的工资' || v_salaryration); EXCEPTION WHEN OTHERS THEN DBMS_OUTPUT.put_line ('发生错误' || SQLERRM); END;
    •  IN OUT模式:IN OUT模式是IN和OUT方式的组合,又称输入/输出参数。当过程被调用时,参数既作为传入的参数,后经过赋值后返回又可以作为返回参数
    创建存储过程
    CREATE
    OR REPLACE PROCEDURE calcSalary (p_job IN VARCHAR2, p_salary IN OUT NUMBER) --定义输入/输出参数,p_salary既作为输入参数,又作为输出参数 IS v_sal NUMBER (10, 2); BEGIN IF p_job = '员工' THEN v_sal := p_salary * 1.15; ELSIF p_job = '职员' THEN v_sal := p_salary * 1.25; ELSIF p_job = '高职' THEN v_sal := p_salary * 1.35; END IF; p_salary := v_sal; END calcSalary;
    调用存储过程
    DECLARE v_sal NUMBER (10, 2) := 8000; --这里作为输入参数 BEGIN calcSalary ('高职', v_sal); DBMS_OUTPUT.put_line ('调整后的工资为:' || v_sal); --这里了作为输出参数 EXCEPTION WHEN OTHERS THEN DBMS_OUTPUT.put_line ('发生错误' || SQLERRM); END;

       7.3、形式参数的约束

    • 在创建存储过程定义时,形式参数不能指定长度、精度约束,否则会报错
    • 虽然形式参数不能使用约束声明,但是可以使用%TYPE对其进行约束
    • 参数的约束是产生在形式参数中的
    CREATE OR REPLACE PROCEDURE newemployeer (p_empId       IN VARCHAR2 (10),
                                              p_empSalary   IN NUMBER (10, 2))  --形参指定长度或者精度是错误的

    CREATE OR REPLACE PROCEDURE newemployeer (p_empId       IN EMPLOYEER.EMPLOYEER_ID%TYPE,
                                              p_empSalary   IN EMPLOYEER.EMPOOYEER_SALARY%TYPE)  --形参指定长度方法
    DECLARE v_sal NUMBER (10, 2) := 8000; --实参才指定长度或者精度 BEGIN
      NULL
    END;

       7.4、参数传递方式

    • 调用子程序时,可以有两种向子程序传递参数的方式:一种是按位置传递,另一种是按名称传递
    • 使用按名称传递方法,使用=>作为关联的操作符,把左边的实参和右边的形参关联起来
    DECLARE
       v_sal   NUMBER (10, 2) := 8000;
       v_job   VARCHAR2 (10) := '高职';
    BEGIN
       calcSalary (p_salary =>v_sal ,p_job  =>v_job );  -- =>左边为存储过程的形参,=>右边为存储过程的实参
       DBMS_OUTPUT.put_line ('调整后的工资为:' || v_sal);
    EXCEPTION
       WHEN OTHERS
       THEN
          DBMS_OUTPUT.put_line ('发生错误' || SQLERRM);
    END;

       7.5、参数默认值

    • 在定义子程序过程中,可以使用DEFAULT关键字或赋值语句为IN模式参数指定默认值
    CREATE OR REPLACE PROCEDURE newemployeer (
       p_departId     EMPLOYEER.DEPARTMENT_ID%TYPE DEFAULT '10',
       p_empId        EMPLOYEER.EMPLOYEER_ID%TYPE := '800001',
       p_empSalary    EMPLOYEER.EMPLOYEER_SALARY%TYPE DEFAULT 9200)

      7.6、使用NOCOPY编译提示

    • 使用NOCOPY编译提示前,首先理解引用传递和值传递之间的区别
    • 值传递:当参数通过值传递时,参数将从实际参数中被复制到形式参数中
    • 引用传递:实际参数的指针被传递到了相应的形式参数中
    • 在使用OUT和IN OUT模式的参数时,如果参数是大型数据结构,比如集合、记录和对象实例,进行全部复制会大大降低执行速度,消耗大量内存,为了防止这种现象可以使用NOCOPY
    DECLARE
       TYPE emptabtyp IS TABLE OF employeer%ROWTYPE;
    
       emp_tab   emptabtyp := emptabtyp (NULL);
       t1        NUMBER (10);
       t2        NUMBER (10);
       t3        NUMBER (10);
    
       PROCEDURE get_time (t OUT NUMBER)
       IS
       BEGIN
          SELECT TO_CHAR (SYSDATE, 'SSSSS') INTO t FROM DUAL;
       END;
    
       PROCEDURE do_nothing1 (tab IN OUT emptabtyp)
       IS
       BEGIN
          NULL;
       END;
    
       PROCEDURE do_nothing2 (tab IN OUT NOCOPY emptabtyp)
       IS
       BEGIN
          NULL;
       END;
    BEGIN
       SELECT *
         INTO emp_tab (1)
         FROM employeer
        WHERE employeer_id = '800001';
    
       emp_tab.EXTEND (9000000, 1);
       get_time (t1);
       do_nothing1 (emp_tab);
       get_time (t2);
       do_nothing2 (emp_tab);
       get_time (t3);
       DBMS_OUTPUT.put_line ('调用所花费的时间(秒)');
       DBMS_OUTPUT.put_line ('-------------------------');
       DBMS_OUTPUT.put_line ('t1时间为:' || t1);
       DBMS_OUTPUT.put_line ('t2时间为:' || t2);
       DBMS_OUTPUT.put_line ('t3时间为:' || t3);
       DBMS_OUTPUT.put_line ('不带NOCOPY的调用:' || TO_CHAR (t2 - t1));
       DBMS_OUTPUT.put_line ('带NOCOPY的调用:' || TO_CHAR (t3 - t2));
       DBMS_OUTPUT.put_line ('                                  ');
    END;
  • 相关阅读:
    Django实战—权限管理系统rbac组件实现
    Django模型层的DateTimeField、DateField字段设置时间格式为显示当前年月日时分秒的时间格式及时区
    mysql数据库删除一条数据之后,主键id不连续的问题解决
    python多继承(super().__init__())、*args和**kwargs、
    Django数据库操作中You are trying to add a non-nullable field 'name' to contact without a default错误处理
    配置等模版
    【SSM】(一)SSM整合-增删改查书籍
    【SpringMVC】(八)使用Ajax前后端传数据&不使用Ajax
    【SpringMVC】@RequestMapping注意点
    leetcode (堆->中级) 264,313,347,373,378,767,1642,973,1673,743,787
  • 原文地址:https://www.cnblogs.com/LiGengMing/p/6092400.html
Copyright © 2011-2022 走看看