PL/SQL概述


[TOC]

PL/SQL概述

PL/SQL(Procedure Language/SQL)是 Oracle对 SQL 语言的过程化扩展,指在 SQL 命令语言中增加了过程处理语句(如分支、循环等),使 SQL 语言具有过程处理能力。把SQL语言的数据操纵能力与过程语言的数据处理能力结合起来,使得 PLSQL 面向过程但比过程语言简单、高效、灵活和实用。

  • PL/SQL是一种完全可移植的高性能事务处理语言
  • PL/SQL提供了内置的,解释的且与OS无关的编程环境
  • 也可以从命令行SQL * Plus接口直接调用PL/SQL
  • 也可以从外部编程语言调用直接调用数据库
  • PL/SQL的通用语法基于ADA和Pascal编程语言
  • 除了Oracle,PL/SQL还可以在TimesTen内存数据库和IBM DB2中使用

一、PL/SQL 程序结构

DECLARE
    -- 声明部分 (可选)
    -- 变量、常量、游标、异常等的声明
BEGIN
    -- 执行部分 (必需)
    -- PL/SQL语句和SQL语句
EXCEPTION
    -- 异常处理部分 (可选)
    -- 异常处理代码
END;

案例:

declare
    age number default 10; -- 定义一个age变量,默认值为10;
begin
    age := 11; -- 将age变量的值变为11;
    DBMS_OUTPUT.put_line('年龄:' || age); -- 打印括号里的值
end;

二. 变量声明

变量是在程序中可以操纵的存储区域的名称。 PL/SQL中的每个变量都有一个指定的数据类型,它决定了变量内存的大小和布局。

PL/SQL变量的名称由可选的字母,数字,美元($)符号,下划线和数字符号组成,不能超过30个字符。 默认情况下,变量名不区分大小写。不能将保留的PL/SQL关键字用作变量名称。

基本语法格式:

-- 声明变量
变量名 类型(长度);
-- 变量赋值
变量名 := 变量值;
-- select into 变量赋值
select 列名 into 变量名 from 表名 where 条件;
select 列名1,列名2 into 变量1,变量2 from 表名 where 条件
-- 列名要和变量名一一对应
2.1 变量数据类型

PL/SQL支持多种数据类型,包括:

  • 标量类型:NUMBER,VARCHAR2,CHAR,DATE,BOOLEAN等
  • 复合类型:RECORD,TABLE
  • 引用类型:REF CURSOR
  • LOB类型:BLOB,CLOB,BFILE
2.2 记录型变量

列变量:记录表中的一列存放在变量中

基本语法格式:

-- 定义列变量
列变量名 表名.列名%type;
-- 定义变量v_num0,其类型与T_ACCOUNT表的NUM0字段相同
v_num0     T_ACCOUNT.NUM0%type;

行变量:记录表中的一行存放在变量中

基本语法格式:

-- 定义行变量
行变量名 表名%rowtype;
-- 引用行变量
行变量名.列名;

三、控制结构

3.1 条件语句

IF-THEN-ELSE

  • PL/SQL中使用elsif而不是elseif
if condition then
    statements;  
    -- 条件成立时执行的语句
elsif condition then  
    statements;	-- 可选的额外条件判断
else  
    statements;	-- 所有条件都不成立时执行
end if;  -- 必须以END IF结束
3.2 循环语句

基本循环

  • 基本循环 - 需要显式指定退出条件
LOOP
    statements;  -- 循环体语句
    EXIT WHEN condition;  -- 满足条件时退出循环
END LOOP;  -- 循环结束标记

WHILE循环

  • WHILE循环 - 先判断条件再执行
while condition loop -- 当条件为真时执行循环
    statements;
end loop;

FOR循环

for counter in start..end loop  -- counter从start到end自动递增
    statements;  
    -- 可以使用counter变量
END LOOP;

四、游标

游标是系统为用户开设的一个数据缓冲区,存放 SQL 语句的执行结果。

游标(Cursor)是一种用于查询结果集的指针,它允许你逐行处理查询结果。

显式游标

