Oracle SQL 学习笔记21 - Procedure 和 Function

Oracle数据库支持创建和使用子程序。创建子程序有很多优点:

  1. 便于维护
  2. 提高数据处理的安全性和一致性
  3. 提高数据处理性能
  4. 增强代码的清晰透明性
    Oracle支持的子程序有两类:
  • 存储过程 (procedure)
  • 函数(function)

在PL/SQL中创建和使用procedure

定义

存储过程是一种执行动作的子程序,可以保存为一种模式对象。创建存储过程提升了代码的可充用和可维护性。

语法

CREATE  [OR  REPLACE]  PROCEDURE  procedure_name
[(parameter1  [mode]  datatype1,
 parameter2  [mode]  datatype2,  ...)]
IS|AS
  [local1_variable_declarations;...]
BEGIN
  -- actions;
END  [procedure_name];

开发Procedure

pro_dev.JPG

参数

参数定义于子程序的头部,在调用者和子程序之间传递数据。有三种类型:

  1. IN parameter:默认类型,表示传入参数
  2. OUT parameter:传出参数
  3. IN OUT:传入传出参数
CREATE  PROCEDURE  procedure(param  [mode]  datatype)
Formal 和 Actual 参数
  • Formal parameter
    在子程序定义部分声明的一种本地变量
CREATE  PROCEDURE  raise_sal(id  NUMBER,  sal  NUMBER)  IS
BEGIN  ...
END  raise_sal;
  • Actual parameters
    可以是具体的值也可以是主程序的变量
emp_id  :=  100;
raise_sal(emp_id,  2000)
使用IN Parameter 举例
CREATE  OR  REPLACE  PROCEDURE  raise_salary  (id  IN  employees.employee_id%TYPE,  percent  IN  NUMBER)
IS
BEGIN
  UPDATE  employees
  SET  salary  =  salary  *  (1  +  percent/100)
  WHERE  employee_id  =  id;
END  raise_salary;
/

EXECUTE  raise_salary(176,  10)
使用OUT Parameter 举例
CREATE  OR  REPLACE  PROCEDURE  query_emp
(id  IN  employees.employee_id%TYPE,
 name OUT  employees.last_name%TYPE,
 salary  OUT  employees.salary%TYPE)
BEGIN
  SELECT  last_name,  salary  INTO  name,  salary
  FROM  employees
  WHERE  employee_id  =  id;
END  query_emp;
OUT Parameters 的值

可以用 DBMS_OUTPUT.PUT_LINE 打印变量。

SET  SERVEROUTPUT  ON
DECLARE
  emp_name  employees.last_name%TYPE;
  emp_sal  employees.salary%TYPE;
BEGIN
  query_emp(171,  emp_name,  emp_sal);
  DBMS_OUTPUT.PUT_LINE('Name:  '  ||  emp_name);
  DBMS_OUTPUT.PUT_LINE('Salary:  '  ||  emp_sal);
END;

也可以用SQL * PLUS host variables 打印变量

VARIABLE  name  VARCHAR2(25)
VARIABLE  sal  NUMBER
EXECUTE  query_emp(171,  :name,  :sal)
PRINT  name  sal
使用 Host Variables

Host Variable 又称“bind”或者“global”变量。定义于PL/SQL之外,可以通过下列方式定义:

  • SQL * Plus VARIABLE 命令
  • Oracle Forms internal 和 UI variables
  • Java variables

引用host variables需要在变量名称前加上“:”。Host Variable 可以在匿名块引用但是不能在过程内部引用,也可以作为参数传入传出过程。

使用IN OUT Parameter 举例

调用环境:

phone_no 
# 在调用之前为‘8006330575’
# 在调用之前为‘(800)633-0575’
CREATE  OR  REPLACE  PROCEDURE  format_phone
  (phone_no  IN  OUT  VARCHAR2)  IS
BEGIN
  phone_no  :=  '('  ||  SUBSTR(phone_no,  1,  3)  ||
                ')'  ||  SUBSTR(phone_no,  4,  4)  ||
                '-'  ||  SUBSTR(phone_no,  7);
END  format_phone;
/

