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
NULLEND;
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;