存储在数据库中供所有用户程序调用的子程序叫存储过程、函数
相同点:完成特定功能的程序
区别:
1)自定义函数可使用return语句返回值,过程不可
2)自定义函数不可进行insert、update、delete等对数据库进行写操作(除非使用自治事务),存储可以
3)自定义函数必须要有返回值
–创建或替换存储过程,as 后面为说明部分,as 相当于declare
create or replace procedure sayhelloworld
as
–说明部分
begin
–dbms_output.put()不换行输出,输出在缓冲区,不显示出来,直到执行put_line才一并输出。
–dbms_output.put_line()换行输出。但首先会输出缓冲区中的内容,然后清空缓冲区。
dbms_output.put_line(“Hello World”);
end;
调用存储过程:
1、call sayhelloworld();
2、存储中调用(以下为临时存储)
declare
begin
sayhelloworld();
end;
create or replace procedure sayhelloworld (V_DT DATE,V_NUM NUMBER , V_CHAR VARCHAR2 )
as
begin
dbms_output.put_line(V_DT);
dbms_output.put_line(V_NUM);
dbms_output.put_line(V_CHAR);
end;
调用存储过程:
1、call SAYHELLOWORLD(SYSDATE, 1, ‘HELLO WORLD’);
2、存储中调用(以下为临时存储)
declare
begin
SAYHELLOWORLD(SYSDATE, 1, ‘HELLO WORLD’);
end;
create or replace procedure sayhelloworld (V_DT DATE,V_NUM NUMBER , V_CHAR VARCHAR2
,V_OUT1 OUT VARCHAR2 ,V_OUT2 OUT NUMBER )
as
begin
dbms_output.put_line(V_DT);
dbms_output.put_line(V_NUM);
dbms_output.put_line(V_CHAR);
V_OUT1:=‘HI’;
V_OUT2:=0;
end;
调用存储过程:
DECLARE
V_CHAR VARCHAR2(20);
V_NUM NUMBER;
BEGIN
SAYHELLOWORLD(SYSDATE, 1, ‘HELLO WORLD’,V_CHAR,V_NUM);
dbms_output.put_line(V_CHAR);
dbms_output.put_line(V_NUM);
END;
CREATE OR REPLACE FUNCTION SAYHELLOWORLD RETURN VARCHAR2
IS
RESULT VARCHAR2(20);
BEGIN
RESULT:=‘Hello World’;
RETURN(RESULT);
END;
调用函数:
SELECT SAYHELLOWORLD() FROM DUAL;
CREATE OR REPLACE FUNCTION SAYHELLOWORLD1(V_DT DATE,V_NUM NUMBER , V_CHAR VARCHAR2
,V_OUT1 IN OUT VARCHAR2 ,V_OUT2 OUT NUMBER
) RETURN VARCHAR2
IS
RESULT VARCHAR2(20);
BEGIN
RESULT:=TO_CHAR(V_DT,‘YYYY’)||TO_CHAR(V_NUM)||V_CHAR;
V_OUT1:=‘WWW’;
V_OUT2:=10;
RETURN(RESULT);
END;
调用函数:
DECLARE
V_CHAR VARCHAR2(20):=‘KKK’;
V_CHAR1 VARCHAR2(20);
V_NUM NUMBER;
BEGIN
V_CHAR1:=SAYHELLOWORLD1(SYSDATE, 1, ‘HELLO WORLD’,V_CHAR,V_NUM);
dbms_output.put_line(V_CHAR||V_CHAR1);
dbms_output.put_line(1||V_NUM);
END;
删除存储:
DROP PROCEDURE Pname;
删除函数:
DROP FUNCTION Fname;
这种类型最简单,类似类型的一个别名,主要是为了对常用的一些类型简单化,它基于原始的某个类型。如:
有些应用会经常用到一些货币类型:number(16,2)。如果在全局范围各自定义这种类型,一旦需要修改该类型的精度,则需要一个个地修改。
那如何实现定义的全局化呢?于是就引出了子类型:
subtype cc_num is number(16,2);
create or replace type typ_calendar as object(
V_YEAR varchar2(8),
V_MONTH varchar2(8),
V_SUN varchar2(8),
V_MON varchar2(8),
V_TUS varchar2(8),
V_WEN varchar2(8),
V_THU varchar2(8),
V_FRI varchar2(8),
V_STA varchar2(8),
V_LAST varchar2(2)
);
create table tcalendar of typ_calendar;
insert into tcalendar select typ_calendar(‘2010’,‘05’,‘1’,‘2’,‘3’,‘4’,‘5’,‘6’,‘7’,‘31’) from dual;
这种类型包含了对类型中数据的内部处理,调用该类型时,可将处理后的数据返回给调用方。
CREATE OR REPLACE TYPE MSG_RECORD is object
(
flag varchar2(128),
message varchar2(4000),
msgdetail clob,
sql_a clob,
sql_u clob,
sql_d clob,
sql_t clob
)
CREATE OR REPLACE TYPE TAB_MSG is table of msg_record
CREATE OR REPLACE TYPE COLUMN_ARRAY IS TABLE OF VARCHAR2(30)
CREATE OR REPLACE TYPE TYPE_REPORT is object
(
/* Variables */
v_Code varchar2(6),
v_Name varchar2(512),
Check_Flag char(1),
Tab_Main varchar2(30),
Tab_Batch varchar2(30),
Tab_Tranlist varchar2(30),
Tab_Batch_In varchar2(30),
Tab_Tranlist_In varchar2(30),
Tab_BakDel varchar2(30),
Col_Batch column_array,
Col_Tranlist column_array,
/* Constructors */
constructor function TYPE_REPORT(v_Code varchar2,
v_CheckFlag char default 'N')
return self as result,
/* Member methods */
member function isCheckBalance return boolean
)
CREATE OR REPLACE TYPE BODY TYPE_REPORT as
/* Constructors */
constructor function TYPE_REPORT(v_Code varchar2, v_CheckFlag char default 'N') return self as result as
begin
self.v_Code := v_Code;
self.Check_Flag := v_CheckFlag;
select cd_desc, cd2, cd3, cd4
into self.v_Name, self.Tab_Batch, self.Tab_Tranlist, self.Tab_BakDel
from c_t_cd
where table_name = 'T_Table'
and cd1 = self.v_Code
;
self.Tab_Main := nvl(self.Tab_Batch, self.Tab_Tranlist);
self.Tab_Batch_In := self.v_Code || '_BATCH';
self.Tab_Tranlist_In := self.v_Code || '_TRANLIST';
select column_name BULK COLLECT INTO self.Col_Batch from dic_report where table_name = self.Tab_Batch;
select column_name BULK COLLECT INTO self.Col_Tranlist from dic_report where table_name = self.Tab_Tranlist;
return;
end;
/* Member methods */
member function isCheckBalance return boolean as
begin
return substr(self.v_Code,1,4) in ('111','222');
end;
end;
CREATE OR REPLACE FUNCTION F_GET_MESSAGE (v_Code char, v_Date char) return tab_msg is
PRAGMA AUTONOMOUS_TRANSACTION;
rpt type_report;
v_code1 char(6);
v_CR_LF char(2) := chr(10)||chr(13);
v_Flag varchar2(8) := '0'; -- 0-正常 1-失败
v_Message varchar2(4000);
v_MsgDetail clob;
v_SQL_A clob;
v_SQL_U clob;
v_SQL_D clob;
v_SQL_T clob;
v_tab_msg tab_msg := tab_msg();
begin
v_tab_msg.extend();
select cd1 into v_code1
from c_t_cd
where table_name = 'T_Table'
and (cd1 = v_Code or cd2 = v_Code or cd3 = v_Code)
;
rpt := type_report(v_code1);
if (rpt.isCheckBalance) then
v_SQL_A:='select * from emp';
v_SQL_u:='update emp t set t.deptno=''10'' where t.sal=2222';
v_SQL_d:='delete from emp';
v_SQL_t:='select * from emp t where t.sal=2222';
v_Message:='hehe';
v_MsgDetail:='lallalala';
goto return_function;
end if;
<<return_function>>
v_tab_msg(1) := msg_record(v_Flag, v_Message, v_MsgDetail, v_SQL_A, v_SQL_U, v_SQL_D,v_SQL_T);
return v_tab_msg;
exception when others then
v_tab_msg(1) := msg_record(SQLCODE, SQLERRM, v_SQL||v_CR_LF||DBMS_UTILITY.FORMAT_ERROR_BACKTRACE, null, null, null,null);
return v_tab_msg;
end;
select table(F_GET_MESSAGE('111', '2020-08-27')) from dual;
简化应用设计、提高应用性能、实现信息隐藏、子程序重载。
语法格式:
CREATE OR REPLACE PACKAGE package_name
/包头名称/
IS|AS
pl/sql_package_spec
/定义过程,函数以及返回类型,变量,常量及数据类型定义/
定义包头应当遵循以下原则:
1)包元素位置可以任意安排.然而在声明部分,对象必须在引用前进行声明.
2)包头可以不对任何类型的元素进行说明.例如,包头可以只带过程和函数说明语句,而不声明任何异常和类型.
3)对过程和函数的任何声明都必须只对子程序和其参数进行描述,不能有任何代码的说明,代码的实现只能在包体中出现.它不同于块声明,在块声明中,过程和函数的代码可同时出现在声明部分.
语法格式:
CREATE OR REPLACE PACKAGE BODY package_name
/包名必须与包头的包名一致/
IS | AS
pl/sql_package_body
/游标,函数,过程的具体定义/
包体是与包头相互独立的,包体只能在包头完成编译后才能进行编译.包体中带有包头中描述的子程序的具体实现的代码段.除此之外,包体还可以包括具有包体人全句属性的附加声明部分,但这些附加声明对于包头是不见的.
create or replace procedure AutoNomouse_Insert
is
PRAGMA AUTONOMOUS_TRANSACTION;
begin
insert into Msg values(‘AutoNomouse Insert’);
commit;
end;
create or replace function AutoNomouse_Insert return VARCHAR2
is
PRAGMA AUTONOMOUS_TRANSACTION;
RESULT INTEGER;
begin
insert into Msg values(‘AutoNomouse Insert’);
RESULT:=SQL%ROWCOUNT;
commit;
RETURN(RESULT);
end;
ora-06519 – 检查到活动自治事务,回滚——退出自治事务时没有提交、回滚或ddl操作
ora-14450 – 试图访问正在使用的事务级临时表
ora-00060 – 等待资源时检查到死锁
DECLARE
out_return varchar2(1000);
val int; --全局变量
errorException exception; --申明异常
errorCode number; --异常编码
errorMsg varchar2(1000); --异常信息
flag varchar2(10);
begin
flag := 'true';
out_return := 'flag=' || flag || ',errorCode=' || errorCode || ',errorMsg=' || errorMsg;
val := 1/0;
exception
--异常捕捉,不要把有需要的代码放在异常捕捉后面,有异常才会执行异常代码下所有代码,没有异常不会执行
when errorException then
errorCode := SQLCODE;
errorMsg := SUBSTR(SQLERRM, 1, 200);
flag := 'false';
out_return := 'flag=' || flag || ',errorCode=' || errorCode || ',errorMsg=' || errorMsg;
when others then
errorCode := SQLCODE;
errorMsg := SUBSTR(SQLERRM, 1, 200);
flag := 'false';
out_return := 'flag=' || flag || ',errorCode=' || errorCode || ',errorMsg=' || errorMsg;
dbms_output.put_line(out_return);
end;
exception | oracle error | condition |
---|---|---|
no_data_found | ora-01403 | select into 语句没有符合条件的记录返回 |
too_many_rows | ora-01422 | select into 语句符合条件的记录有多条返回 |
dup_val_on_index | ora-00001 | 对于数据库表中的某一列,该列已经被限制为唯一索引,程序试图存储两个重复的值 |
value_error | ora-06502 | 在转换字符类型,截取或长度受限时,会发生该异常,如一个字符分配给一个变量,而该变量声明的长度比该字符短,就会引发该异常 |
storage_error | ora-06500 | 内存溢出 |
zero_divide | ora-01476 | 除数为零 |
case_not_found | ora-06592 | 对于选择case语句,没有与之相匹配的条件,同时,也没有else语句捕获其他的条件 |
cursor_already_open | ora-06511 | 程序试图打开一个已经打开的游标 |
timeout_on_resource | ora-00051 | 系统在等待某一资源,时间超时 |
declare
v_table varchar2(20);
v_table_1 varchar2(20);
v_sql Varchar2(100);
Begin
v_table:='comm';
v_sql:='select sex from '||v_table;
dbms_output.put_line(v_table);
dbms_output.put_line(v_sql);
Execute Immediate v_sql ;
Execute Immediate 'insert into comme select * from comm';
commit;
End;
Declare
v_sid Integer:=20170117;
v_sql Varchar2(100);
v_result Varchar2(50):='111111';
Begin
v_sid:=12;
v_sql:='Select count(*) from comme d where d.sex=:1';
dbms_output.put_line(v_sql);
Execute Immediate v_sql into v_result
using 2;
dbms_output.put_line(v_result);
End;
declare
Begin
Execute Immediate 'insert into comm values (:1,:2,:3)'
Using 'IT',2,3221;
Commit;
End;
CREATE TABLE t1 (id NUMBER(10),description VARCHAR2(50),CONSTRAINT t1_pk PRIMARY KEY (id));
CREATE SEQUENCE t1_seq;
INSERT INTO t1 VALUES (t1_seq.nextval, 'ONE');
INSERT INTO t1 VALUES (t1_seq.nextval, 'TWO');
INSERT INTO t1 VALUES (t1_seq.nextval, 'THREE');
declare
l_id t1.id%type;
begin
insert into t1 values(t1_seq.nextval,'four')
returning id into l_id;
commit;
dbms_output.put_line('id='||l_id);
end;
declare
type sallist is table of comm.population%type;
sal sallist;
begin
select population bulk collect into sal from comm
where rownum<=100;
select population bulk collect into sal from comm sample (10);
end;
declare
type depttab is table of come%rowtype;
dept_t depttab;
cursor c1 is
select eno,color,name from come where eno>107;
begin
open c1;
fetch c1 bulk collect into dept_t;
close c1;
end;
create or replace procedure test(emo in number)
as
begin
if emo>100 then
dbms_output.put_line(emo+100);
else
dbms_output.put_line(emo-100);
end if;
end;
create or replace procedure test
as
cursor s_cursor is select country from comm;
country1 s_cursor%rowtype;
begin
for country1 in s_cursor loop
dbms_output.put_line(country1.country);
end loop;
end;
create or replace procedure test(i in number)
as
type varr is varray(10) of varchar2(20);
vaar varr;
begin
vaar:=varr('she','her','he','him','me');
for i in 1..vaar.count loop
dbms_output.put_line(vaar(i));
end loop;
end;
create or replace procedure test(i in number)
as
type v_table is table of varchar2(20) index by binary_integer;
m_table v_table;
begin
for i in 1..20 loop
m_table(i):=i;
dbms_output.put_line(m_table(i));
end loop;
end;
create or replace procedure test(i in number)
as
type v_table is table of emp%rowtype index by binary_integer;
m_table v_table;
begin
select * bulk collect into m_table from emp;
for i in 1..m_table.count/2 loop
dbms_output.put_line(m_table(i).deptno);
dbms_output.put_line(m_table(i).sal);
end loop;
end;
create or replace procedure test
as
type v_table is table of emp%rowtype index by binary_integer;
m_table v_table;
begin
select * into m_table(1) from emp where sal=2450;
dbms_output.put_line(m_table(1).deptno);
end;
create or replace type varr as varray(30) of varchar2(30);
create or replace procedure test(vaar in varr)
as
str varchar2(30);
begin
for i in 1..vaar.count loop
str:=vaar(i);
dbms_output.put_line('str='||str);
dbms_output.put_line('vaar=('||i||')='||vaar(i));
end loop;
end;
create or replace procedure test
as
i number(3,0);
begin
i:=1;
while i<10 loop
i:=i+1;
dbms_output.put_line(i);
end loop;
end;
create or replace procedure test(cur out sys_refcursor)
as
curr sys_refcursor;
name varchar2(20);
begin
open curr for select ename from emp where deptno=20;
loop
fetch curr into name;
exit when curr%notfound;
dbms_output.put_line(name);
end loop;
cur:=curr;
close curr;
end;
create or replace procedure test
as
cursor vcur is select country from comme;
vvcur vcur%rowtype;
begin
for vvcur in vcur loop
dbms_output.put_line(vvcur.country);
end loop;
end;
create or replace procedure test
as
cursor curr(countr varchar2,popu number) is
select * from comme where country=countr and sex=popu;
begin
for person in curr('China',2) loop
dbms_output.put_line(person.population);
end loop;
end;
create or replace procedure test
as
type cur is ref cursor return comme%rowtype;
comcur cur;
comrecord comme%rowtype;
begin
open comcur for select * from comme;
loop
fetch comcur into comrecord;
exit when comcur%notfound;
dbms_output.put_line('country is :'||comrecord.country||' and population is :'||comrecord.population);
end loop;
close comcur;
end;
create or replace procedure test
as
type comcur is ref cursor;
comcurs comcur;
cumrecord comme%rowtype;
comrecord come%rowtype;
begin
open comcurs for select * from comme;
loop
fetch comcurs into cumrecord;
exit when comcurs%notfound;
dbms_output.put_line(cumrecord.country);
end loop;
close comcurs;
open comcurs for select * from come;
loop
fetch comcurs into comrecord;
exit when comcurs%notfound;
dbms_output.put_line(comrecord.eno);
end loop;
close comcurs;
end;
create or replace procedure test
as
com_cur sys_refcursor;
currecord comme%rowtype;
ccurrecord come%rowtype;
begin
open com_cur for select * from comme;
loop
fetch com_cur into currecord;
exit when com_cur%notfound;
dbms_output.put_line(currecord.sex);
end loop;
close com_cur;
open com_cur for select * from come;
loop
fetch com_cur into ccurrecord;
exit when com_cur%notfound;
dbms_output.put_line(ccurrecord.name);
end loop;
end;
create or replace procedure test
as
type cur is ref cursor;
type cor is ref cursor;
enam emp.ename%type;
dnam dept.dname%type;
curr cur;
coor cor;
begin
open curr for
select d.dname,
cursor(select e.ename from emp e where e.deptno=d.deptno)
from dept d;
loop
fetch curr into dnam,coor;
exit when curr%notfound;
dbms_output.put_line('dname is :'||dnam);
loop
fetch coor into enam;
exit when coor%notfound;
dbms_output.put_line('ename is :'||enam);
end loop;
end loop;
close curr;
end;
declare
eno varchar2(20);
bbb varchar2(20);
b varchar2(20);
a varchar2(20);
begin
if ('' || '123') is not null then
begin
eno:='||';
end;
end if;
if (a='' and b='123') is null then
begin
bbb:='and';
end;
end if;
dbms_output.put_line(bbb);
dbms_output.put_line(eno);
end;
exit:跳出整个循环,后续内容还可执行
return:直接结束,后续内容不执行
continue:跳出本次循环,后续循环仍可执行
goto:直接跳到指定位置执行其后语句
declare
l_count number;
l_test number := 2;
begin
dbms_output.put_line('开始循环');
for l_count in 1 .. 3 loop
if l_test = l_count then
dbms_output.put_line('满足条件,退出循环');
EXIT;
else
dbms_output.put_line('继续循环');
end if;
end loop;
dbms_output.put_line('结束程序');
end;
结果如下:
开始循环
继续循环
满足条件,退出循环
结束程序
declare
l_count number;
l_test number := 2;
begin
dbms_output.put_line('开始循环');
for l_count in 1 .. 3 loop
if l_test = l_count then
dbms_output.put_line('满足条件,退出循环');
return;
else
dbms_output.put_line('继续循环');
end if;
end loop;
dbms_output.put_line('结束程序');
end;
结果如下:
开始循环
继续循环
满足条件,退出循环
declare
l_count number;
l_test number := 2;
begin
dbms_output.put_line('开始循环');
for l_count in 1 .. 3 loop
if l_test = l_count then
dbms_output.put_line('满足条件,退出循环');
continue;
else
dbms_output.put_line('继续循环');
end if;
end loop;
dbms_output.put_line('结束程序');
end;
结果如下:
开始循环
继续循环
满足条件,退出循环
继续循环
结束程序
begin
dbms_output.put_line('开始循环');
for l_count in 1 .. 3 loop
if l_test = l_count then
dbms_output.put_line('满足条件,退出循环');
goto print_now;
else
dbms_output.put_line('继续循环');
end if;
end loop;
dbms_output.put_line('结束程序');
<<print_now>>
dbms_output.put_line(sysdate);
end;
结果如下:
开始循环
继续循环
满足条件,退出循环
27-8月 -20