0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Procedure Language

Last updated at Posted at 2019-06-23

  Procedure Language 实际上是Oracle对SQL语言的能力扩展,让SQL语言拥有了if条件判断,for循环等处理。

http://otndnld.oracle.co.jp/document/products/oracle11g/111/doc_dvd/appdev.111/E05670-03/overview.htm#i8859
http://otndnld.oracle.co.jp/document/products/oracle10g/102/doc_cd/appdev.102/B19257-01/collections.html#1004
https://docs.oracle.com/cd/E82638_01/sqlrf/DROP-TYPE.html#GUID-2CBA2EFD-1B01-46A8-A4CD-B2975D3A1D67

http://otndnld.oracle.co.jp/document/products/oracle11g/111/doc_dvd/appdev.111/E05670-03/static.htm#BABJCAAG
一、PLSQL基本语法

复制代码
1 DECLARE
2 -- 声明部分
3 变量名 变量类型 := 初始值
4 变量名 emp.sal % TYPE -- 引用类型的变量
5 emp % rowtype -- 记录型变量
6 BEGIN
7 -- 业务逻辑
8 END ;
复制代码
1、变量的声明与使用

复制代码
1 -- 已知数据类型的赋值声明
2 DECLARE
3 i NUMBER := 100 ;
4 BEGIN
5 -- 输出语句相当于 System.out.print();
6 dbms_output.put_line('Hello World!' || i) ;
7 END ;
8
9 -- 未知数据类型的类型声明
10 -- 输出7369的工资
11
12 DECLARE
13 vsal emp.sal % TYPE ;
14 BEGIN
15 -- 给变量赋值
16 SELECT sal INTO vsal FROM emp WHERE empno = 7369 ;
17 dbms_output.put_line(vsal) ;
18 END ;
19
20 -- 记录型变量声明与赋值
21 -- 输出7369的所有信息
22 DECLARE
23 vrow emp % rowtype ;
24 BEGIN
25 SELECT * INTO vrow FROM emp WHERE empno = 7369 ;
26 dbms_output.put_line(vrow.empno||' '|| vrow.ename);
27 END ;
复制代码
2、if条件判断语法与使用

复制代码
1 -- 根据不同年龄输出信息
2 DECLARE
3 -- 由客户端输入
4 age number := &aaa;
5 BEGIN
6 IF age <= 18 THEN
7 dbms_output.put_line('未成年人');
8 ELSIF age > 18 AND age <= 24 THEN
9 dbms_output.put_line('年轻人');
10 ELSIF age > 24 AND age < 48 THEN
11 dbms_output.put_line('中年人');
12 ELSE
13 dbms_output.put_line('老年人');
14 END IF;
15 END;
复制代码
3、三种循环

复制代码
1 /*
2 三种循环
3 for 变量名 in 起始值..结束值 loop
4
5 end loop;
6 ----------------------------------
7 while 条件 loop
8
9 end loop;
10 -----------------------------------
11 loop
12 exit when 退出的条件
13 循环体
14 end loop;
15 */
16
17 -- for 循环
18 -- 输出1-10
19 DECLARE
20
21 BEGIN
22 FOR i IN 1..10 LOOP
23 dbms_output.put_line(i);
24 END LOOP;
25 END;
26 -- 输出10-1
27 DECLARE
28
29 BEGIN
30 FOR i IN REVERSE 1..10 LOOP
31 dbms_output.put_line(i);
32 END LOOP;
33 END;
34
35 -- while 循环
36 DECLARE
37 i NUMBER := 1;
38 BEGIN
39 WHILE i <= 10 loop
40 dbms_output.put_line(i);
41 i := i+1;
42 END LOOP;
43 END;
44
45 -- 简单循环
46 DECLARE
47 i NUMBER := 1;
48 BEGIN
49 LOOP
50 EXIT WHEN i > 10;
51 dbms_output.put_line(i);
52 i := i+1;
53 END LOOP;
54 END;
复制代码
二、游标

1、游标概述  

1.1 游标: (光标/指针) 是对查询结果集的封装, 相当于是jdbc中的ResultSet

1.2 语法:

1 -- 声明游标
2 CURSOR 游标名 IS 查询语句;
3 CURSOR 游标名(参数名 参数类型) IS 查询语句 WHERE 列名 = 参数名;
1.3 开发步骤:

