exception
异常处理
end
exception
异常处理
end;
第一类 :PL/SQL 变量
(DECLARE 阶段别声明和初始化)
(每一行只声明一个变量)
(在执行阶段被赋予新值)
(可以在PL/SQL之间传递)
(通过标准输出包可以看到结果)
表汇总的字段数据类型
varchar2 4000
char 2000
long 2G
SQL> declare
2 v_1 number;
3 v_2 number(2);
4 v_3 int;
5 v_4 varchar2(20) default 'abc';
6 v_5 constant varchar2(20) :='zyx';
7 v_6 number not null :=120;
8 v_7 boolean :=false;
9 v_8 rowid;
10 begin
11 null;
12 v_1:=1.232434;
13 v_1 :=200;
14 end;
15 /
PL/SQL 过程已成功完成。
SQL>
SQL> ed
已写入 file afiedt.buf
1 declare
2 empno number;
3 v_2 number(2);
4 v_3 int;
5 v_4 varchar2(20) default 'abc';
6 v_5 constant varchar2(20) :='zyx';
7 v_6 number not null :=120;
8 v_7 boolean :=false;
9 v_8 rowid;
10 begin
11 null;
12 empno:=7788;
13 delete emp where empno=empno;
14* end;
SQL> /
PL/SQL 过程已成功完成。
SQL> select * from emp;
未选定行
SQLPLUS 或者「命令窗口」中如果没有输出,可以使用 set serveroutp on 来修改 是否能输出
ed
已写入 file afiedt.buf
1 declare
2 empno number;
3 v_2 number(2);
4 v_3 int;
5 v_4 varchar2(20) default 'abc';
6 v_5 constant varchar2(20) :='zyx';
7 v_6 number not null :=120;
8 v_7 boolean :=false;
9 v_8 rowid;
10 begin
11 null;
12 empno :=7788;
13 v_2:=1;
14 dbms_output.put_line('变量v2的值是:'|| v_2); 这里也可以写sysdate
15* end;
SQL> /
变量v2的值是:1
PL/SQL 过程已成功完成。
也可以缓存v_4 v_5
已写入 file afiedt.buf
1 declare
2 v_var1 number:=123;
3 begin
4 dbms_output.put_line('OUT:v_var1:'||v_var1);
5 declare
6 v_var2 number:=456;
7 begin
8 dbms_output.put_line('IN:v_var1:'||v_var1);
9 dbms_output.put_line('IN:v_var2:'||v_var2);
10 END;
11 --dbms_output.put_line('IN:v_var2:'||v_var2); 内部中的变量不能被外部块使用,去掉注释就报错
12* end;
SQL> /
OUT:v_var1:123
IN:v_var1:123
IN:v_var2:456
PL/SQL 过程已成功完成。
外部的变量可以内部使用,但是内部的变量外部不能使用
SQL> ed
已写入 file afiedt.buf
1 declare
2 v_out varchar2(20) :='this is out';
3 begin
4 dbms_output.put_line(v_out);
5 declare
6 v_inner varchar2(20) :='this is inner';
7 begin
8 v_out:='abc'; --这里还可以重新赋值
9 dbms_output.put_line(v_out);
10 dbms_output.put_line(v_inner);
11 end;
12* end;
SQL> /
this is out
abc
this is inner
PL/SQL 过程已成功完成。
语句库的标签可以加在一个语句块开始的位置上 当外部块和内部块出现同命名变量时 借助标签 来区别 但是这样的程序可读性太差 尽量不要使用变量名来重复
在PL sql 里面嵌入 sql 语句 使用SELECT 查询结果对变量赋值
PL/SQL 中的select 必须使用into 将选出结果存入变量之内
SQL> ed
已写入 file afiedt.buf
1 declare
2 v_sal number ;
3 v_job varchar2(20);
4 begin
5 select sal , job into v_sal,v_job from emp where empno=7788;
6 dbms_output.put_line(v_sal||','||v_job);
7* end;
SQL> /
3000,ANALYST
PL/SQL 过程已成功完成。
使用 %type 参照方式定义变量的类型
有时在程序中声明的变量 很多时候是为了存放表中的列数据 这样我们自己定义变量类型时往往都是和表中的类型以及长度一致的 %type 就可以避免复杂的输入 直接可以参照表的某列的类型直接作为变量的类型这样即使表中的类型被修改了 程序中的类型也随之改变 程序的健壮性提高
SQL> declare
2 v_ename scott.emp.ename%type;
3 begin
4 select ename into v_ename from scott.emp where empno=7788;
5 dbms_output.put_line(v_ename);
6 end;
7 /
SCOTT
PL/SQL 过程已成功完成。
2 复合变量
记录 (record)
使用方法
1 先定义类型,类型中国描述了这是一个 RECORD
2 但却没有 说明内部 具体列的内容
定义自定义 类型就是为了 定义 record 内部 包含什么列
3 在将定义好的类型 关联一个变量
引用 record 类型中的值 记录名 内部列名
declare
2 type rec_typ is record(
3 v_ename emp.ename%type,
4 v_job emp.job%type,
5 v_sal emp.sal%type);
6
7 v_rec rec_typ;
8 begin
9 select ename,job,sal into v_rec.v_ename,v_rec.v_job,v_rec.v_sal from emp where
10 empno=&no;
11 dbms_output.put_line(
12 'his name is'||v_rec.v_ename||','||chr(10)||
13 'his job is'||v_rec.v_job||','||chr(10)||
14 'his name is'||v_rec.v_sal||','
15 );
16 end;
17 /
输入 no 的值: 7788
原值 10: empno=&no;
新值 10: empno=7788;
his name isSCOTT,
his job isANALYST,
his name is3000,
PL/SQL 过程已成功完成。
使用8个变量在一行
使用 %rowtype 来代替 record 的类型定义
和 %type 类型 %type是参照一列的类型
而 %rowtype 是将表的所有列做为了record中的成员
列名即是成员名
此时当然也可以使用 加入sal job ename
PL/SQL 表 (INDEX BY 表)
类似于属组,但下标除了定义成数字外还可以定义为字符,定义成数值下标时必须要使用
binary_integer 类型 binary_integer 类型相比 number 类型区别 :
存储的数据以二进制方式存储 占用更少的空间
可以存储 -2147483747- 2147483747 之间的任意整数
主要使用在PL/SQL 表的下标类型撒花姑娘不能出现在常规的类型上反之 PL/SQL 表的下标也不能使用number 来取代
使用方法
1 先定义类型,指出下面是数字还是字符
2 在类型关联变量
引用 PL/SQL 表中的元素 :表名 (下标)
SQL> ed
已写入 file afiedt.buf
1 declare
2 type tab_typ is table of varchar2(20) index by binary_integer ;
3 v_tab tab_typ;
4 begin
5 v_tab(1):='jason';
6 v_tab(2):='jack';
7 select ename into v_tab(3) from emp where empno=7788;
8 dbms_output.put_line(v_tab(1)||',' ||v_tab(2)||','||v_tab(3));
9* end;
SQL> /
jason,jack,SCOTT
PL/SQL 过程已成功完成。
也可以修改里面的 变量值
SQL> ed
已写入 file afiedt.buf
1 declare
2 type tab_typ is table of varchar2(20) index by varchar2(1);
3 v_tab tab_typ;
4 begin
5 v_tab('a'):='jason';
6 v_tab('b'):='jack';
7 select ename into v_tab('c') from emp where empno=7788;
8 dbms_output.put_line(v_tab('a')||',' ||v_tab('b')||','||v_tab('c'));
9 dbms_output.put_line(v_tab.first);
10 dbms_output.put_line(v_tab.last);
11 dbms_output.put_line(v_tab.prior('b'));
12 /*
13 前一个下标
14 */
15 dbms_output.put_line(v_tab.next('b'));
16 /*
17 b 的下一个下标
18 */
19 dbms_output.put_line(v_tab.count);
20* end;
PL/SQL 表的缺陷是:
of 类型的定义比较死板
定义的类型是字符 所有值就只能存储字符
定义的累累是数字 所有值就只能存储数字
解决办法是使用PL/SQL 表+ record 可以随心所欲的存不同类型值
PL/SQL 表+record
PL/SQL 中不能直接使用select 需要使用 select into 将查询结果插入到标量在
dms_output打印标量
1 先定义record 类型,声明类型中包含哪些列
2 在定义PL/SQL 类型 PL/SQL 表类型关联之前定义的record 类型
3 这样就将 record 作为了PL/SQL 表中的元素
4 在将PL/SQL 类型关联到变量中
引用方法 :
赋值 :PL/SQL 表名 下标
取值: PL/SQL 表名(下标).record 的列名
SQL> ed
已写入 file afiedt.buf
1 declare
2 type rec_typ is record(
3 v_ename varchar2(20),
4 v_sal number);
5 type tab_typ is table of rec_typ index by binary_integer;
6 /*
7 默认为整形
8 */
9 v_tab tab_typ;
10 begin
11 v_tab(1).v_ename:='jack';
12 v_tab(1).v_sal:=3000;
13 select ename,sal into v_tab(2).v_ename,v_tab(2).v_sal
14 from emp where empno=7788;
15 dbms_output.put_line(v_tab(1).v_ename|| chr(10)||
16 v_tab(1).v_sal|| chr(10)||
17 v_tab(2).v_ename|| chr(10)||
18 v_tab(2).v_sal);
19* end;
SQL> /
jack
3000
SCOTT
3000
PL/SQL 过程已成功完成。
当然此时我们也可以简写
在sqlplus的外部声明一下
SQL> var x varchar2(100);
SQL> begin
2 :x:='tttt';
3 end;
4 /
PL/SQL 过程已成功完成。
SQL> print x
X
--------------------------------------------------------------------------------
tttt 这可以操作
SQL> begin
2 select ename into :x from emp where empno=7839;
3 end;
4 /
PL/SQL 过程已成功完成。
SQL> print x
X
--------------------------------------------------------------------------------
KING
使用 IF语句按条件判断,控制PL/SQL 流程 ;
if -then -end if
if- then-else-end if
if-then-elsif-then-else-end if
Oracle 的写法
if expr1
then
action1
elif expr2
then
action2
else
default_action;
fi
if
expr1
then
action1;
elsif expr2;
then action2 ;
...
else
default action;
end if;
set serveroutput on 打开后台显示
开始声明
SQL> ed
已写入 file afiedt.buf
1 declare
2 v_hour number :=(to_char(sysdate,'hh24'));
3 begin
4 if v_hour >=6 and v_hour<=11 then
5 dbms_output.put_line('现在是上午');
6 else
7 dbms_output.put_line('现在不是上午');
8 end if;
9* end;
SQL> /
现在不是上午
PL/SQL 过程已成功完成。 单分支情况
我们还可以继续嵌套
使用 CASE 分支
1 declare
2 v_grade varchar2(1) :=upper('&grade');
3 v_info varchar2(20);
4 begin
5 v_info:=
6 case v_grade when 'A' then
7 'excellent'
8 when 'B' then
9 'very good!!!'
10 when 'C' then
11 'yiban'
12 else 'No such as grade!!!'
13 end;
14 dbms_output.put_line(
15 'the grade is'|| v_grade||chr(10)||
16 ' the information is ' || v_info
17 );
18* end;
SQL> /
输入 grade 的值: C
原值 2: v_grade varchar2(1) :=upper('&grade');
新值 2: v_grade varchar2(1) :=upper('C');
the grade isC
the information is yiban
PL/SQL 过程已成功完成。
使用别的分支函数
SQL> ed
已写入 file afiedt.buf
1 declare
2 v_grade varchar2(1) :=upper('&grade');
3 v_info varchar2(20);
4 begin
5 case
6 when
7 v_grade = 'A' then
8 v_info:='excellent!!!!';
9 when v_grade='B' then
10 v_info:='very good!!!';
11 when v_grade='C' then
12 v_info:='yiban';
13 else
14 v_info:='No such as grade!!!';
15 end case;
16 dbms_output.put_line(
17 'the grade is ' || v_grade||chr(10)||
18 ' the information is ' || v_info
19 );
20* end;
SQL> /
输入 grade 的值: a
原值 2: v_grade varchar2(1) :=upper('&grade');
新值 2: v_grade varchar2(1) :=upper('a');
the grade is A
the information is excellent!!!!
PL/SQL 过程已成功完成
循环
SQL> ed
已写入 file afiedt.buf
1 declare
2 i int :=1;
3 begin
4 loop 开始
5 dbms_output.put_line(i);
6 i:=i+1;
7 exit when i>10;
8 end loop; 结束
9* end;
SQL> /
1
2
3
4
5
6
7
8
9
10
PL/SQL 过程已成功完成。
数字FOR 循环
SQL> begin
2 for i in 1..10 loop
3 dbms_output.put_line(i);
4 end loop;
5 end;
6 /
1
2
3
4
5
6
7
8
9
10
PL/SQL 过程已成功完成。
双重循环
外循环和内循环都执行5次 ,内外计数器变量名相同 i
显示内循环的计数器和外循环的计数器的乘积
当乘积超过15时候 退出
SQL> ed
已写入 file afiedt.buf
1 begin
2 for i in 1..5 loop
3 for j in 1..5 loop
4 /*
5 exit when i*j>15 ;
6 */
7 if i*j>15 then
8 return;
9 end if;
10 dbms_output.put_line(i*j);
11 end loop;
12 end loop;
13 dbms_output.put_line('test');
14* end;
SQL> /
1
2
3
4
5
2
4
6
8
10
3
6
9
12
15
4
8
12
PL/SQL 过程已成功完成。
SQL> ed
已写入 file afiedt.buf
1 declare
2 i int:=1;
3 begin
4 loop
5 dbms_output.put_line(i);
6 i:=i+1;
7 if i>10 then
8 goto outofloop;
9 end if;
10 end loop;
11 dbms_output.put_line('this is a test text');
12 <
13 dbms_output.put_line('the last i is ' || i);
14* end;
SQL> /
1
2
3
4
5
6
7
8
9
10
the last i is 11
PL/SQL 过程已成功完成。
写一个PL/SQL 块
想dept表中循环插入5条信息 每一条记录 的deptno值比表中最大的deptno增加1
dname 分别为 "Test1" "Test2" ....."Test5";
SQL> ed
已写入 file afiedt.buf
1 declare
2 mdeptno dept.deptno%type;
3 begin
4 for i in 1..5 loop
5 select max(deptno) into mdeptno from dept;
6 insert into dept values((mdeptno+1),'Test ' || i,null );
7 end loop;
8* end;
9 /
PL/SQL 过程已成功完成。
SQL> rollback;
回退已完成。
SQL> select * from dept;
DEPTNO DNAME LOC
---------- -------------- -------------
10 ACCOUNTING NEW YORK
20 RESEARCH DALLAS
30 SALES CHICAGO
40 OPERATIONS BOSTON
SQL> ed
已写入 file afiedt.buf
1 declare
2 type num_typ is table of int index by binary_integer;
3 type var_typ is table of varchar2(10) index by binary_integer;
4 /*
5 这是下标的 写法
6 */
7 v_num num_typ;
8 v_var var_typ;
9 v_max dept.deptno%type;
10 /*
11 这是给它赋值
12 */
13 begin
14 select max(deptno) into v_max from dept;
15 select rownum,'test' || rownum
16 bulk collect into v_num,v_var
17 from dual
18 connect by rownum<=5;
19 forall i in v_num.first..v_num.last
20 insert into dept values((v_num(i)+v_max),v_var(i),null);
21 commit;
22* end;
SQL> /
PL/SQL 过程已成功完成。
然后查询
SQL> select * from dept;
DEPTNO DNAME LOC
---------- -------------- -------------
10 ACCOUNTING NEW YORK
20 RESEARCH DALLAS
30 SALES CHICAGO
40 OPERATIONS BOSTON
41 test1
42 test2
43 test3
44 test4
45 test5
已选择9行。
事务控制语言 commit 和rollback
1 declare
2 v_counter int;
3 begin
4 v_counter:=0;
5 for i in 1..500 loop
6 insert into t1 values(i);
7 v_counter:=v_counter+1;
8 if v_counter=50 then
9 commit;
10 v_counter:=0;
11 end if;
12 end loop;
13 commit;
14* end;
15 /
PL/SQL 过程已成功完成。
查看 SQL> select * from t1; 有500行
PLSQL 表 + record +循环打印结果集 (dept表的所有行所有列)
1 declare
2 type dept_type is table of dept%rowtype index by binary_integer;
3 v_dept dept_type;
4 v_cnt int;
5 v_deptno dept.deptno%type;
6 begin
7 select count(*) into v_cnt from dept;
8 for i in 1..v_cnt loop
9 select deptno into v_deptno from (
10 select rownum rn, d.* from dept d)
11 where rn=i;
12 select * into v_dept(i) from dept where deptno=v_deptno;
13 end loop;
14 for i in v_dept.first..v_dept.last loop
15 dbms_output.put_line(v_dept(i).deptno||','||
16 v_dept(i).dname||','||
17 v_dept(i).loc);
18 end loop;
19* end;
SQL> /
10,ACCOUNTING,NEW YORK
20,RESEARCH,DALLAS
30,SALES,CHICAGO
40,OPERATIONS,BOSTON
41,test1,
42,test2,
43,test3,
44,test4,
45,test5,
PL/SQL 过程已成功完成。
使用 bulk collect
批量将数据绑定到数组中,可以减少sql执行次数,提高程序运行效率
练习
打印 1到100的所有素数
显示 游标使用流程
1 声明 declare 2 打开 open 3 fetch 4 关闭 close
1 简单游标的使用
游标是在open 时使用内存的获取一次内存就少一行 游标获取之后应该使用相关的变量接收游标的量,j就像select ...into 一样
已写入 file afiedt.buf
1 declare
2 cursor cl is select * from scott.emp;
3 emp_rec scott.emp%rowtype;
4 begin
5 open cl;
6 loop
7 --因为使循环所以使用 loop 来
8 fetch cl into emp_rec;
9 exit when cl%notfound;
10 dbms_output.put_line(emp_rec.empno||','|| emp_rec.ename);
11 end loop;
12* end;
SQL> /
7369,SMITH
7499,ALLEN
7521,WARD
7566,JONES
7654,MARTIN
7698,BLAKE
7782,CLARK
7788,SCOTT
7839,KING
7844,TURNER
7876,ADAMS
7900,JAMES
7902,FORD
7934,MILLER
PL/SQL 过程已成功完成。 使用sysdba 来登录
入职最早的5个人
1 declare
2 cursor c1 is select * from scott.emp order by hiredate;
3 --入职最早的5个人
4 emp_rec scott.emp%rowtype;
5 begin
6 open c1 ;
7 loop
8 --因为使循环所以使用loop 来
9 fetch c1 into emp_rec;
10 exit when c1%rowcount>5;
11 dbms_output.put_line(emp_rec.empno||','|| emp_rec.ename||','|| emp_rec.hiredate);
12 --最后的能打印两行
13 end loop;
14 close c1;
15* end;
SQL> /
7369,SMITH,17-12月-80
7499,ALLEN,20-2月 -81
7521,WARD,22-2月 -81
7566,JONES,02-4月 -81
7698,BLAKE,01-5月 -81
PL/SQL 过程已成功完成。
这里我们也可以使用for循环来使用
1 declare
2 cursor c1 is select * from scott.emp order by hiredate;
3 begin
4 for emp_rec in c1 loop
5 exit when c1%rowcount>5;
6 dbms_output.put_line(emp_rec.empno||','|| emp_rec.ename||','|| emp_rec.hiredate);
7 end loop;
8* end;
SQL> /
7369,SMITH,17-12月-80
7499,ALLEN,20-2月 -81
7521,WARD,22-2月 -81
7566,JONES,02-4月 -81
7698,BLAKE,01-5月 -81
PL/SQL 过程已成功完成。
begin
2 for emp_rec in (select * from scott.emp order by hiredate) loop
3 --exit when c1%rowcount>5;
4 dbms_output.put_line(emp_rec.empno||','|| emp_rec.ename||','|| emp_rec.hiredate);
5 end loop;
6* end;
SQL> /
前5行的打印
1 declare
2 i int:=1;
3 begin
4 for emp_rec in (select * from scott.emp order by hiredate) loop
5 --exit when c1%rowcount>5;
6 dbms_output.put_line(emp_rec.empno||','|| emp_rec.ename||','|| emp_rec.hiredate);
7 i:=i+1;
8 exit when i>5;
9 end loop;
10* end;
SQL> /
7369,SMITH,17-12月-80
7499,ALLEN,20-2月 -81
7521,WARD,22-2月 -81
7566,JONES,02-4月 -81
7698,BLAKE,01-5月 -81
PL/SQL 过程已成功完成。
也可以使用
3 游标 for循环使用for 循环来替代loop 循环 for 循环中的变量根据in关键字后面的内容二决定变量的类型
如果in 后面是数字 则变量是number 类型标量变量
如果in 后面是游标 则变量是record 类型符合变量
1 declare
2 cursor c1 is select ename,sal from scott.emp;
3 begin
4 for r1 in c1 loop
5 exit when c1%rowcount>5;
6 dbms_output.put_line(r1.ename||','||r1.sal);
7 end loop;
8* end;
9 /
SMITH,800
ALLEN,1600
WARD,1250
JONES,2975
MARTIN,1250
PL/SQL 过程已成功完成。
4 使用游标for循环 可以省略游标的声明 但这种游标for循环不能使用游标属性控制 要自己加上计数器
已写入 file afiedt.buf
1 declare
2 v_num number:=0;
3 begin
4 for r1 in(select ename,sal from scott.emp) loop
5 dbms_output.put_line(SQL%rowcount);
6 v_num:=v_num+1;
7 exit when v_num>5;
8 dbms_output.put_line(r1.ename||','||r1.sal);
9 end loop;
10* end;
SQL> /
SMITH,800
ALLEN,1600
WARD,1250
JONES,2975
MARTIN,1250
PL/SQL 过程已成功完成。
5 参数游标
1 declare
2 cursor
3 c1 (p_no emp.deptno%type)
4 is
5 select * from emp where deptno=p_no;
6 begin
7 for i in c1(&a) loop
-- 这里是要形式参数
8 dbms_output.put_line(i.empno||',' ||i.ename||','||i.deptno);
9 end loop;
10* end;
SQL> /
输入 a 的值: 10
原值 7: for i in c1(&a) loop
新值 7: for i in c1(10) loop
7782,CLARK,10
7839,KING,10
7934,MILLER,10
PL/SQL 过程已成功完成。
1 declare
2 cursor w is select * from dept;
3 cursor n (p_dp dept.deptno%type)
4 is select * from emp where deptno=p_dp;
5 begin
6 for i in w loop
7 dbms_output.put_line('正在打印'|| i.deptno||'号部门的信息...');
8 dbms_output.put_line('部门编号:'|| i.deptno||chr(10)||
9 '部门名称:' ||i.dname|| chr(10)||
10 ' 部门地址'||i.loc
11 );
12 dbms_output.put_line('正在输出人员信息......');
13 dbms_output.put_line('工号 姓名 工资');
14 for j in n(i.deptno) loop
15 dbms_output.put_line(j.empno||' '||j.ename|| ' '||j.sal);
16 end loop;
17 end loop;
18 dbms_output.put_line('处理完毕');
19* end;
20 /
正在打印10号部门的信息...
部门编号:10
部门名称:ACCOUNTING
部门地址NEW YORK
正在输出人员信息......
工号 姓名 工资
7782 CLARK 2450
7839 KING 5000
把工资2000一下的人增加10%
1 declare
2 cursor cw is select deptno from dept;
3 cursor cn(p_dp dept.deptno%type)
4 is
5 select * from emp where deptno=p_dp for update;
6 /*
7 由于是做修改所以是for update
8 */
9 begin
10 for i in cw loop
11 dbms_output.put_line('正在处理'||i.deptno||'号部门数据......');
12 for j in cn(i.deptno) loop
13 if j.sal<2000 then
14 update emp set sal=sal*1.1 where current of cn;
15 dbms_output.put_line(
16 j.ename||'s sal have been changed from ' || j.sal|| ' to ' || j.sal*1.1
17 );
18 /*
19 current of 是对当前行的锁定
20 */
21 end if;
22 end loop;
23 end loop;
24* end;
SQL> /
正在处理10号部门数据......
MILLERs sal have been changed from 1573 to 1730.3
正在处理20号部门数据......
SMITHs sal have been changed from 968 to 1064.8
ADAMSs sal have been changed from 1331 to 1464.1
正在处理30号部门数据......
ALLENs sal have been changed from 1936 to 2129.6
WARDs sal have been changed from 1512.5 to 1663.75
MARTINs sal have been changed from 1512.5 to 1663.75
TURNERs sal have been changed from 1815 to 1996.5
JAMESs sal have been changed from 1149.5 to 1264.45
正在处理40号部门数据......
正在处理41号部门数据......
正在处理42号部门数据......
正在处理43号部门数据......
正在处理44号部门数据......
正在处理45号部门数据......
PL/SQL 过程已成功完成。
游标变量 也称为参考游标,可以事先不定义游标的具体内容,而是在程序中使用 open for 对它进行定义参考游标可以作为此次过程中的传入传出参数
游标对应不同的值获取不同的值
1 declare
2 type refcur is ref cursor;
3 c1 refcur;
4 emp_rec emp%rowtype;
5 dept_rec dept%rowtype;
6 begin
7 open c1 for select * from emp;
8 loop
9 --此时我们不能使用for 循环 使用loop 循环
10 fetch c1 into emp_rec;
11 exit when c1%notfound;
12 dbms_output.put_line(emp_rec.ename);
13 end loop;
14 close c1;
15 --也可以重新把变量打开
16 open c1 for select * from dept;
17 loop
18 --此时我们不能使用for 循环 使用loop 循环
19 fetch c1 into dept_rec;
20 exit when c1%notfound;
21 dbms_output.put_line(dept_rec.dname);
22 end loop;
23 close c1;
24* end;
25 /
隐试游标的属性
SQL%ROWCOUNT 成功操作的行数
SQL%FOUND 发现符合条件的行返回TRUE
SQL%NOTFOUND 没有发现符合条件的返回TRUE
SQL% ISOPEN 游标打开状态 boolean
显示游标的属性 ,只需要把SQL写成游标名称即可
SQL> declare
2 v_cnt int;
3
4 begin
5 select count(*) into v_cnt from emp ;
6 dbms_output.put_line(sql%rowcount);
7 delete emp;
8 dbms_output.put_line(sql%rowcount);
9 end;
10 /
1 执行成功一次
14 成功14次
PL/SQL 过程已成功完成。
L> declare
2 v_sal emp.sal%type;
3 begin
4 select sal into v_sal from emp;
5 end;
6 /
declare
第 1 行出现错误:
ORA-01422: 实际返回的行数超出请求的行数
ORA-06512: 在 line 4
捕获异常
1 declare
2 v_sal emp.sal%type;
3 begin
4 select sal into v_sal from emp;
5 dbms_output.put_line('gogogo');
6 exception
7 when too_many_rows then
8 dbms_output.put_line('值太多,没有成功执行,检查代码');
9* end;
10 /
值太多,没有成功执行,检查代码
PL/SQL 过程已成功完成。
1 declare
2 v_sal emp.sal%type;
3 fk_ex exception ;
4 --绑定错误
5 pragma exception_init(fk_ex,-2292);
6 begin
7 delete dept;
8 dbms_output.put_line('gogogo');
9 exception
10 when too_many_rows then
11 dbms_output.put_line('值太多,没有成功执行,检查代码');
12 when no_data_found then
13 dbms_output.put_line('么有获得数据,已经退出程序');
14 when fk_ex then
--这是绑定的错误日志
15 dbms_output.put_line('有外键引用,无法删除');
16 when others then
17 dbms_output.put_line('错误编号:'|| sqlcode ||chr(10)||'错误描述:'||sqlerrm);
18 --sqlerrm 默认值
19* end;
SQL> /
有外键引用,无法删除
PL/SQL 过程已成功完成。
1 declare
2 my_ex exception;
3 pragma exception_init(my_ex,-20001);
4 v_h number :=to_number(to_char(sysdate,'hh24'));
5 --获取当前24制的日期
6 v_w varchar2(20):=to_char(sysdate,'dy');
7 begin
8 if v_h between 9 and 17 or v_w in('sat','sun') then
9 raise_application_error(-20001,'非工作时间禁止修改');
10 else
11 update emp set sal=sal+1000;
12 end if;
13 exception
14 when my_ex then
15 dbms_output.put_line('非上班时间,请不要修改工资');
16* end;
SQL> /
非上班时间,请不要修改工资
PL/SQL 过程已成功完成。
解决办法
工作在纽约的人扣除5%
工作是经理的,而且住在DALLAS的人加薪15%
6 对直接上级是 ‘BLAKE’ 的所有员工,按照参加工作的世界加薪:
81年6月以前的加薪10%;
81年6月以后的加薪5%;
1 declare
2 cursor c1 is
3 select * from emp
4 where mgr=(select empno from emp where ename='BLAKE') for update;
5 v_sal1 emp.sal%type;
6 begin
7 for i in c1 loop
8 if i.hiredate 9 update emp set sal=sal*1.1 where current of c1; 10 --对应上面的声明的sal1 11 dbms_output.put_line(i.ename||','||i.sal ||','||','|| i.sal*1.1); 12 elsif i.hiredate>to_date('1981-06-01','yyyy-mm-dd') then 13 update emp set sal=sal*1.05 where current of c1; 14 --对应上面的声明的sal1 15 dbms_output.put_line(i.ename||','||i.sal ||','||','|| i.sal*1.05); 16 end if; 17 end loop; 18* end; SQL> / ALLEN,5060,,5566 WARD,4675,,5142.5 MARTIN,4462.5,,4685.625 TURNER,4725,,4961.25 JAMES,4147.5,,4354.875 1 create or replace procedure raise_sal 存储过程的名字 -- raise_sal 2 (p_id in emp.empno%type) --in 是默认的 3 is 4 v_sal emp.sal%type; -- 声明变量 5 begin 6 update emp set sal=sal+100 where empno=p_id; 7 select sal into v_sal from emp where empno=p_id; 8 dbms_output.put_line(v_sal); 9* end; 10 / 过程已创建。 SQL> begin 2 raise_sal(7788); 3 end; 4 / 3200 PL/SQL 过程已成功完成。 SQL> 传出的参数 L> create or replace procedure get_info 2 (p_id emp.empno%type, 3 o_ename out emp.ename%type, 4 o_sal out emp.sal%type) 5 is 6 begin 7 select ename,sal into o_ename,o_sal from emp where empno=p_id; 8 end; 9 / 过程已创建。 SQL> create or replace procedure get_info 2 (p_id emp.empno%type, 3 o_ename out emp.ename%type, 4 o_sal out emp.sal%type) 5 is 6 begin 7 select ename,sal into o_ename,o_sal from emp where empno=p_id; 8 end; 9 / 过程已创建。 SQL> declare 2 p_id emp.empno%type; 3 p_ename emp.ename%type; 4 p_sal emp.sal%type; 5 6 begin 7 p_id :=&no; 8 get_info(p_id,p_ename,p_sal); 9 dbms_output.put_line(p_id||','||p_ename||','||p_sal); 10 11 end; 12 / 输入 no 的值: 7788 原值 7: p_id :=&no; 新值 7: p_id :=7788; 7788,SCOTT,3200 PL/SQL 过程已成功完成。 还可以这样做 > var ename varchar2(20) SQL> var sal number QL> exec get_info(7839,:ename,:sal); PL/SQL 过程已成功完成。 SQL> print 打印工资和名字 ENAME KING SAL 5100 3 再写衣蛾传入传出的类型 已写入 file afiedt.buf 1 create or replace procedure forstr 2 (p_str in out varchar2) 3 is 4 v_str varchar2(100); 5 begin 6 for i in 1..length(p_str) loop 7 if mod(i,4) =1 then 8 v_str:=v_str || substr(p_str,i,4)||'-'; 9 end if; 10 end loop; 11 p_str := v_str; 12* end; 13 / 过程已创建。 1 declare 2 p_str varchar2(100); 3 begin 4 p_str:='&a'; 5 forstr(p_str); 6 dbms_output.put_line(p_str); 7* end; SQL> / 输入 a 的值: dtakjhjh;hkjhjk;wera 原值 4: p_str:='&a'; 新值 4: p_str:='dtakjhjh;hkjhjk;wera'; dtak-jhjh-;hkj-hjk;-wera- PL/SQL 过程已成功完成。 再次创建一个存储过程 SQL> create or replace procedure add_dept( 2 p_deptno dept.deptno%type, 3 p_dname dept.dname%type default 'sales', 4 p_loc dept.loc%type default 'bj' 5 ) 6 is 7 begin 8 insert into dept values( 9 p_deptno, 10 p_dname, 11 p_loc); 12 commit; 13 end; 14 / 过程已创建。 SQL> select * from dept; DEPTNO DNAME LOC ---------- -------------- ------------- 10 ACCOUNTING NEW YORK 20 RESEARCH DALLAS 30 SALES CHICAGO 40 OPERATIONS BOSTON 60 sales bj 41 test1 42 test2 43 test3 44 test4 45 test5 已选择10行。 QL> exec add_dept(61,'teacher','tj'); PL/SQL 过程已成功完成。 SQL> exec add_dept(62,p_loc=>'shanghai'); 前面做默认后面是形式参数修改 PL/SQL 过程已成功完成。 SQL> select * from dept; DEPTNO DNAME LOC 10 ACCOUNTING NEW YORK 20 RESEARCH DALLAS 30 SALES CHICAGO 40 OPERATIONS BOSTON 60 sales bj 61 teacher tj 62 sales shanghai 41 test1 42 test2 43 test3 44 test4 45 test5 已选择12行。 使用动态来查询 SQL> begin 2 execute immediate 'drop table t1 purge'; 3 end; 4 / PL/SQL 过程已成功完成。 SQL> select * from tab; TNAME TABTYPE CLUSTERID CUSTOMER TABLE DEPT TABLE EMP TABLE MYVIEW VIEW PRODUCT TABLE PURCHASE TABLE SALGRADE TABLE 已选择7行。 如何创建存储过程的动态SQL 1 create or replace procedure tab_create 2 (p_tname varchar2, 3 p_col1 varchar2, 4 p_typ1 varchar2, 5 p_col2 varchar2, 6 p_typ2 varchar2 7 ) 8 is 9 v_sql varchar2(200); 10 begin 11 v_sql :='create table '|| p_tname || '(' || p_col1 || ' ' ||p_typ1 || ',' || 12 p_col2|| ' '||p_typ2||')'; 13 execute immediate v_sql; 14* end; SQL> / 过程已创建。 查看权限 此时我看到了权限不足 SQL> execute tab_create('t1','id','number','name','varchar2(10)'); BEGIN tab_create('t1','id','number','name','varchar2(10)'); END; 第 1 行出现错误: ORA-01031: 权限不足 ORA-06512: 在 "SCOTT.TAB_CREATE", line 13 ORA-06512: 在 line 1 2 我们开始查看权限 在 scott用户中 SQL> select * from session_roles; ROLE CONNECT RESOURCE 4 此时我们登录sys 用户 授予权限 SQL> conn sys/bwf123456 AS SYSDBA; 已连接。 SQL> grant create table to scott; 授权成功。 5 再次创建 SQL> desc t1 ; 名称 是否为空? 类型 ID NUMBER NAME VARCHAR2(10) 创建删除表 1 create or replace procedure tab_drop 2 (tab_name varchar2) 3 is 4 v_no number; 5 begin 6 v_no:=dbms_sql.open_cursor; 7 dbms_sql.parse(v_no, 8 'drop table ' || tab_name||'purge', 9 dbms_sql.native ); 10 dbms_sql.close_cursor(v_no); 11 --关闭游标 12 exception 13 when 14 others then 15 dbms_output.put_line(sqlcode||','||sqlerrm); 16* end; SQL> / 过程已创建。 2 开始使用 exec tab_drop('T1'); SQL> execute tab_drop('t1'); -942,ORA-00942: 表或视图不存在 PL/SQL 过程已成功完成。 查看源代码 SQL> select name,text from user_source where name='TAB_DROP'; 最后的是写上名字 缩小那个text SQL> col text for a60 SQL> select name,text from user_source where name='TAB_DROP'; NAME TEXT TAB_DROP procedure tab_drop TAB_DROP (tab_name varchar2) TAB_DROP is TAB_DROP v_no number; TAB_DROP begin TAB_DROP v_no:=dbms_sql.open_cursor; TAB_DROP dbms_sql.parse(v_no, TAB_DROP 'drop table ' || tab_name||'purge', TAB_DROP dbms_sql.native ); TAB_DROP dbms_sql.close_cursor(v_no); TAB_DROP /* TAB_DROP 关闭游标 TAB_DROP */ TAB_DROP exception TAB_DROP when others then TAB_DROP dbms_output.put_line(sqlcode||','||sqlerrm); TAB_DROP end; 已选择17行。 3 对存储过程加密 D:\backup>wrap iname=tab_drop.sql 已经对文件进行加密 PL/SQL Wrapper: Release 11.2.0.1.0- 64bit Production on 星期二 7月 13 22:10:16 2021 Copyright (c) 1993, 2009, Oracle. All rights reserved. Processing tab_drop.sql to tab_drop.plb 此时的文件已经变成了plb 结尾的 如何调用 在Oracle里面直接使用@调用 SQL> @tab_drop.plb 4 ,查看scott 下有哪些存储过程 SQL> select object_name,object_type from user_objects where object_type='PROCEDURE'; OBJECT_NAME -------------------------------------------------------------------------------- OBJECT_TYPE ------------------- ADD_DEPT PROCEDURE 5 删除存储过程 开始创建过程 1 create or replace procedure cur_out 2 (p_name varchar2, 3 p_col1 varchar2, 4 p_col2 varchar2, 5 p_deptno number, 6 p_cur out sys_refcursor) 7 is 8 v_sql varchar2(200); 9 begin 10 v_sql:='select '||p_col1||',' ||p_col2||'from'||p_name||'where deptno='||p_deptno; 11 open p_cur for v_sql; 12 --放开游标 13* end; 14 / 过程已创建。 declare 2 c1 sys_refcursor; 3 v_empno emp.empno%type; 4 v_ename emp.ename%type; 5 begin 6 cur_out('dept','deptno','dname',10,c1); 7 loop 8 fetch c1 into v_empno,v_ename; 9 exit when c1%notfound; 10 dbms_output.put_line(v_empno||','||v_ename); 11 end loop; 12 close c1; 13* end; SQL> / 开始创建函数 1 create or replace function get_sal 2 (p_id emp.empno%type) 3 return emp.empno%type 4 is 5 v_sal emp.sal%type; 6 begin 7 select sal into v_sal from emp where empno=p_id; 8 return v_sal; 9* end; SQL> / 函数已创建。 声明 1 declare 2 a number; 3 begin 4 a:=get_sal(7788); 5 dbms_output.put_line(a); 6* end; SQL> / PL/SQL 过程已成功完成。 SQL> execute :a:=get_sal(7839) PL/SQL 过程已成功完成。 SQL> print a A ---------- 5100 SQL> select get_sal(7788) from dual; GET_SAL(7788) ------------- 3200 SQL> select empno,ename,get_sal(empno) from emp; EMPNO ENAME GET_SAL(EMPNO) ---------- ---------- -------------- 7369 SMITH 800 7499 ALLEN 1600 7521 WARD 1250 7566 JONES 2975 7654 MARTIN 1250 7698 BLAKE 2850 7782 CLARK 2450 7788 SCOTT 3200 7839 KING 5100 7844 TURNER 1500 7876 ADAMS 1100 EMPNO ENAME GET_SAL(EMPNO) ---------- ---------- -------------- 7900 JAMES 950 7902 FORD 3000 7934 MILLER 1300 已选择14行。 使用 函数来 SQL> create or replace function fun_2 2 (p_sal emp.sal%type) 3 4 return number 5 6 is 7 begin 8 return(p_sal*0.08); 9 end; 10 / 函数已创建。 1 create or replace function pbdm 2 --布尔型的名字 3 (p_id dept.deptno%type) 4 return boolean 5 is 6 c_cnt int; 7 begin 8 select 1 into c_cnt from dept where deptno=p_id; 9 return(true); 10 exception 11 when no_data_found then 12 return(false) ; 13* end; SQL> / 函数已创建。创建 一个存储过程