declare
    -- 声明游标,定义查询语句
    cursor emp_cursor is selsct * from employees;
    -- 定义记录变量,用于存储游标返回的行数据
    v_emp employees%ROWTYPE;  
BEGIN
    OPEN emp_cursor;  -- 打开游标
    LOOP
        FETCH emp_cursor INTO v_emp;  
        -- 获取一行数据存入变量
        exit when emp_cursor%NOTFOUND; 
        -- 当没有更多数据时退出循环
        -- 在这里处理获取到的数据(v_emp)
    END LOOP;
    CLOSE emp_cursor;  -- 必须显式关闭游标
END;

隐式游标

隐式调用游标,实现自动管理,可以简化代码

BEGIN
    -- 直接在FOR循环中使用SELECT语句
    for emp_rec in (select * from employees) LOOP
        -- 可以直接使用emp_rec访问当前行数据
        -- 不需要显式打开/关闭游标
    END LOOP;
END;

五、异常处理

异常是指PL/SQL程序在执行时出现的错误,在PL/SQL中的一个警告或错误的情形都被称为异常

异常处理通常写在执行体的最下面,在所有执行语句之后以exception关键字开始异常处理

在运行程序时出现的错误叫做异常,发生异常后,语句将停止执行,控制权转移到 PL/SQL 块的异常处理部分

异常有两种类型:

  • 预定义异常 - 当 PL/SQL 程序违反 Oracle 规则或超越系统限制时隐式引发
  • 用户定义异常 - 用户可以在 PL/SQL 块的声明部分定义异常,自定义的异常通过 RAISE 语句显式引发

以下是Oracle数据库中常见的21个预定义异常及其说明的整理表格:

异常名称 错误代码 触发场景
NO_DATA_FOUND ORA-01403 SELECT INTO 语句未找到任何数据时触发。
TOO_MANY_ROWS ORA-01422 SELECT INTO 语句返回多行数据时触发。
DUP_VAL_ON_INDEX ORA-00001 尝试向唯一索引列插入重复值时触发(违反唯一性约束)。
INVALID_CURSOR ORA-01001 尝试操作未打开或已关闭的游标(如关闭未打开的游标)。
ZERO_DIVIDE ORA-01476 除数为零时触发(如 5/0)。
INVALID_NUMBER ORA-01722 字符串转换为数字失败时触发(如 TO_NUMBER('ABC'))。
VALUE_ERROR ORA-06502 变量赋值时数据类型或长度不匹配(如字符串超长)。
STORAGE_ERROR ORA-06500 PL/SQL 运行时内存不足或栈溢出时触发。
PROGRAM_ERROR ORA-06501 PL/SQL 内部错误(如程序逻辑问题)。
TIMEOUT_ON_RESOURCE ORA-00051 等待数据库资源超时(如锁等待)。
CURSOR_ALREADY_OPEN ORA-06511 尝试打开已打开的游标时触发。
LOGIN_DENIED ORA-01017 使用无效用户名或密码登录数据库时触发。
NOT_LOGGED_ON ORA-01012 尝试执行数据库操作但未建立有效连接时触发。
ACCESS_INTO_NULL ORA-06530 尝试为未初始化的对象属性赋值时触发。
CASE_NOT_FOUND ORA-06592 CASE 语句无匹配条件且无 ELSE 子句时触发。
COLLECTION_IS_NULL ORA-06531 访问未初始化的集合(如嵌套表或数组)时触发。
SUBSCRIPT_BEYOND_COUNT ORA-06533 访问集合元素时下标超过集合实际元素数量。
SUBSCRIPT_OUTSIDE_LIMIT ORA-06532 使用非法下标(如负数或零)访问集合元素。
ROWTYPE_MISMATCH ORA-06504 游标返回的行类型与接收变量类型不匹配时触发。
SELF_IS_NULL ORA-30625 调用未初始化的对象类型的方法时触发。
SYS_INVALID_ROWID ORA-01410 转换无效字符串为 ROWID 时触发(如 ROWID 格式错误)。

异常基本语法结构:

EXCEPTION
	when	异常类型	then -- 异常处理逻辑

用户自定义异常语法结构:

