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;

 

posted on 2016-11-23 09:12  LiGengMing  阅读(316)  评论(0编辑  收藏  举报

导航