代码模块化, 即将一大块代码拆成若干小块(过程), 然后就可以在其他模块调用这些模块了, 这样, 重用性更好, 也方便管理.
过程: 过程是一个可以像执行 PL/SQL 语句一样调用的程序, 一个过程可以执行一个或多个动作. 我们可以通过参数列表向过程传递或者从过程传出信息.
函数: 函数是一个通过RETURN 语句返回数据的程序, 使用起来就像是一个 PL/SQL 表达式. 我们可以通过参数列表传入参数, 也可以通过参数列表传出参数, 不过通常情况下这么做并不好.
数据库触发器: 触发器是当数据库中发生了某些事件时, 所触发执行的一系列命令
包: 一个由若干函数, 过程, 类型以及变量一起组成的并被命名的集合.
过程
PROCEDURE [schema.]name[(parameter, parameter...)]
[AUTHID DEFINE | CURRENT_USER] -- 运行时, 是用定义者权限运行, 还是用当前用户权限运行(前一种叫定义者权限模式, 后一种叫调用者权限模式)
IS
[declare]
BEGIN
executable statement;
EXCEPTION
exception handlers
END [name];
create or replace procedure apply_discount(
company_id_in in company.company_id%type,
discount_in in number)
is
min_discount constant number := .05;
max_discount constant number := .25;
invalid_discount exception;
begin
if discount_in between min_discount and max_discount then
update item
set item_amount := item_amount * (1-discount_in);
where EXISTS(select 'x' from order where order.order_id = item.order_id
and order.company_id = company_id_in);
if sql%rowcount = 0 then
raise no_data_found;
end if;
else
raise invalid_discount;
end if;
exception
when invalid_discount then
dbms_output.put_line('The specified discount is invalid.'); when no_data_found then
dbms_ouput.put_line('No orders in the system for company:' ||
to_char(company_id_in));
end apply_discount;
/
show errors;
-- 调用一个 procedure
一个过程是作为一个可执行的PL/SQL语句调用的, 换句话说, 过程调用必须以分号; 结尾, 可以在 pl/SQL 代码块的可执行单元中的其他SQL或者PLSQL语句(如果有的话)之前后调用
BEGIN
apply_discount(new_company_id, 0.15); -- 在begin 与end 之间不用 exec 就可以执行
END;
函数
-- 调用函数
DECLARE
v_nickname VARCHAR2(100) := favorite_nickname('Steven');
-- 也可以在查询, 或者表达式语句中直接调用函数
-- 如果参数的类型是 out 的, 唯一的好处就是这个函数可以返回多个值通过这些out类型的参数, 而
-- return 语句只能返回一个值
-- 提示, out 或 in out 模式应该只出现在 procedure 过程中, 函数所要返回的全部信息都应该通过 RETURN 子句返回
参数调用模式
in : 只读的, 在模块内部可以使用实参的值, 但是不能修改. 如果你没有指定参数模式, 默认就是 IN 模式
out: 只写的, 模块内部可以给参数赋值, 但是参数值不能被使用
in out: 可读写, 模块内既可以使用(读), 也可以修改(写)参数的值
只能放在赋值语句的右边
只能放在赋值语句的左边
所以, out 类型必须要传实参进去, 而且还的是个变量, 设想一下, 如果不传实参, 那么这个plsql执行完了以后, out模式的返回值返回给谁?
可以放在赋值语句的任何一边
提示: OUT 或者 IN OUT 模式的形参只应该出现在过程的定义中(而非函数), 函数所要返回的全部信息只应该通过RETURN子句来返回, 而不应该通过这两种参数. 如果能够遵守这个指导原则进行设计, 你的子程序就很容易被理解和使用了, 另外, 不能再一个SQL语句中调用带有OUT或者INOUT模式参数的函数.
命名表示法是有好处的:
1. 命名表示法是自我说明的
2. 为我们提供了有关参数规范的完整灵活性( 比如有些过程是有缺省函数的, 而你传参是又不想全部传进去, 比如有10个参数, 你只想传3个, 剩下的就用缺省参数值, 那么这种表示法, 你只要写3个参数就可以了, 这是位置表示法没法实现的)
考虑 out, 和 In out 的运行机理, 会在模块内部做一个参数的变量的拷贝, 知道过程运行成功后, 才将这个拷贝赋值给OUT变量, NOCOPY 顾名思义, 不做这个 拷贝.
缺省值 ( in 类型参数)
参数的缺省值和声明变量时指定缺省值一样, 例如:
create or replace procedure astrology_reading(
sign_in IN varchar2 := 'LIBRA',
born_at_in IN DATE DEFAULT SYSDATE)
IS
上边例子中的两个参数都有缺省值.
局部或者潜逃模块
局部模块或者嵌套模块 是 一个在PL/SQL块(不管是匿名还是命名的)的声明部分定义的过程或者函数. 之所以把这种模块叫做局部的, 是因为这种模块只在父PL/SQL块中定义, 它不能被它所在的包围块之外的块调用.
创建局部过程或者函数的语法和创建单独模块的语法完全一样, 比如:
declare
procedure show_date(date_in IN DATE) -- 内部模块
is
begin
dbms_output.put_line(to_char(date_in, 'Month dd, yyyy'));
end show_date;
begin
-- ...
end;
提示: 重载的另一个名字是静态多态. 术语多态是指语言能够定义并且选择性的使用多个具有相同名字的程序中的一个的能力, 如果该使用哪一个程序的决定是在编译时刻就确定下来的, 就叫静态多态, 如果决定是在运行时刻才确定下来的, 就叫动态多态(java), 动态多态是通过对象类型的继承得到的.
看来还是参数个数不同, 或者参数类型不同(整个家族都不同), 重载比较靠谱.
如果一个模块A调用模块B, 而同时模块B又调用模块A, 这种相互递归, 声明就会有问题, 这是就可以使用前置声明
因为第一个函数要调用第二个函数, 所以需要声明, 否则它就无法认识第2个函数.
所谓前置声明, 只是将两个互相递归调用的函数之一比如A, 把声明部分拿到最前边先声明, 然后是函数B的定义(引用了A), 然后再是A的定义(引用了B)