declare
    自定义异常名 EXCEPTION;
    PRAGMA EXCEPTION_INIT (自定义异常名,错误码(-01403));
begin

exception
    when 自定义异常名 then
       subquery;
end;

案例:

BEGIN
    -- 可能抛出异常的代码块
    -- 例如:SELECT INTO可能抛出NO_DATA_FOUND异常
EXCEPTION  -- 异常处理部分
    WHEN NO_DATA_FOUND THEN  -- 处理特定异常
        DBMS_OUTPUT.PUT_LINE('没有找到数据');
    WHEN OTHERS THEN  -- 处理所有其他未捕获的异常
        DBMS_OUTPUT.PUT_LINE('错误代码: ' || SQLCODE);  -- 系统错误代码
        DBMS_OUTPUT.PUT_LINE('错误消息: ' || SQLERRM);  -- 错误消息文本
END;

六、存储过程和函数

6.1 存储过程

存储过程:是一组为了完成特定功能的SQL语句集合,经编译后存储在数据库中

主要特点

  • 不直接返回值,但可以通过OUT参数返回数据
  • 可以作为独立的PL/SQL块调用
  • 可以包含事务控制语句(COMMIT/ROLLBACK)
  • 主要用于执行操作而非计算

存储过程创建语法:

CREATE [OR REPLACE] PROCEDURE procedure_name  
	-- OR REPLACE表示存在则替换
[(parameter1 [IN | OUT | IN OUT] datatype [, ...])]  -- 参数定义部分
{IS | AS}  -- IS或AS关键字开始声明部分
[声明部分]  -- 定义局部变量、游标等
BEGIN
   -- 执行部分(核心业务逻辑)
EXCEPTION
	-- 异常处理部分
END [procedure_name];
	-- 可选的结束标签
模式 描述 示例
IN 默认模式,输入参数 p_emp_id IN NUMBER
OUT 输出参数,用于返回值 p_result OUT VARCHAR2
IN OUT 既是输入也是输出参数 p_counter IN OUT NUMBER

案例:

CREATE OR REPLACE PROCEDURE update_salary(
    p_emp_id IN NUMBER,    -- 输入参数:员工ID
    p_amount IN NUMBER     -- 输入参数:薪资调整金额
) AS
BEGIN
    -- 更新指定员工的薪资
    UPDATE employees
    SET salary = salary + p_amount
    WHERE employee_id = p_emp_id;
    
    COMMIT;  -- 提交事务
EXCEPTION
    WHEN OTHERS THEN  -- 发生异常时回滚
        ROLLBACK;
        RAISE;  -- 重新抛出异常
END update_salary;
6.2 函数

主要特点

  • 必须返回一个值(通过RETURN语句)
  • 不能包含事务控制语句(COMMIT/ROLLBACK)
  • 主要用于计算和返回值
  • 可以作为SQL表达式的一部分调用

PLSQL函数创建语法

CREATE [OR REPLACE] FUNCTION function_name		-- OR REPLACE表示存在则替换
[(parameter1 [IN | OUT | IN OUT] datatype [, ...])]  -- 参数定义部分
RETURN return_datatype  -- 必须指定返回类型
{IS | AS}  -- IS或AS关键字开始声明部分
[声明部分]  -- 定义局部变量、游标等
BEGIN
   -- 执行部分
   RETURN expression;  -- 必须包含RETURN语句
EXCEPTION
   -- 异常处理部分
END [function_name];

案例:

CREATE OR REPLACE FUNCTION get_employee_name(
    p_emp_id IN NUMBER  -- 输入参数:员工ID
) RETURN VARCHAR2 AS    -- 指定返回值类型
    v_name VARCHAR2(100);  -- 局部变量
BEGIN
    -- 查询并拼接员工姓名
    SELECT first_name || ' ' || last_name
    INTO v_name
    FROM employees
    WHERE employee_id = p_emp_id;
    
    RETURN v_name;  -- 必须包含RETURN语句
EXCEPTION
    WHEN NO_DATA_FOUND THEN
        RETURN '员工不存在';  -- 异常时返回默认值
