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;