传递参数的方式

  • 位置传参(Positional)
    将 Actual 参数按照 Formal 参数的顺序列出。
  • 名称参数(Named)
    Actual 参数可以以任意顺序列出,用 association 操作符(=>)将 Formal 参数和相关的 Actual 参数联系起来。
  • 复合模式(Combination)
    一些参数以位置方式传入,其他的以名称方式传入。
传参举例
CREATE  OR  REPLACE  PROCEDURE  add_dept(
  name  IN  departments.department_name%TYPE,
  loc  IN  departments.location_id%TYPE)  IS
BEGIN
  INSERT  INTO  departments(department_id,  departmemt_name,  location_id)
  VALUES  (departments_seq.NEXTVAL,  name,  loc);
END  add_dept;
/

EXECUTE  add_dept('TRAINING',  2500)
EXECUTE  add_dept(loc=>2400,  name=>'EDUCATION')
传参时使用 DEFAULT 选项
CREATE  OR  REPLACE  PROCEDURE  add_dept(
  name  IN  departments.department_name%TYPE  :=  'Unknown',
  loc  IN  departments.location_id%TYPE  DEFAULT  1700)  
IS
BEGIN
  INSERT  INTO  departments(department_id,  departmemt_name,  location_id)
  VALUES  (departments_seq.NEXTVAL,  name,  loc);
END  add_dept;
/

有了default,传参方式更加灵活

EXECUTE  add_dept
EXECUTE  add_dept('ADVERTISING',  loc  =>  1200)
EXECUTE  add_dept(loc  =>  1200)

传参模式总结

IN OUT IN OUT
Default 模式 必须指定 必须指定
值被传入子程序 返回值到调用子程序的环境 被传入子程序,返回值到调用子程序的环境
Formal 参数作为一个常量(constant) 没有被初始化的Variable 初始化的Variable
Actual 参数可以是一个literal,表达式,常量,或者被初始化的Variable 必须是一个Variable 必须是一个Variable
可以被赋Default值 不能被赋Default值 不能被赋Default值

调用Procedures

可以在匿名块中调用存储过程,也可以在其他存储过程中调用存储过程。

CREATE  OR  REPLACE  PROCEDURE  process_employees
IS
  CURSOR  emp_cursor  IS
    SELECT  employee_id
    FROM  employees;
BEGIN
  FOR  emp_rec  IN  emp_cursor
  LOOP
    raise_salary(emp_rec.employee_id,  10);
  END  LOOP;
  COMMIT;
END  process_employees;
/

异常处理

EXP.JPG
  • 异常处理举例
CREATE  PROCEDURE  add_department(
  name  VARCHAR2,  mgr  NUMBER,  loc  NUMBER)  IS
BEGIN
  INSERT  INTO  DEPARTMENTS  (department_id,  department_name,  manager_id,  location_id)
  VALUES  (DEPARTMENTS_SEQ.NEXTVAL,  name,  mgr,  loc);
  DBMS_OUTPUT.PUT_LINE('Added  Dept:  '||  name);
EXCEPTION
  WHEN  OTHERS  THEN
    DBMS_OUTPUT.PUT_LINE('Err:  adding  dept:  '||  name);
END;
/
CREATE  PROCEDURE  create_departments  IS
BEGIN
  add_department('Media',  100,  1800); # ok
  add_department('Editing',  99,  1800);  # error
  add_department('Advertising',  101,  1800); #ok
END;

删除Procedures

DROP  PROCEDURE  procedure_name;

通过数据字典查看 procedure

可以通过两种方式在字典中查看存储过程:

  • 通过USER_SOURCE看代码
SELECT  text
FROM  user_source
WHERE  name  =  'ADD_DEPARTMENT' and type  =  'PROCEDURE'
ORDER  BY  line;
  • 通过USER_OBJECTS看名称和创建时间
SELECT  object_name
FROM  user_objects
WHERE  object_type  =  'PROCEDURE';

在PL/SQL中创建和使用function

创建Functions的语法

创建Functions必须要有RETURN statement:

CREATE  [OR  REPLACE]  FUNCTION  function_name
  [(parameter1  [mode1]  datetype1,  ...)]
RETURN  datatype  IS|AS
  [local_variable_declarations;  ...]
BEGIN
  --  actions;
  RETURN  expression;
END  [function_name];

Stored Function 举例

  • 创建函数
CREATE  OR  REPLACE  FUNCTION  get_sal(
  id employees.employee_id%TYPE)  
