存储过程/存储函数
📄字数 6.4K
👁️阅读量 加载中...
在 PL/SQL 中,存储过程(Procedure) 和 存储函数(Function) 是两种封装业务逻辑的子程序,它们的主要区别在于:函数必须返回一个值,而存储过程不返回值(但可通过输出参数返回数据)。
存储过程/函数的创建、删除重编译
查看存储过程/函数
存储过程和函数保存在数据库的字典中,几个常用的字典视图如下:
SYS_PROCEDURES:sys_procedures 系统表用于存储、管理系统库和用户库中所有创建的存储函数和过程信息,查看需在系统库下有系统权限。
ALL_PROCEDURES:all_procedures 系统视图用于存储和管理创建的所有存储函数和过程信息。
DBA__PROCEDURES:dba_procedures 系统视图用于存储和管理当前库中所有创建的存储函数和过程信息,查看需要在当前库下且有查看权限。
USER_PROCEDURES:user_procedures 系统视图用于查询当前用户创建的所有存储函数和过程信息。
示例
sql
-- 查看sys_procedures
SQL> select * from sys_procedures;
+-------+-----------+---------+---------+-----------------------+----------+----------+--------+--------------------------+-------+----------+--------+-----------+-----------+-----------+
| DB_ID | SCHEMA_ID | USER_ID | PROC_ID | PROC_NAME | LANGUAGE | RET_TYPE | DEFINE | CREATE_TIME | VALID | COMMENTS | IS_SYS | PIPELINED | RESERVED1 | RESERVED2|
+-------+-----------+---------+---------+-----------------------+----------+----------+--------+--------------------------+-------+----------+--------+-----------+-----------+-----------+
| 1 | 1 | 1 | 1048628 | RAND_VALUE | PLSQL | BIGINT | <CLOB> | 2025-07-09 18:54:03.912 | T | <NULL> | F | F | <NULL> | <NULL>|
| 1 | 1 | 1 | 1048637 | PROC_TEST_DISASSEMBLE | PLSQL | <NULL> | <CLOB> | 2025-07-11 11:42:12.161 | T | <NULL> | F | F | <NULL> | <NULL>|
| 1 | 1 | 1 | 1048639 | FUNC_TEST_DISASSEMBLE | PLSQL | INTEGER | <CLOB> | 2025-07-11 11:42:12.171 | T | <NULL> | F | F | <NULL> | <NULL>|
+-------+-----------+---------+---------+-----------------------+----------+----------+--------+--------------------------+-------+----------+--------+-----------+-----------+-----------+
-- 查看all_procedures
SQL> select * from all_procedures;
+-------+-----------+---------+---------+-----------------------+----------+----------+--------+--------------------------+-------+----------+--------+-----------+-----------+-----------+
| DB_ID | SCHEMA_ID | USER_ID | PROC_ID | PROC_NAME | LANGUAGE | RET_TYPE | DEFINE | CREATE_TIME | VALID | COMMENTS | IS_SYS | PIPELINED | RESERVED1 | RESERVED2|
+-------+-----------+---------+---------+-----------------------+----------+----------+--------+--------------------------+-------+----------+--------+-----------+-----------+-----------+
| 1 | 1 | 1 | 1048628 | RAND_VALUE | PLSQL | BIGINT | <CLOB> | 2025-07-09 18:54:03.912 | T | <NULL> | F | F | <NULL> | <NULL>|
| 1 | 1 | 1 | 1048637 | PROC_TEST_DISASSEMBLE | PLSQL | <NULL> | <CLOB> | 2025-07-11 11:42:12.161 | T | <NULL> | F | F | <NULL> | <NULL>|
| 1 | 1 | 1 | 1048639 | FUNC_TEST_DISASSEMBLE | PLSQL | INTEGER | <CLOB> | 2025-07-11 11:42:12.171 | T | <NULL> | F | F | <NULL> | <NULL>|
+-------+-----------+---------+---------+-----------------------+----------+----------+--------+--------------------------+-------+----------+--------+-----------+-----------+-----------+
-- 查看dba_procedures
SQL> select * from dba_procedures;
+-------+-----------+---------+---------+-----------------------+----------+----------+--------+--------------------------+-------+----------+--------+-----------+-----------+-----------+
| DB_ID | SCHEMA_ID | USER_ID | PROC_ID | PROC_NAME | LANGUAGE | RET_TYPE | DEFINE | CREATE_TIME | VALID | COMMENTS | IS_SYS | PIPELINED | RESERVED1 | RESERVED2|
+-------+-----------+---------+---------+-----------------------+----------+----------+--------+--------------------------+-------+----------+--------+-----------+-----------+-----------+
| 1 | 1 | 1 | 1048628 | RAND_VALUE | PLSQL | BIGINT | <CLOB> | 2025-07-09 18:54:03.912 | T | <NULL> | F | F | <NULL> | <NULL>|
| 1 | 1 | 1 | 1048637 | PROC_TEST_DISASSEMBLE | PLSQL | <NULL> | <CLOB> | 2025-07-11 11:42:12.161 | T | <NULL> | F | F | <NULL> | <NULL>|
| 1 | 1 | 1 | 1048639 | FUNC_TEST_DISASSEMBLE | PLSQL | INTEGER | <CLOB> | 2025-07-11 11:42:12.171 | T | <NULL> | F | F | <NULL> | <NULL>|
+-------+-----------+---------+---------+-----------------------+----------+----------+--------+--------------------------+-------+----------+--------+-----------+-----------+-----------+
-- 查看user_procedures
SQL> select * from user_procedures;
+-------+-----------+---------+---------+-----------------------+----------+----------+--------+--------------------------+-------+----------+--------+-----------+-----------+-----------+
| DB_ID | SCHEMA_ID | USER_ID | PROC_ID | PROC_NAME | LANGUAGE | RET_TYPE | DEFINE | CREATE_TIME | VALID | COMMENTS | IS_SYS | PIPELINED | RESERVED1 | RESERVED2|
+-------+-----------+---------+---------+-----------------------+----------+----------+--------+--------------------------+-------+----------+--------+-----------+-----------+-----------+
| 1 | 1 | 1 | 1048628 | RAND_VALUE | PLSQL | BIGINT | <CLOB> | 2025-07-09 18:54:03.912 | T | <NULL> | F | F | <NULL> | <NULL>|
| 1 | 1 | 1 | 1048637 | PROC_TEST_DISASSEMBLE | PLSQL | <NULL> | <CLOB> | 2025-07-11 11:42:12.161 | T | <NULL> | F | F | <NULL> | <NULL>|
| 1 | 1 | 1 | 1048639 | FUNC_TEST_DISASSEMBLE | PLSQL | INTEGER | <CLOB> | 2025-07-11 11:42:12.171 | T | <NULL> | F | F | <NULL> | <NULL>|
+-------+-----------+---------+---------+-----------------------+----------+----------+--------+--------------------------+-------+----------+--------+-----------+-----------+-----------+
执行存储过程/函数
语法格式
ExecuteFuncStmt 为执行存储过程或函数语法;
ExecuteImmediateStmt 则是执行动态SQL语法。
- func_name:存储过程名、存储函数名、包中定义的存储过程名或存储函数名,其中如果调用包中存储过程或存储函数则需加包名;
- func_params:过程/函数参数列表;
- expr:要执行的动态SQL表达式,可为字符串类型变量,也可为字符串常量;
- USING expr_list:在执行动态SQL语句时对参数列表进行绑定,expr_list 参数列表与动态SQL语句中的占位符进行绑定;
- INTO ident_list:标识符链,将动态SQL的执行返回结果存入标识符中;
- BULK COLLECT:单个集合变量或多个集合变量的多记录可使用 BULK COLLECT;
- INCONST:可限制动态SQL查询结果条数。
说明
以上语法已经包含了块语句执行的语法,所以示例中也会包含块语句的执行范例
示例
sql
-- 创建无参存储过程/函数
SQL> CREATE TABLE test_proc_tab(id INT,dtime TIME);
SQL> CREATE OR REPLACE PROCEDURE proc_test() IS
loop_num INT;
BEGIN
loop_num := 0;
FOR i IN 1 .. 10 LOOP
INSERT INTO test_proc_tab (id, dtime) VALUES (i, current_time);
loop_num := loop_num + 1;
END LOOP;
SEND_MSG('过程执行完成' || loop_num || '次');
COMMIT;
END;
/
--执行存储过程/函数
SQL> EXEC proc_test();
SQL> CALL proc_test();
-- 创建有参存储过程/函数
SQL> CREATE OR REPLACE PROCEDURE proc_test2(parameter INTEGER) AS
x int;
BEGIN
x := 0;
FOR i IN 1 .. parameter LOOP
INSERT INTO test_proc_tab VALUES (i, sysdate);
x := x + SQL%ROWCOUNT;
END LOOP;
SEND_MSG('共插入:' || x || '次');
COMMIT;
END;
/
--执行存储过程/函数
SQL> EXEC proc_test2(3);
SQL> CALL proc_test2(3);
SQL> CREATE TABLE ttt(id int);
-- EXECUTE IMMEDIATE
SQL> DECLARE
NUM INT:=100;
BEGIN
EXECUTE IMMEDIATE 'SELECT COUNT(*) FROM USER_TABLES WHERE TABLE_NAME=''TTT''';
DBMS_OUTPUT.PUT_LINE(NUM);
END;
/
100
+-------+
| EXPR1 |
+-------+
| 1 |
+-------+
-- RETURNING INTO USING
SQL> DECLARE
NUM INT;
BEGIN
EXECUTE IMMEDIATE 'SELECT COUNT(*) FROM USER_TABLES WHERE TABLE_NAME=:TABLE_NAME ' RETURNING INTO NUM USING 'TTT';
DBMS_OUTPUT.PUT_LINE(NUM);
END;
/
1
-- USING RETURNING INTO
SQL> DECLARE
NUM INT;
BEGIN
EXECUTE IMMEDIATE 'SELECT COUNT(*) FROM USER_TABLES WHERE TABLE_NAME=:TABLE_NAME ' USING 'TTT' RETURNING INTO NUM ;
DBMS_OUTPUT.PUT_LINE(NUM);
END;
/
1
-- RETURNING INTO
SQL> DECLARE
NUM INT;
BEGIN
EXECUTE IMMEDIATE 'SELECT COUNT(*) FROM USER_TABLES WHERE TABLE_NAME=''TTT''' RETURNING INTO NUM ;
DBMS_OUTPUT.PUT_LINE(NUM);
END;
/
1
-- USING INTO
SQL> DECLARE
NUM INT;
BEGIN
EXECUTE IMMEDIATE 'SELECT COUNT(*) FROM USER_TABLES WHERE TABLE_NAME=:TABLE_NAME ' USING 'TTT' INTO NUM ;
DBMS_OUTPUT.PUT_LINE(NUM);
END;
/
1
-- INTO USING
SQL> DECLARE
NUM INT;
BEGIN
EXECUTE IMMEDIATE 'SELECT COUNT(*) FROM USER_TABLES WHERE TABLE_NAME=:TABLE_NAME ' INTO NUM USING 'TTT' ;
DBMS_OUTPUT.PUT_LINE(NUM);
END;
/
1
-- INTO
SQL> DECLARE
NUM INT;
BEGIN
EXECUTE IMMEDIATE 'SELECT COUNT(*) FROM USER_TABLES WHERE TABLE_NAME=''TTT'' ' INTO NUM ;
DBMS_OUTPUT.PUT_LINE(NUM);
END;
/
1
-- USING
SQL> DECLARE
NUM INT:=100;
BEGIN
EXECUTE IMMEDIATE 'SELECT COUNT(*) FROM USER_TABLES WHERE TABLE_NAME=:TABLE_NAME' USING 'TTT';
DBMS_OUTPUT.PUT_LINE(NUM);
END;
/
100
+-------+
| EXPR1 |
+-------+
| 1 |
+-------+
-- RETURNING
SQL> DECLARE
NUM INT:=100;
BEGIN
EXECUTE IMMEDIATE 'SELECT COUNT(*) FROM USER_TABLES WHERE TABLE_NAME=:TABLE_NAME ' RETURNING ;
DBMS_OUTPUT.PUT_LINE(NUM);
END;
/
Error: [E19132] 语法错误
-- USING RETURNING
SQL> DECLARE
NUM INT:=100;
BEGIN
EXECUTE IMMEDIATE 'SELECT COUNT(*) FROM USER_TABLES WHERE TABLE_NAME=:TABLE_NAME ' USING 'TTT' RETURNING ;
DBMS_OUTPUT.PUT_LINE(NUM);
END;
/
Error: [E19132] 语法错误
-- RETURNING USING
SQL> DECLARE
NUM INT;
BEGIN
CALL IMMEDIATE 'SELECT COUNT(*) FROM USER_TABLES WHERE TABLE_NAME=:TABLE_NAME' RETURNING USING 'TTT';
DBMS_OUTPUT.PUT_LINE(NUM);
END;
/
Error: [E19132] 语法错误
-- CALL IMMEDIATE
SQL> DECLARE
NUM INT;
BEGIN
CALL IMMEDIATE 'SELECT COUNT(*) FROM USER_TABLES WHERE TABLE_NAME=:TABLE_NAME ' RETURNING INTO NUM USING 'TTT';
DBMS_OUTPUT.PUT_LINE(NUM);
END;
/
1
-- returning bulk collect
SQL> create table t1(c1 int, c2 varchar, c3 date);
SQL> create table t2(c1 varchar, c2 int, c3 varchar, c4 date);
SQL> create table t3(c1 varchar, c2 varchar)
SQL> create table t_msg(c1 varchar, c2 varchar);
SQL> insert into t1 values(1, 'a', '2001-01-01');
SQL> insert into t1 values(2, 'b', '2012-02-02');
SQL> insert into t1 values(3, 'c', '2023-03-03');
SQL> insert into t1 values(4, 'd', '2034-04-04');
SQL> declare
v_sql varchar;
v_msg varchar;
type type_c1 is table of t1.c1%type;
type type_c2 is table of t1.c2%type;
type type_c3 is table of t1.c3%type;
v1 type_c1;
v2 type_c2;
v3 type_c3;
begin
v_sql := 'select * from t1';
v_msg := 'execute immediate sql returning bulk collect into';
execute immediate v_sql returning bulk collect into v1, v2, v3;
for i in 1..4 loop
insert into t2 values(v_msg, v1(i), v2(i), v3(i));
end loop;
end;
/
SQL> select * from t2;
+---------------------------------------------------+----+----+-------------+
| C1 | C2 | C3 | C4 |
+---------------------------------------------------+----+----+-------------+
| execute immediate sql returning bulk collect into | 1 | a | 2001-01-01 |
| execute immediate sql returning bulk collect into | 2 | b | 2012-02-02 |
| execute immediate sql returning bulk collect into | 3 | c | 2023-03-03 |
| execute immediate sql returning bulk collect into | 4 | d | 2034-04-04 |
+---------------------------------------------------+----+----+-------------+
-- bulk collect
SQL> declare
v_sql varchar;
v_msg varchar;
v_i int;
type type_c3 is table of date;
v3 type_c3;
begin
v_sql := 'select c3 from t1 where c1 < ?';
v_msg := 'execute immediate sql using bulk collect into';
v_i := 3;
execute immediate v_sql using v_i bulk collect into v3;
for i in 1..2 loop
insert into t2(c1, c4) values(v_msg, v3(i));
end loop;
end;
/
SQL> select * from t2;
+---------------------------------------------------+--------+--------+-------------+
| C1 | C2 | C3 | C4 |
+---------------------------------------------------+--------+--------+-------------+
| execute immediate sql returning bulk collect into | 1 | a | 2001-01-01 |
| execute immediate sql returning bulk collect into | 2 | b | 2012-02-02 |
| execute immediate sql returning bulk collect into | 3 | c | 2023-03-03 |
| execute immediate sql returning bulk collect into | 4 | d | 2034-04-04 |
| execute immediate sql using bulk collect into | <NULL> | <NULL> | 2001-01-01 |
| execute immediate sql using bulk collect into | <NULL> | <NULL> | 2012-02-02 |
+---------------------------------------------------+--------+--------+-------------+
-- limit
SQL> declare
v_sql varchar;
v_msg varchar;
v1 int;
v2 varchar;
v3 date;
begin
v_sql := 'select * from t1';
v_msg := 'call immediate sql returning into limit';
call immediate v_sql returning into v1, v2, v3 limit 1;
insert into t2 values(v_msg, v1, v2, v3);
end;
/
SQL> select * from t2;
+---------------------------------------------------+--------+--------+-------------+
| C1 | C2 | C3 | C4 |
+---------------------------------------------------+--------+--------+-------------+
| execute immediate sql returning bulk collect into | 1 | a | 2001-01-01 |
| execute immediate sql returning bulk collect into | 2 | b | 2012-02-02 |
| execute immediate sql returning bulk collect into | 3 | c | 2023-03-03 |
| execute immediate sql returning bulk collect into | 4 | d | 2034-04-04 |
| execute immediate sql using bulk collect into | <NULL> | <NULL> | 2001-01-01 |
| execute immediate sql using bulk collect into | <NULL> | <NULL> | 2012-02-02 |
| call immediate sql returning into limit | 4 | d | 2034-04-04 |
+---------------------------------------------------+--------+--------+-------------+