1.打开游标 open 游标名

2.从游标中提取数据:

fetch 游标名 into 变量

   游标名%notfound 没有数据

   游标名%found 找到数据

3.关闭游标 close 游标名

2、使用示例:

复制代码
1 -- 无参
2 -- 输出所有员工的信息
3 DECLARE
4 -- 声明游标
5 CURSOR vemps IS SELECT * FROM emp;
6 -- 声明变量
7 vrow emp % rowtype;
8 BEGIN
9 --1. 打开游标
10 open vemps;
11 --2. 提取数据
12 LOOP
13 FETCH vemps INTO vrow;
14 -- 判断是否有数据
15 EXIT WHEN vemps % notfound;
16 -- 打印数据
17 dbms_output.put_line('姓名:'||vrow.ename||' 工资:'||vrow.sal);
18 END LOOP;
19
20 -- 关闭游标
21 CLOSE vemps;
22 END;
23 ----------------------------------------------------------------
24 -- for 变量游标
25 DECLARE
26 -- 声明游标
27 CURSOR vemps IS SELECT * FROM emp;
28 -- 声明记录型变量
29 vrow emp % rowtype;
30 BEGIN
31 -- 循环遍历游标
32 FOR vrow IN vemps
33 LOOP
34 dbms_output.put_line('姓名:'||vrow.ename||' 工资:'||vrow.sal);
35 END LOOP;
36 END;
37
38 -- ===============================================================
39 -- 有参
40 -- 输出指定部门的员工信息
41 DECLARE
42 -- 声明游标
43 CURSOR vemps(vdeptno NUMBER) IS SELECT * FROM emp WHERE deptno = vdeptno;
44 -- 声明记录型变量
45 vrow emp % rowtype;
46 BEGIN
47 -- 1. 打开游标
48 OPEN vemps(20);
49 -- 2.循环遍历游标
50 LOOP
51 FETCH vemps into vrow;
52 EXIT when vemps % notfound;
53 -- 打印数据
54 dbms_output.put_line('姓名:'||vrow.ename||' 工资:'||vrow.sal);
55 END LOOP;
56 -- 3. 关闭游标
57 CLOSE vemps;
58 END;
复制代码
三、例外

1、例外概述

  例外 (意外): 相当于是java异常

  语法:

复制代码
1 declare
2 声明部分
3 begin
4 业务逻辑
5 exception
6 处理例外
7 when 例外1 then
8
9 when 例外2 then
10
11 when others then
12
13 end;
复制代码
  常见的系统的例外:

zero_divide : 除零例外
value_error : 类型转换
no_data_found : 没有找到数据例外
too_many_rows : 查询出多行记录,但是赋值给了单行变量
2、例外使用示例

复制代码
1 DECLARE
2 i NUMBER;
3 vrow emp % rowtype;
4 BEGIN
5 -- i := 5/0;
6 -- i := 'aaa';
7 -- select * into vrow from emp where empno = 1234566;
8 select * into vrow from emp;
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 value_error THEN
15 dbms_output.put_line('发生类型转换的例外');
16 WHEN zero_divide THEN
17 dbms_output.put_line('发生除零的例外');
18 WHEN others THEN
19 dbms_output.put_line('发生未知的例外');
20 END;
复制代码
3、自定义例外

  语法:

复制代码
1 DECLARE
2 -- 声明例外
3 例外名称 EXCEPTION ;
4 BEGIN
5 -- 抛出例外
6 raise 例外名称 ;
7 EXCEPTION
8 -- 捕获例外
9 WHEN 例外名称 THEN
10 ....
11 END ;
复制代码
  使用示例:

复制代码
1 -- 查询指定编号的员工,若没有找到,则抛出自定义例外
2 DECLARE
3 -- 声明游标
4 CURSOR vemps IS SELECT * FROM emp WHERE empno = 1234 ;
5 -- 记录型变量
6 vrow vemps % rowtype ;
7 -- 定义例外
8 no_emp_found EXCEPTION ;
9 BEGIN
10 --1.打开游标
11 OPEN vemps ;
12 --2.提取记录
13 FETCH vemps INTO vrow ;
14 -- 判断是否有数据
15 IF vemps % notfound THEN
16 -- 抛出例外
17 raise no_emp_found ;
18 END IF ;
19 -- 关闭游标
20 CLOSE vemps ;
21 EXCEPTION
22 WHEN no_emp_found THEN
23 dbms_output.put_line('没有找到对应的员工') ;
24 END ;
复制代码
四、存储过程