END get_employee_name;
6.3 存储过程与函数对比
特性 存储过程 函数
返回值 通过OUT参数返回 必须通过RETURN返回
调用方式 独立调用 可作为表达式一部分
事务控制 可以包含COMMIT/ROLLBACK 不能包含
主要用途 执行数据库操作 计算并返回值
DML语句 可以包含 可以包含(在特定情况下)
在SQL中调用 不能直接在SELECT中使用 可以直接在SELECT中使用

七、包 [Package]

PL/SQL包是Oracle数据库中的一个重要特性,它是一组相关的过程、函数、变量、常量和游标等程序对象的集合,被组织在一个逻辑单元中

PL/SQL包由两部分组成:

  1. 包规范 [Package Specification]
    • 定义包的公共接口
    • 声明可以被外部程序访问的类型、变量、常量、异常、游标、过程和函数
    • 不包含任何实现代码
  2. 包体 [Package Body]
    • 包含包规范中声明的所有过程和函数的实现代码
    • 可以包含额外的私有变量、过程和函数(这些在包规范中未声明,只能在包内部使用)

包规范

-- 包规范 - 定义公共接口
CREATE OR REPLACE PACKAGE emp_pkg AS
    -- 公共类型和常量声明
    -- 公共过程声明
    PROCEDURE update_salary(
        p_emp_id IN NUMBER,
        p_amount IN NUMBER
    );
    -- 公共函数声明
    FUNCTION get_employee_name(
        p_emp_id IN NUMBER
    ) RETURN VARCHAR2;
END emp_pkg;

包体

-- 包体 - 实现包规范中声明的内容
CREATE OR REPLACE PACKAGE BODY emp_pkg AS
    -- 私有变量和类型声明 - 只能在包内访问
    -- 实现update_salary过程
    PROCEDURE update_salary(
        p_emp_id IN NUMBER,
        p_amount IN NUMBER
    ) AS
    BEGIN
        -- 实际实现代码
        NULL;  -- 示例中暂不实现
    END update_salary;
    
    -- 实现get_employee_name函数
    FUNCTION get_employee_name(
        p_emp_id IN NUMBER
    ) RETURN VARCHAR2 AS
    BEGIN
        -- 实际实现代码
        RETURN NULL;  -- 示例中暂不实现
    END get_employee_name;
END emp_pkg;

常用系统包,Oracle提供了许多内置包如:

  • DBMS_OUTPUT:用于输出调试信息
  • UTL_FILE:用于文件I/O操作
  • DBMS_SCHEDULER:用于作业调度
  • DBMS_LOB:用于大对象操作
  • DBMS_SQL:用于动态SQL

八、触发器

PL/SQL触发器是Oracle数据库中一种特殊的存储过程,它在特定数据库事件发生时自动执行。触发器与表相关联,当指定的事件发生在表上时,触发器就会被"触发"执行

8.1 触发器的类型

DML触发器:响应DML操作(INSERT, UPDATE, DELETE):

  • 行级触发器:受影响的每一行都会触发一次
  • 语句级触发器:每个DML语句触发一次,无论影响多少行

DDL触发器:响应DDL操作(CREATE, ALTER, DROP等)

系统/数据库事件触发器:响应系统事件(如数据库启动、关闭,用户登录、注销等)

INSTEAD OF触发器:用于视图上的复杂更新操作

8.2 触发器的时间点
  • BEFORE:在触发事件之前执行
  • AFTER:在触发事件之后执行
  • INSTEAD OF:替代触发事件执行

创建触发器的基本语法:

CREATE [OR REPLACE] TRIGGER trigger_name
{BEFORE | AFTER | INSTEAD OF} {INSERT | UPDATE | DELETE [OF column,...]}
ON {table_name | view_name}
[REFERENCING [OLD AS old] [NEW AS new]]
[FOR EACH ROW]
[WHEN (condition)]
DECLARE
  -- 声明部分
BEGIN
  -- 触发器主体
EXCEPTION
  -- 异常处理
END;

以上是PL/SQL的基本语法概述,PL/SQL还支持更高级的特性如批量绑定、动态SQL、集合操作等。


文章作者: 十二惊惶
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 十二惊惶 !
  目录