Oracle PL/SQL 基础与块结构

Oracle PL/SQL 基础与块结构

适用版本:Oracle Database 10g / 11g / 12c / 19c / 23ai 文档版本:v1.0 / 2026-07


1. 概述

PL/SQL 是 Oracle 过程化 SQL 扩展[1]:

详细见:Oracle PL/SQL 基础与块结构


2. 块结构

2.1 完整

DECLARE
  -- 声明
  v_name VARCHAR2(100) := 'Alice';
  v_count NUMBER;
BEGIN
  -- 执行
  SELECT COUNT(*) INTO v_count FROM employees;
  DBMS_OUTPUT.PUT_LINE('Count: ' || v_count);
EXCEPTION
  -- 异常
  WHEN NO_DATA_FOUND THEN
    DBMS_OUTPUT.PUT_LINE('No data');
  WHEN OTHERS THEN
    DBMS_OUTPUT.PUT_LINE('Error: ' || SQLERRM);
END;
/

2.2 匿名块

BEGIN
  DBMS_OUTPUT.PUT_LINE('Hello');
END;
/

2.3 命名块

  • 过程(PROCEDURE)
  • 函数(FUNCTION)
  • 包(PACKAGE)
  • 触发器(TRIGGER)

详细见:Oracle 存储过程与函数详解Oracle PL/SQL 包设计与最佳实践Oracle PL/SQL 触发器详解


3. 变量声明

3.1 标量

DECLARE
  v_id NUMBER;
  v_name VARCHAR2(100);
  v_salary NUMBER(10, 2);
  v_hire_date DATE;
  v_active BOOLEAN := TRUE;
  v_count NUMBER DEFAULT 0;
BEGIN
  ...
END;

3.2 锚定

DECLARE
  v_name employees.name%TYPE;        -- 列类型
  v_emp employees%ROWTYPE;            -- 行类型
BEGIN
  SELECT * INTO v_emp FROM employees WHERE id = 1;
  v_name := v_emp.name;
END;

3.3 复合

DECLARE
  TYPE emp_rec IS RECORD (
    id NUMBER,
    name VARCHAR2(100),
    salary NUMBER
  );
  
  TYPE emp_tab IS TABLE OF employees%ROWTYPE INDEX BY VARCHAR2(50);
  
  v_emp emp_rec;
  v_emps emp_tab;
BEGIN
  ...
END;

详细见:Oracle PL/SQL 集合详解

3.4 常量

DECLARE
  c_max_salary CONSTANT NUMBER := 100000;
  c_company_name CONSTANT VARCHAR2(50) := 'ACME';
BEGIN
  ...
END;

4. 赋值

4.1 直接

v_name := 'Alice';
v_count := 10;
v_active := TRUE;

4.2 SELECT INTO

SELECT name, salary INTO v_name, v_salary
FROM employees WHERE id = 1;

4.3 表达式

v_total := v_salary + v_bonus;
v_full_name := v_first || ' ' || v_last;
v_age := MONTHS_BETWEEN(SYSDATE, v_birth) / 12;

5. 控制流

5.1 IF

IF v_salary > 10000 THEN
  v_grade := 'A';
ELSIF v_salary > 5000 THEN
  v_grade := 'B';
ELSE
  v_grade := 'C';
END IF;

5.2 CASE

-- 简单
CASE v_dept_id
  WHEN 10 THEN v_dept := 'IT';
  WHEN 20 THEN v_dept := 'Sales';
  ELSE v_dept := 'Other';
END CASE;

-- 搜索
CASE 
  WHEN v_salary > 10000 THEN v_level := 'High';
  WHEN v_salary > 5000 THEN v_level := 'Mid';
  ELSE v_level := 'Low';
END CASE;

5.3 LOOP

-- 基本
LOOP
  v_count := v_count + 1;
  EXIT WHEN v_count > 10;
END LOOP;

-- WHILE
WHILE v_count < 10 LOOP
  v_count := v_count + 1;
END LOOP;

-- FOR
FOR i IN 1..10 LOOP
  DBMS_OUTPUT.PUT_LINE(i);
END LOOP;

-- REVERSE
FOR i IN REVERSE 1..10 LOOP
  DBMS_OUTPUT.PUT_LINE(i);
END LOOP;

6. SQL in PL/SQL

6.1 SELECT

SELECT name, salary INTO v_name, v_salary
FROM employees WHERE id = 1;

6.2 DML

INSERT INTO employees (id, name) VALUES (1, 'Alice');
UPDATE employees SET salary = 5000 WHERE id = 1;
DELETE FROM employees WHERE id = 1;

6.3 事务

COMMIT;
ROLLBACK;
SAVEPOINT sp1;
ROLLBACK TO sp1;

