存储过程是预编译的SQL代码块,存储在数据库中,可以通过名称调用执行。存储过程可以包含SQL语句、流程控制、变量声明、异常处理等,适合封装复杂的业务逻辑。存储过程支持参数传递,包括输入参数(IN)、输出参数(OUT)和输入输出参数(IN OUT)。
TEST.EMPLOYEE(已有约 100004 行数据)DBMS_OUTPUT 包CREATE OR REPLACE PROCEDURE 过程名(参数列表) AS
-- 变量声明部分
变量名 数据类型;
BEGIN
-- 执行语句部分
SQL语句;
EXCEPTION
-- 异常处理部分(可选)
WHEN 异常名 THEN
处理语句;
END;
/
创建存储过程的基本模板,包含变量声明、执行语句和异常处理三部分,以 / 结束创建。
CREATE OR REPLACE PROCEDURE TEST.PROC_HELLO AS
BEGIN
DBMS_OUTPUT.PUT_LINE('Hello, World!');
END;
/
创建一个名为 PROC_HELLO 的存储过程,调用时输出 “Hello, World!”。
CREATE OR REPLACE PROCEDURE TEST.PROC_GET_EMPLOYEE(
p_id IN INT
) AS
v_name VARCHAR(50);
v_age INT;
BEGIN
SELECT NAME, AGE INTO v_name, v_age
FROM TEST.EMPLOYEE
WHERE ID = p_id;
DBMS_OUTPUT.PUT_LINE('员工姓名: ' || v_name || ', 年龄: ' || v_age);
EXCEPTION
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE('未找到ID为 ' || p_id || ' 的员工');
END;
/
传入员工ID(输入参数 p_id),查询对应员工的姓名和年龄并输出;如果ID不存在则捕获异常并提示。
CREATE OR REPLACE PROCEDURE TEST.PROC_GET_EMPLOYEE_COUNT(
p_dept IN VARCHAR,
p_count OUT INT
) AS
BEGIN
SELECT COUNT(*) INTO p_count
FROM TEST.EMPLOYEE
WHERE DEPT = p_dept;
END;
/
传入部门名称(输入参数 p_dept),将对应部门的员工数量通过输出参数 p_count 返回给调用者。
SET SERVEROUTPUT ON;
启用服务端输出功能,使 DBMS_OUTPUT.PUT_LINE 的输出能够显示在客户端。
CREATE OR REPLACE PROCEDURE TEST.PROC_DEBUG_DEMO(
p_id IN INT
) AS
v_name VARCHAR(50);
v_age INT;
BEGIN
DBMS_OUTPUT.PUT_LINE('开始执行,输入参数 p_id=' || p_id);
SELECT NAME, AGE INTO v_name, v_age
FROM TEST.EMPLOYEE
WHERE ID = p_id;
DBMS_OUTPUT.PUT_LINE('查询结果: NAME=' || v_name || ', AGE=' || v_age);
IF v_age > 30 THEN
DBMS_OUTPUT.PUT_LINE('该员工年龄超过30岁');
ELSE
DBMS_OUTPUT.PUT_LINE('该员工年龄不超过30岁');
END IF;
DBMS_OUTPUT.PUT_LINE('执行结束');
EXCEPTION
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE('异常: 未找到ID=' || p_id || '的记录');
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('其他异常: ' || SQLERRM);
END;
/
在执行过程中通过 DBMS_OUTPUT.PUT_LINE 输出每一步的进度和变量值(开始、查询结果、判断结果、结束),方便追踪执行路径。
CALL TEST.PROC_DEBUG_DEMO(1);
调用调试存储过程,输出每一步的执行情况,确认程序按预期路径运行。
实际输出:
开始执行,输入参数 p_id=1
查询结果: NAME=张三, AGE=30
该员工年龄不超过30岁
执行结束
DMSQL 过程已成功完成
CREATE TABLE TEST.PROC_LOG (
LOG_ID INT IDENTITY(1,1),
PROC_NAME VARCHAR(100),
LOG_TIME DATETIME DEFAULT SYSDATE,
LOG_MSG VARCHAR(4000)
);
创建日志表,包含自增ID、过程名、记录时间和消息内容,用于持久化存储存储过程的执行日志。
CREATE OR REPLACE PROCEDURE TEST.PROC_DEBUG_WITH_LOG(
p_id IN INT
) AS
v_name VARCHAR(50);
v_age INT;
BEGIN
-- 记录开始日志
INSERT INTO TEST.PROC_LOG(PROC_NAME, LOG_MSG)
VALUES ('PROC_DEBUG_WITH_LOG', '开始执行,p_id=' || p_id);
SELECT NAME, AGE INTO v_name, v_age
FROM TEST.EMPLOYEE
WHERE ID = p_id;
-- 记录查询结果
INSERT INTO TEST.PROC_LOG(PROC_NAME, LOG_MSG)
VALUES ('PROC_DEBUG_WITH_LOG', '查询结果: NAME=' || v_name || ', AGE=' || v_age);
DBMS_OUTPUT.PUT_LINE('员工: ' || v_name || ', 年龄: ' || v_age);
IF v_age > 30 THEN
DBMS_OUTPUT.PUT_LINE('年龄超过30岁');
ELSE
DBMS_OUTPUT.PUT_LINE('年龄不超过30岁');
END IF;
-- 记录结束日志
INSERT INTO TEST.PROC_LOG(PROC_NAME, LOG_MSG)
VALUES ('PROC_DEBUG_WITH_LOG', '执行结束');
COMMIT;
EXCEPTION
WHEN NO_DATA_FOUND THEN
INSERT INTO TEST.PROC_LOG(PROC_NAME, LOG_MSG)
VALUES ('PROC_DEBUG_WITH_LOG', '异常: 未找到ID=' || p_id);
DBMS_OUTPUT.PUT_LINE('未找到ID=' || p_id);
WHEN OTHERS THEN
INSERT INTO TEST.PROC_LOG(PROC_NAME, LOG_MSG)
VALUES ('PROC_DEBUG_WITH_LOG', '其他异常: ' || SQLERRM);
DBMS_OUTPUT.PUT_LINE('异常: ' || SQLERRM);
END;
/
在执行过程中向 PROC_LOG 表插入开始、查询结果、结束三条日志记录,同时通过 DBMS_OUTPUT 输出信息到客户端;异常时也会记录异常日志。
TRUNCATE TABLE TEST.PROC_LOG;
CALL TEST.PROC_DEBUG_WITH_LOG(1);
SELECT * FROM TEST.PROC_LOG ORDER BY LOG_ID;
清空日志表,执行存储过程查询 ID=1 的员工,然后查看日志表中记录的执行过程。
日志表输出:
行号 LOG_ID PROC_NAME LOG_TIME LOG_MSG
----- ------ ----------------------- ----------------------- --------------------------------
1 1 PROC_DEBUG_WITH_LOG 2026-08-03 09:18:18.000 开始执行,p_id=1
2 2 PROC_DEBUG_WITH_LOG 2026-08-03 09:18:18.000 查询结果: NAME=张三, AGE=30
3 3 PROC_DEBUG_WITH_LOG 2026-08-03 09:18:18.000 执行结束
DROP PROCEDURE TEST.PROC_HELLO;
删除指定的存储过程。
SELECT TEXT FROM USER_SOURCE WHERE NAME='PROC_DEBUG_WITH_LOG' AND TYPE='PROCEDURE';
查看存储过程的源代码。
| 调试方法 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
DBMS_OUTPUT.PUT_LINE |
即时反馈,无需额外表 | 只能在当前会话查看,无法持久化 | 开发阶段实时调试 |
| 日志表记录 | 持久化存储,可随时查询 | 需要创建表,存储过程需修改 | 生产环境问题追踪 |
| 问题 | 原因 | 解决方法 |
|---|---|---|
DBMS_OUTPUT 无输出 |
未启用输出功能 | 执行 SET SERVEROUTPUT ON; |
无效的变量名 |
在存储过程外部引用了局部变量 | 变量只能在存储过程内部使用,或在外部使用绑定变量 |
NO_DATA_FOUND 未触发 |
查询实际返回了数据 | 使用 SELECT COUNT(*) 先判断是否存在,再用 SELECT INTO |
-- 启用 DBMS_OUTPUT
SET SERVEROUTPUT ON;
-- 创建无参数存储过程
CREATE OR REPLACE PROCEDURE TEST.PROC_HELLO AS
BEGIN
DBMS_OUTPUT.PUT_LINE('Hello, World!');
END;
/
CALL TEST.PROC_HELLO();
-- 创建带输入参数的存储过程
CREATE OR REPLACE PROCEDURE TEST.PROC_GET_EMPLOYEE(p_id IN INT) AS
v_name VARCHAR(50);
v_age INT;
BEGIN
SELECT NAME, AGE INTO v_name, v_age FROM TEST.EMPLOYEE WHERE ID = p_id;
DBMS_OUTPUT.PUT_LINE('员工: ' || v_name || ', 年龄: ' || v_age);
EXCEPTION
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE('未找到ID=' || p_id);
END;
/
CALL TEST.PROC_GET_EMPLOYEE(1);
-- 创建带调试输出的存储过程
CREATE OR REPLACE PROCEDURE TEST.PROC_DEBUG_DEMO(p_id IN INT) AS
v_name VARCHAR(50);
v_age INT;
BEGIN
DBMS_OUTPUT.PUT_LINE('开始执行,p_id=' || p_id);
SELECT NAME, AGE INTO v_name, v_age FROM TEST.EMPLOYEE WHERE ID = p_id;
DBMS_OUTPUT.PUT_LINE('查询结果: NAME=' || v_name || ', AGE=' || v_age);
IF v_age > 30 THEN
DBMS_OUTPUT.PUT_LINE('年龄超过30岁');
ELSE
DBMS_OUTPUT.PUT_LINE('年龄不超过30岁');
END IF;
DBMS_OUTPUT.PUT_LINE('执行结束');
EXCEPTION
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE('异常: 未找到ID=' || p_id);
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('其他异常: ' || SQLERRM);
END;
/
CALL TEST.PROC_DEBUG_DEMO(1);
-- 创建日志表
CREATE TABLE TEST.PROC_LOG (
LOG_ID INT IDENTITY(1,1),
PROC_NAME VARCHAR(100),
LOG_TIME DATETIME DEFAULT SYSDATE,
LOG_MSG VARCHAR(4000)
);
-- 创建带日志记录的存储过程
CREATE OR REPLACE PROCEDURE TEST.PROC_DEBUG_WITH_LOG(p_id IN INT) AS
v_name VARCHAR(50);
v_age INT;
BEGIN
INSERT INTO TEST.PROC_LOG(PROC_NAME, LOG_MSG) VALUES ('PROC_DEBUG_WITH_LOG', '开始执行,p_id=' || p_id);
SELECT NAME, AGE INTO v_name, v_age FROM TEST.EMPLOYEE WHERE ID = p_id;
INSERT INTO TEST.PROC_LOG(PROC_NAME, LOG_MSG) VALUES ('PROC_DEBUG_WITH_LOG', '查询结果: NAME=' || v_name || ', AGE=' || v_age);
DBMS_OUTPUT.PUT_LINE('员工: ' || v_name || ', 年龄: ' || v_age);
IF v_age > 30 THEN
DBMS_OUTPUT.PUT_LINE('年龄超过30岁');
ELSE
DBMS_OUTPUT.PUT_LINE('年龄不超过30岁');
END IF;
INSERT INTO TEST.PROC_LOG(PROC_NAME, LOG_MSG) VALUES ('PROC_DEBUG_WITH_LOG', '执行结束');
COMMIT;
EXCEPTION
WHEN NO_DATA_FOUND THEN
INSERT INTO TEST.PROC_LOG(PROC_NAME, LOG_MSG) VALUES ('PROC_DEBUG_WITH_LOG', '异常: 未找到ID=' || p_id);
DBMS_OUTPUT.PUT_LINE('未找到ID=' || p_id);
WHEN OTHERS THEN
INSERT INTO TEST.PROC_LOG(PROC_NAME, LOG_MSG) VALUES ('PROC_DEBUG_WITH_LOG', '其他异常: ' || SQLERRM);
DBMS_OUTPUT.PUT_LINE('异常: ' || SQLERRM);
END;
/
CALL TEST.PROC_DEBUG_WITH_LOG(1);
SELECT * FROM TEST.PROC_LOG ORDER BY LOG_ID;
-- 删除存储过程
DROP PROCEDURE TEST.PROC_HELLO;
核心要点:
DBMS_OUTPUT.PUT_LINE 即时输出变量值和执行状态,需先执行 SET SERVEROUTPUT ON;EXCEPTION 块,可捕获 NO_DATA_FOUND、OTHERS 等异常文章
阅读量
获赞
