[ORACLE数据库] ORACLE 的 PL/SQL 块
对于刚接触 ORACLE 数据库的初学者来说,PL/SQL 可能是一个既陌生又重要的概念。它不仅是数据库操作的 “进阶工具”,更是实现复杂业务逻辑的核心。
一、什么是 PL/SQL?
PL/SQL(Procedural Language/SQL)是 ORACLE 推出的过程化编程语言,属于第三代语言。它最大的特点是将 SQL 的数据操纵能力与过程化编程结合—— 既可以直接使用 SQL 的查询、插入、更新等操作,又能像 C、Java 一样通过条件判断、循环等逻辑处理复杂业务,因此成为数据库事务处理的强大工具。
二、PL/SQL 块的分类:你需要知道的 5 种类型
PL/SQL 的代码以 “块” 为基本单位,根据功能和使用场景,可分为三大类共 5 种类型,初学者需重点区分它们的特点和用途:
1️⃣基础块:无名块与命名块
- 无名块(匿名块):动态编写,只能执行一次,可调用其他程序但不能被其他程序调用。适合临时执行的简单逻辑(如一次性数据处理)。
- 命名块:给无名块加上标签(名称),本质仍是匿名块的变种,主要用于代码标识和嵌套场景。
2️⃣可复用的子程序
存储在数据库中,编译一次后可反复调用,是实现代码复用的核心:
- 存储过程:一组预定义的 SQL 语句集合,用于完成特定功能(如批量更新数据)。
- 函数:与存储过程类似,但必须返回一个值,可作为表达式或参数使用(如计算某列的平均值)。
3️⃣自动触发的触发器
无需手动调用,由特定事件(如数据修改、系统启动)触发自动执行:
- DML 触发器:在表或视图执行 INSERT/UPDATE/DELETE 时触发(如数据删除前自动备份)。
- 视图替代触发器:解决多表联合视图无法直接修改的问题,替代用户操作执行自定义逻辑。
- 系统触发器:响应数据库系统事件(如用户登录 / 退出、系统启动)。
4️⃣模块化管理的程序包
类似 Java 的 “包”,用于集中管理存储过程、函数、变量等,实现代码的模块化和封装,方便调用和维护。
三、PL/SQL 的 “规矩”:标识符与变量命名
写 PL/SQL 代码前,必须先掌握标识符(变量、常量等的名称)的规则,否则会频繁报错:
- 基础限制:长度≤30 字符;首字符必须是字母;不区分大小写;不能用减号 “-”;不能是 SQL 保留字(如 SELECT、INSERT)。
- 建议命名规范(提升代码可读性):
标识符类型 | 命名规则 | 示例 |
---|---|---|
程序变量 | v_前缀 + 名称 | v_ename(表示 “员工姓名变量”) |
程序常量 | c_前缀 + 名称 | c_company_name(表示 “公司名称常量”) |
游标变量 | Cursor + 名称 | Cursor_Emp(表示 “员工游标”) |
异常标识 | e_前缀 + 名称 | e_too_many(表示 “数量过多异常”) |
表类型 | 名称 + _table_type | Emp_record_type(表示 “员工记录类型”) |
四、入门实战:从无名块开始写代码
无名块是 PL/SQL 最基础的形式,结构简单,适合新手练手。它的固定结构为:
plsql
DECLARE -- 定义变量(可选)变量声明;
BEGIN -- 执行逻辑(必选)SQL语句或PL/SQL逻辑;
END; -- 结束(必选)
/ -- 执行该块(在SQL*Plus中)
示例 1:插入数据并显示结果
plsql
-- 打开输出显示(否则看不到结果)
set serveroutput on;
DECLARErow_id ROWID; -- 存储行标识info VARCHAR2(40); -- 存储提示信息
BEGIN-- 插入数据,并将插入的行标识和信息存入变量INSERT INTO scott.dept VALUES (90, '财务室', '海口') RETURNING rowid, dname||':'||to_char(deptno)||':'||loc INTO row_id, info;-- 输出结果(类似Java的System.out.println)DBMS_OUTPUT.PUT_LINE('ROWID:'||row_id);DBMS_OUTPUT.PUT_LINE(info);
END;
/
关键说明:
RETURNING
:用于获取 INSERT/UPDATE/DELETE 操作影响的数据(如行标识、列值)。DBMS_OUTPUT.PUT_LINE
:打印结果到控制台,需先执行set serveroutput on
开启。
五、进阶工具:存储过程与函数
当一段逻辑需要反复执行(如每月工资计算),就该用存储过程或函数了 —— 它们存储在数据库中,调用时无需重新编译,效率更高。
1️⃣存储过程:无返回值的 “代码块”
(1)创建存储过程
- 无参数示例(修改部门名称):
plsql
-- create or replace:若存在则覆盖
create or replace procedure update_my_dept
is
beginUpdate dept set dname = '销售部' where deptno=40;commit; -- 提交事务
end;
- 有参数示例(新增部门):
plsql
create or replace procedure add_my_dept(V_deptno number, -- 部门编号参数V_dname varchar2, -- 部门名称参数V_loc varchar2 -- 部门位置参数
)
is
begininsert into my_dept values(V_deptno, V_dname, V_loc);commit;
end;
(2)调用存储过程
plsql
-- 方法1:用exec调用
exec add_my_dept(14, '研发部', '北京');-- 方法2:用call调用
call add_my_dept(17, '市场部', '上海');
2️⃣函数:有返回值的 “计算工具”
函数与存储过程的核心区别是必须返回一个值,常用来做计算或数据转换。
(1)创建函数
plsql
create or replace function hello(str in varchar2)
return varchar2 -- 声明返回值类型
isret varchar2(20); -- 定义局部变量
beginret := 'Hello, ' || str; -- 拼接字符串DBMS_OUTPUT.put_line(ret); -- 打印结果return ret; -- 返回结果(必须有)
end;
(2)调用函数
plsql
-- 像调用系统函数一样使用
select hello('Oracle') from dual; -- 输出:Hello, Oracle
(3)注意事项
- 参数类型不能指定长度(如
varchar2
不能写成varchar2(20)
)。 - 必须有
return
子句,且函数体内至少有一条return
语句。 - 支持
in
(输入)、out
(输出)、in out
(输入输出)参数。
六、自动执行的 “监听器”:触发器
触发器是数据库的 “自动响应机制”—— 当指定事件发生时,自动执行预设逻辑,无需手动调用。
1️⃣触发器的核心组成
- 触发事件:如 INSERT/UPDATE/DELETE(DML)、登录 / 退出(用户事件)。
- 触发时间:事件发生前(BEFORE)或后(AFTER)。
- 触发对象:表、视图、数据库等。
- 触发条件:用
WHEN
指定逻辑,为 TRUE 时才执行。 - 触发频率:语句级(事件发生一次执行一次)或行级(每行触发一次)。
2️⃣实例:删除数据前自动备份
plsql
-- 创建触发器:删除my_dept表中deptno=40的记录时,自动备份到dept_backups
create or replace trigger backup_deptbefore delete -- 删除前执行on scott.my_dept -- 监听的表for each row -- 行级触发(每删一行执行一次)when (old.deptno = 40) -- 只备份deptno=40的记录
begin-- :old表示“修改前的记录”,存储旧值insert into dept_backups values(:old.deptno, :old.dname, :old.loc, sysdate);
end;
3️⃣系统触发器:监控数据库事件
比如记录用户登录 / 退出时间:
plsql
-- 先创建日志表
create table log_event (user_name varchar2(10),address varchar2(20),logon_date timestamp,logoff_date timestamp
);-- 登录触发器:用户登录时记录信息
create or replace trigger tr_logon
after logon on database -- 数据库级事件:登录后
begininsert into log_event (user_name, address, logon_date)values (ora_login_user, ora_client_ip_address, systimestamp);
end tr_logon;
七、模块化管理:程序包
当存储过程和函数越来越多时,用 “程序包” 管理会更清晰 —— 类似将多个工具放进一个 “工具箱”,方便调用和维护。
程序包由两部分组成:
- 包声明(package):定义对外暴露的存储过程、函数、变量(类似 “接口”)。
- 包体(package body):实现包声明中定义的逻辑(类似 “实现类”)。
示例:创建管理部门的程序包
plsql
-- 1. 包声明:定义要包含的存储过程
create or replace package my_package asprocedure add_my_dept2(V_deptno number, V_dname varchar2, V_loc varchar2);procedure del_my_dept3(V_deptno number);
end;-- 2. 包体:实现存储过程的逻辑
create or replace package body my_package as-- 实现新增部门procedure add_my_dept2(V_deptno number, V_dname varchar2, V_loc varchar2) isbegininsert into my_dept values(V_deptno, V_dname, V_loc);commit;end add_my_dept2;-- 实现删除部门procedure del_my_dept3(V_deptno number) isbegindelete from my_dept where deptno = V_deptno;commit;end del_my_dept3;
end;
调用时直接通过包名访问:
plsql
exec my_package.add_my_dept2(20, '测试部', '广州');
总结:从入门到熟练的关键
PL/SQL 是 ORACLE 数据库操作的核心工具,掌握它需要:
- 理解块的分类(无名块、存储过程、触发器等)的适用场景;
- 牢记标识符规则和命名规范,养成良好编码习惯;
- 多动手实践:从简单的无名块开始,逐步尝试存储过程、函数和触发器;
- 学会用程序包管理代码,提升项目的可维护性。