详细见:Oracle 事务与并发控制详解


7. NULL 处理

-- NULL 比较
IF v_value IS NULL THEN ...
IF v_value IS NOT NULL THEN ...

-- 函数
v_name := NVL(v_value, 'default');
v_status := NVL2(v_value, 'has', 'no');
v_first := COALESCE(v_a, v_b, v_c);

8. 注释

-- 单行

/* 
  多行
*/

-- 标题
PROCEDURE my_proc IS
  -- TODO: 待实现
BEGIN
  NULL;  -- 占位
END;

9. PRAGMA

9.1 AUTONOMOUS_TRANSACTION

PROCEDURE log_msg IS
  PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
  ...
END;

9.2 EXCEPTION_INIT

DECLARE
  e_fk EXCEPTION;
  PRAGMA EXCEPTION_INIT(e_fk, -02292);
BEGIN
  ...
EXCEPTION
  WHEN e_fk THEN ...
END;

9.3 SERIALLY_REUSABLE

CREATE OR REPLACE PACKAGE pkg AS
  PRAGMA SERIALLY_REUSABLE;
END;

9.4 INLINE

PROCEDURE caller IS
  PRAGMA INLINE(small_proc, 'YES');
BEGIN
  small_proc;
END;

10. DBMS_OUTPUT

BEGIN
  DBMS_OUTPUT.ENABLE;
  DBMS_OUTPUT.PUT_LINE('Hello');
  DBMS_OUTPUT.PUT('No newline');
  DBMS_OUTPUT.NEW_LINE;
END;
/

SET SERVEROUTPUT ON;

11. 子程序

11.1 局部

DECLARE
  v_count NUMBER;
  
  PROCEDURE local_proc IS
  BEGIN
    DBMS_OUTPUT.PUT_LINE('Local');
  END;
  
  FUNCTION local_fn RETURN NUMBER IS
  BEGIN
    RETURN 1;
  END;
BEGIN
  local_proc;
  v_count := local_fn;
END;
/

11.2 嵌套

PROCEDURE outer IS
  PROCEDURE inner IS
  BEGIN
    ...
  END;
BEGIN
  inner;
END;

12. 数据类型

类型示例
标量NUMBER, VARCHAR2, DATE, BOOLEAN
锚定%TYPE, %ROWTYPE
复合RECORD, TABLE, VARRAY
引用REF CURSOR
LOBCLOB, BLOB
对象OBJECT

详细见:Oracle 数据类型详解Oracle PL/SQL 集合详解


13. 异常

13.1 预定义

EXCEPTION
  WHEN NO_DATA_FOUND THEN ...
  WHEN TOO_MANY_ROWS THEN ...
  WHEN ZERO_DIVIDE THEN ...
  WHEN OTHERS THEN ...

13.2 自定义

DECLARE
  e_custom EXCEPTION;
  PRAGMA EXCEPTION_INIT(e_custom, -20001);
BEGIN
  IF condition THEN
    RAISE e_custom;
  END IF;
EXCEPTION
  WHEN e_custom THEN ...
END;

详细见:Oracle PL/SQL 异常处理详解


14. 命名规范

- 变量:v_xxx
- 常量:c_xxx
- 类型:t_xxx(type)/ xxx_rec / xxx_tab
- 异常:e_xxx
- 游标:c_xxx
- 参数:p_xxx
- 过程:sp_xxx / proc_xxx
- 函数:fn_xxx

15. 性能

15.1 绑定变量

-- 自动(静态 SQL)
EXECUTE IMMEDIATE 'SELECT * FROM t WHERE id = :id' USING v_id;

15.2 BULK

SELECT * BULK COLLECT INTO v_emp FROM employees;
FORALL i IN 1..v_emp.COUNT
  INSERT INTO log VALUES (v_emp(i).id);

详细见:Oracle PL/SQL 性能优化详解


16. 常见坑与排错

16.1 ORA-06550

- 编译错误
- 检查语法

16.2 ORA-06502

- 值错误
- 类型/长度

16.3 ORA-06512

- 错误栈
- 行号

16.4 NO_DATA_FOUND

- SELECT INTO 无行
- 处理

17. 最佳实践

  1. 命名规范:清晰
  2. %TYPE/%ROWTYPE:锚定
  3. 常量:定义
  4. 异常处理:完整
  5. 绑定变量:性能
  6. BULK:批量
  7. 注释:说明
  8. 模块化:包
  9. 测试:验证
  10. 文档化:维护

18. 参考资料

[1] Oracle Database PL/SQL Language Reference 19c, “PL/SQL Language Elements” https://docs.oracle.com/en/database/oracle/oracle-database/19/lnpls/plsql-language-elements.html