RETURN  NUMBER  IS
  sal  employees.salary%TYPE  :=  0;
BEGIN
  SELECT  sal
  INTO  sal
  FROM  employees
  WHERE  employee_id  =  id;
  RETURN  sal;
END  get_sal;
/
EXECUTE  dbms_output.put_line(get_sal(100))

函数的执行方法

作为PL/SQL 表达式的一部分

  • 用 host variable 来获得结果
VARIABLE  salary  NUMBER
EXECUTE  :salary :=  get_sal(100)
  • 使用local variable 来获得结果
DECLARE  sal  employees.salary%TYPE;
BEGIN
  sal  :=  get_sal(100);...
END;

作为参数传入另一个子程序

EXECUTE  dbms_output.put_line(get_sal(100))

在SQL语句中使用(有一些限制)

SELECT  job_id,  get_sal(employee_id)  FROM  employees;

在SQL中使用用户自定义函数的好处

在SQL中使用用户自定义函数有很多好处:

  1. 当活动过于复杂的时候,能扩展SQL
  2. 较之于在应用中filter数据,用自定义函数在WHERE子句filter数据更有效率
  3. 能操作数据的值

在SQL中使用函数举例

CREATE  OR  REPLACE  FUNCTION  tax(value  IN  NUMBER)
RETURN  NUMBER  IS
BEGIN
  RETRUN  (value  *  0.08);
END  tax;
/
SELECT  employee_id,  last_name,  salary,  tax(salary)
FROM  employees
WHERE  department_id  =  100;

哪些子句中可以调用函数

用户自定义函数和Oracle预定义函数可用于:

  1. 查询中SELECT的列表或者SELECT子句
  2. WHERE子句和HAVING子句中的条件表达式
  3. 查询中的子句:CONNECT BY,START WITH,ORDER BY和GROUP BY
  4. INSERT语句中的VALUES子句
  5. UPDATE语句的SET子句

通过SQL调用函数的限制

通过SQL表达式调用函数的限制:

  • 函数必须创建函数对象
  • 仅可接受IN参数而且只能是SQL data types,而非

PL/SQL-specific类型

  • 返回SQL数据类型,而非PL/SQL-specific类型
    通过SQL语句中调用函数的限制
  • 必须以位置模式传递参数
  • 调用者或是函数属主或有执行权限
    函数如果通过
  • SELECT 或并行UPDATE,DELETE语句调用,则函数不能包含DML
  • 针对表T的UPDATE或DELETE语句调用,则函数不能包括任何针对表T的任何SELECT和DML
  • SQL语句调用,则函数不可结束事务(that is, cannot execute COMMIT or ROLLBACK operations),不可定义或使用savepoint,不可alter system或session。

调用函数限制举例

CREATE  OR  REPLACE  FUNCTION  dml_call_sql(sal  NUMBER)
RETURN  NUMBER  IS
BEGIN
  INSERT  INTO  employees(employee_id,  last_name,  email,  hire_date,  job_id,  salary)
  VALUES(1,  'Frost',  '[email protected]',  SYSDATE,  'SA_MAN',  sal);
  RETRUN  (sal  +  100)
END;

以下语句会抛出异常:

UPDATE  employees
  SET  salary  =  dml_call_sql(2000)
WHERE  employee_id  =  170;
# ORA-04091...
# ORA-06512...

删除Functions

删除一个已经保存的函数

DROP  FUNCTION  function_name
  • 删除函数之后,所有赋予函数的权限将被收回。
  • CREATE OR REPLAE 等同于删除函数,然后重建该函数,函数上的权限不变。

通过数据字典查询函数信息

  • 通过USER_SOURCE可查看函数代码
SELECT  text
FROM  user_source
WHERE  type  =  'FUNCTION'
ORDER  BY  line;
  • 通过USER_OBJECT可查询名称等信息
SELECT  object_name
FROM  user_objects
WHERE  object_type  =  'FUNCTION';

存储过程和函数的对比

存储过程 函数
作为PL/SQL语句执行 作为一个表达式执行
在头部不能包含RETURN语句 在头部必须包含RETURN语句
可以通过output参数返回值 必须返回一个single值
可以有一个不包括值的返回语句 必须至少包含一个RETURN语句

你可能感兴趣的:(Oracle SQL 学习笔记21 - Procedure 和 Function)