1、概述  

  存储过程: 实际上是将一段已经编译好的PLSQL代码片断,封装在数据库中。

  作用:

  1. 提高执行效率

  2. 提高代码复用性

  语法:

复制代码
1 create [or replace] procedure 过程名称[(参数1 in|out 参数类型,参数2 in|out 参数类型)]
2 is | as
3   -- 声明    
4 begin
5   -- 业务
6 end;
复制代码
2、使用示例

  

复制代码
1 -- 给指定员工涨薪,并打印涨薪前和涨薪后的工资
2 -- 员工编号 : 输入参数
3 -- 涨多少 : 输入参数
4 /*
5 1. 查询当前工
6 2. 打印涨薪前工资
7 3. 涨工资
8 4. 打印涨薪后的工资
9 5. 提交数据
10 /
11 create or replace procedure proc_updatesal(vempno in number,vcount in number)
12 is
13 -- 声明变量记录当前工资
14 vsal number;
15 begin
16 --1. 查询当前工资
17 select sal into vsal from emp where empno=vempno;
18 --2. 打印涨薪前工资
19 dbms_output.put_line('涨薪前:'||vsal);
20 --3. 涨工资
21 update emp set sal=vsal+vcount where empno=vempno;
22 -- 4. 打印涨薪后的工资
23 dbms_output.put_line('涨薪后:'||(vsal+vcount));
24 --5. 提交数据
25 commit;
26 end;
27
28 -- 调用存储过程
29 -- 方式1:
30 call proc_update_sal(7369,100);
31
32 -- 方式2:
33 declare
34
35 begin
36 proc_updatesal(7369,100);
37 end;
38
39 s
40 -- 获取指定编号员工的年薪
41 /

42 编号: in 输入
43 年薪: out 输出
44 /
45 create or replace procedure proc_getyearsal(vempno in number,vyearsal out number)
46 is
47
48 begin
49 select sal
12+nvl(comm,0) into vyearsal from emp where empno=vempno;
50 end;
51
52 -- plsql代码片断中调用
53 declare
54 yearsal number;
55 begin
56 proc_getyearsal(7369,yearsal);
57 dbms_output.put_line(yearsal);
58 end;
59
60
61 -- 封装存储过程,输出的是游标类型, 所有员工
62 /*
63 sys_refcursor : 系统引用游标
64 */
65 create or replace procedure proc_getemps(vemps out sys_refcursor)
66 is
67
68 begin
69 -- 打开游标, 谁调用谁关闭
70 open vemps for select * from emp;
71 end;
72
73 declare
74 vemps sys_refcursor;
75 vrow emp%rowtype;
76 begin
77 -- 调用存储过程
78 proc_getemps(vemps);
79
80 loop
81 fetch vemps into vrow;
82 exit when vemps%notfound;
83 dbms_output.put_line(vrow.ename);
84 end loop;
85 -- 关闭游标
86 close vemps;
87 end;
复制代码
五、存储函数

1、存储函数概述

  存储函数: 实际上是将一段已经编译好的PLSQL代码片断,封装在数据库中。

  作用:

  1. 提高执行效率

  2. 提高代码复用性

  语法:

复制代码
1 create [or replace] function 函数名称(参数1 in|out 参数类型) return 返回类型
2 is
3
4 begin
5
6 end;
复制代码
  存储过程和存储函数:

  1. 函数有返回值,过程没有返回值

  2. 函数可以直接在SQL语句中使用,过程不可以

  3. 函数能实现的功能,过程能实现

  4. 过程能实现的功能,函数也能实现

  5. 函数和过程本质上没有区别 通常情况下,我们自己开发封装的是存储过程

2、使用示例

复制代码
1 -- 存储函数:获取年薪
2 create or replace function func_getyearsal(vempno number) return number
3 is
4 vyearsal number;
5 begin
6 select sal12+nvl(comm,0) into vyearsal from emp where empno=vempno;
7 return vyearsal;
8 end;
9
10 -- 调用
11 declare
12 yearsal number;
13 begin
14 yearsal := func_getyearsal(7369);
15 dbms_output.put_line(yearsal);
16 end;
17
18 select emp.
,func_getyearsal(emp.empno) from emp;
复制代码
六、触发器

  1、数据库触发器是一个与表相关的、存储的PL/SQL程序。每当一个特定的数据操作语句(insert,update,delete)在指定的表上发出是,Oracle自动地执行触发器中定义的语句序列。

  2、作用:

监听表中的数据变化;
对表中的数据进行校验
  3、语法:

复制代码
1 CREATE [OR REPLACE] TRIGGER 触发器名称
2 {BEFORE | AFTER}
3 {INSERT | UPDATE | DELETE}
4 ON 表名
5 [ FOR EACH ROW [WHEN(条件)]]
6 DECLARE
7 ....
8 BEGIN
9 PLSQL块
10 END 触发器名;
复制代码
  4、触发器的类型

行级触发器:一条SQL语句,影响了多少行记录,触发器就会执行多少次;
两个内置对象:
:new 新的记录
:old 旧的记录
语句级触发器:一条SQL语句,无论影响了多少行记录,都只触发一次;
  5、使用示例

复制代码
1 -- 若用户向表中插入数据之后, 打印一句话
2 create or replace trigger tri_test1
3 after
4 insert
5 on emp
6 declare
7
8 begin
9 dbms_output.put_line('有人插入了....');
10 end;
11
12 insert into emp(empno,ename) values(9527,'华安');
13 -- 执行一条更新工资的语句
14
15 -- 周二老板不在,不能办理员工入职(不能向员工表中插入数据)
16 -- 触发器
17 -- before insert
18 -- 判断今天是否是周二
19 select trim(to_char(sysdate,'day')) from dual;
20
21 create or replace trigger tri_checkday
22 before
23 insert
24 on emp
25 declare
26 vday varchar2(20);
27 begin
28 -- 查询当前周几
29 select trim(to_char(sysdate,'day')) into vday from dual;
30 -- 判断是否为周二,若为周二,则需要中断插入操作
31 if vday = 'tuesday' then
32 -- -20000 - -20999
33 raise_application_error(-20001,'周二老板不在,不能插入');
34 end if;
35 end;
36
37 insert into emp(empno,ename) values(9527,'华安');
38
39 select * from emp;
40
41 -- 语句级触发器
42 create trigger tri_test3
43 before
44 update
45 on emp
46 declare
47
48 begin
49 dbms_output.put_line('语句级触发器');
50 end;
51
52 -- 行级触发器
53 create or replace trigger tri_test4
54 before
55 update
56 on emp
57 for each row
58 declare
59
60 begin
61 dbms_output.put_line('行级触发器,旧的工资:'||:old.sal||' 新的工资:'||:new.sal);
62 end;
63
64 update emp set sal=sal+100;
65
66 -- 6个月 ---> 人事 加薪 ---> 加10块钱 ---> 老板签字
67 -- 校验员工薪资 调整后的工资一定要 大于 薪资调整前的工资
68 -- 触发器: before update on emp
69 -- 行级触发器
70 create or replace trigger tri_checksal
71 before
72 update
73 on emp
74 for each row
75 declare
76
77 begin
78 -- 调整后的工资 <= 薪资调整前的工资 ,则中断更新操作
79 -- :new.sal <= :old.sal
80 if :new.sal <= :old.sal then
81 raise_application_error(-20002,'坑爹的,降薪啦!');
82 end if;
83 end;
84
85 update emp set sal=sal-100;
86
87 /*
88 使用触发器模拟类似auto_increment功能
89 当用户插入的时候,若为sid为null,则给sid赋值一个编号
90 */
91 create table stu(
92 sid number primary key,
93 name varchar2(20)
94 );
95
96 -- 创建一个序列
97 create sequence seq_stu;
98
99 -- 触发器: before insert on stu
100 -- 行级触发器
101 create or replace trigger tri_auto
102 before
103 insert
104 on stu
105 for each row
106 declare
107
108 begin
109 -- 从序列中查询一个数字出来,赋值给sid
110 select seq_stu.nextval into :new.sid from dual;
111 end;
112
113 -- 同样一张表,有时候自己指定id, 有时候需要数据库自动生成id
114 insert into stu values(null,'zs');
115 insert into stu values(4,'zs');
116 select * from stu;

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?