Oracle-PL/SQL基础

本文全面介绍了PL/SQL的基础知识,包括PL/SQL的特点、基本语法、变量类型、流程控制语句、游标操作等内容,并通过丰富的示例代码展示了如何在Oracle数据库中编写高效稳定的程序。

概述

PL/SQL简介

pl/sql(procedural language/sql)是Oracle在标准的sql语言上的扩展,pl/sql不仅允许嵌入Sql语言,还可以定义变量和常量,允许使用条件语句和循环语句,允许使用例外处理各种错误,这样使得它的功能变得更加强大。

pl/sql是面向过程的语言。

这里写图片描述

不同数据库的SQL扩展

  • PL/SQL 是Oracle数据的SQL扩展。
  • SQL/PL 是DB2数据库的SQL扩展。
  • T-SQL 是SQL Server数据库的SQL扩展。

PL/SQL的必要性:

1、提高应用程序的运行性能。 
2、模块化的设计思想。 
3、减少网络传输量。 
4、提高安全性。

PL/SQL编写规范

1、注释

单行注释– 
sql>select * from emp where empno=7788; –取得员工信息

多行注释

/………………../

2、标识符号的命名规范

(1)当定义变量时,建议用v_作为前缀 v_sal。

(2)当定义常量时,建议用c_作为前缀 c_rate。

(3)当定义游标时,建议用_cursor作为后缀emp_cursor。

(4)当定义例外时,建议用e_作为前缀 e_error。


PL/SQL块

块(block)是pl/sql的基本程序单元,编写pl/sql程序实际上 就是编写pl/sql块。要完成相对简单的应用功能,可能只需要编写一个pl/sql块;但是如果想要实现复杂的功能,可能需要在一个pl/sql块中嵌套其他的pl/sql块。


块结构示意图

pl/sql块由三个部分构成:定义部分、执行部分、例外处理部分。

declear:定义部分是从declare开始的,这部分是可选的。定义常量、变量、游标例外、复杂数据类型。

begin:执行部分是从begin开始的,这部分是必须的。

exception:例外处理部分是exception开始的,该部分可选的。

这里写图片描述


第一个PL/SQL程序

PLSQL中的命令窗口

 
  1. SQL> set serveroutput on ;--打开输出

  2. SQL> declare

  3. --定义部分 ,如果没有定义,declare可以省略。 可选 ,定义常量、变量、游标、例外、复杂数据类型

  4. 2 begin

  5. --程序

  6. 3 dbms_output.put_line('66666666');

  7. 4 end;

  8. 5 /

  9.  
  10. 66666666

  11.  
  12. PL/SQL procedure successfully completed

  13.  
  14. SQL> /

  15. --表示执行上一个PL/SQL块。

  16.  
  17. 66666666

  18.  
  19. PL/SQL procedure successfully completed

  20.  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

这里写图片描述


PL/SQL基础语法


程序结构

 
  1. declare

  2. 说明部分(变量说明\游标声明\例外说明)

  3. begin

  4. 语句序列(DML语句)

  5. exception

  6. 例外处理语句

  7. end ;

  8. /

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

基本变量类型

定义

基本变量类型: 
char 、 varchar2、date、number、boolean、long

举例:

 
  1. var1 char(20);

  2. married boolean := false ;

  3. psal number(7,2);

  • 1
  • 2
  • 3

栗子

 
  1. SQL> set serveroutput on ;

  2. SQL>

  3. SQL> declare

  4. 2 -- 定义基本变量类型

  5. 3 v_name varchar2(20);--varchar2 字符串类型

  6. 4 v_num number(7,2);--number 数字类型

  7. 5 v_date date ;--date 日期类型

  8. 6

  9. 7 begin

  10. 8

  11. 9 v_name :='小工匠';

  12. 10 dbms_output.put_line('name:'||v_name);

  13. 11

  14. 12 v_num :=999;

  15. 13 dbms_output.put_line('num:'||v_num);

  16. 14

  17. 15 v_date :=sysdate;

  18. 16 dbms_output.put_line('数据库时间:'||v_date);

  19. 17

  20. 18 dbms_output.put_line('明天的时间:'||(v_date+1));

  21. 19 end ;

  22. 20 /

  23.  
  24. name:小工匠

  25. num:999

  26. 数据库时间:22-JUN-16

  27. 明天的时间:23-JUN-16

  28.  
  29. PL/SQL procedure successfully completed

  30.  
  31. SQL>

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31

引用型变量 %type

定义

使用%TYPE类型的变量

举例

v_name emp.ename%type ;
  • 1

栗子

打印7369员工的姓名和薪水信息

 
  1. SQL> set serveroutput on ;

  2. SQL> declare

  3. 2 --打印 7369 员工的姓名和薪水信息

  4. 3 /* 可以使用基本类型来定义变量的类型,推荐使用引用型变量来定义变量类型*/

  5. 4 --vname varchar2(20);

  6. 5 --v_sal number;

  7. 6

  8. 7 --定义引用变量

  9. 8 v_name emp.ename%type ;

  10. 9 v_sal emp.sal%type;

  11. 10 begin

  12. 11 --业务操作,变量赋值 (两种赋值方式 1. v_sal := 20 第二种 into的方式 )

  13. 12 select ename, sal into v_name, v_sal from emp e where e.empno = 7369;

  14. 13 --输出信息

  15. 14 dbms_output.put_line(v_name||'的薪水是'||v_sal);

  16. 15 end;

  17. 16 /

  18.  
  19. SMITH的薪水是800

  20.  
  21. PL/SQL procedure successfully completed

  22.  
  23. SQL>

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

这里写图片描述


记录型变量 %rowtype

定义

%rowtype 记录型变量 代表表中的一行,而一行中有很多列。

举例

emp_rec emp%rowtype
  • 1

记录型变量分量的引用

 
  1. 手工赋值

  2. emp_rec.ename := 'ADMIN'

  3. 或者

  4. into 赋值之后, 使用 emp_rec.enmae获取

  • 1
  • 2
  • 3
  • 4

栗子

 
  1. SQL> set serveroutput on ;

  2. SQL> declare

  3. 2 --打印 7369 员工的姓名和薪水信息

  4. 3

  5. 4 --定义记录型变量 ,得到 7369一行的所有列的信息

  6. 5 v_emp_rec emp%rowtype ;

  7. 6 begin

  8. 7 --业务sql

  9. 8 select * into v_emp_rec from emp a where a.empno=7369;

  10. 9 --获取姓名和薪水,并打印

  11. 10 dbms_output.put_line(v_emp_rec.ename||'的薪水是'||v_emp_rec.sal);

  12. 11 end ;

  13. 12 /

  14.  
  15. SMITH的薪水是800

  16.  
  17. PL/SQL procedure successfully completed

  18.  
  19. SQL>

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

这里写图片描述


if语句的使用

形式一

 
  1. if 条件 then 语句1;

  2. 语句2;

  3. end if;

  • 1
  • 2
  • 3

形式2

 
  1. if 条件 then 语句1;

  2. else 语句2;

  3. end if;

  • 1
  • 2
  • 3

形式3 注意elsif

elsif没有e 且是连在一起的

 
  1. if 条件 then 语句;

  2. elsif 条件 then 语句;

  3. else 语句;

  4. end if;

  • 1
  • 2
  • 3
  • 4

栗子

 
  1. /*

  2. 判断用户从键盘输入的数字

  3. 1、如何使用if语句

  4. 2、接收一个键盘输入(从键盘上获取的都是字符串)

  5. */

  6. set serveroutput on ;

  7. --接收一个键盘输入

  8. --num :地址值,含义是:在该地址上保存了输入的值 ,获取地址值上对应的值,需要使用 &

  9. accept num prompt'请输入一个数字';

  10.  
  11. declare

  12. --定义变量保存用户从键盘输入的数字 这里并没有对异常进行处理,输入字符串,会抛异常

  13. pnum number := #

  14. begin

  15. --执行if 语句进行条件判断 提示信息 不能使用双引号,必须使用单引号,否则报错

  16. if pnum = 0 then dbms_output.put_line('输入的数字是'||pnum);

  17. elsif pnum = 1 then dbms_output.put_line('输入的数字是'||pnum);

  18. elsif pnum = 2 then dbms_output.put_line('输入的数字是'||pnum);

  19. else dbms_output.put_line('其他数字'||pnum);

  20. end if;

  21. end;

  22. /

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

这里写图片描述


循环语句的使用

形式1

 
  1. while 条件 loop

  2. .....

  3. end loop;

  • 1
  • 2
  • 3

当条件满足时,执行循环体,不满足时,退出循环体。

 
  1. set serveroutput on ;

  2.  
  3. declare

  4. --定义循环变量 初始值为1 记得加上变量的类型 否则报错

  5. v_num number :=1 ;

  6. begin

  7. while v_num <=5 loop

  8. --打印 v_num

  9. dbms_output.put_line(v_num);

  10. --变量+1 不能使用v_num++的形式,oracle不支持这种写法

  11. v_num :=v_num+1 ;

  12. end loop;

  13. end;

  14. /

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

这里写图片描述


形式2

 
  1. loop

  2. exit [when 条件];

  3. .....

  4. end loop;

  • 1
  • 2
  • 3
  • 4

条件成立时,退出循环体,不成立时执行循环体。

 
  1. set serveroutput on ;

  2. declare

  3. --定义循环变量

  4. v_num number :=1 ;

  5. begin

  6. loop

  7. exit when v_num>10 ; --记得加 标点符号

  8. dbms_output.put_line(v_num);

  9. v_num := v_num+1;

  10. end loop ;

  11. end;

  12. /

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

这里写图片描述

推荐使用第二种,因为在控制游标的时候比较方便。


形式3

 
  1. for i in 1 .. 10 loop

  2. 语句;

  3. end loop;

  • 1
  • 2
  • 3
 
  1. set serveroutput on ;

  2. declare

  3. v_num number :=1 ;

  4. begin

  5. for i in 1 .. 5 loop -- .. 前后可以有空格 也可以没有

  6. dbms_output.put_line(v_num);

  7. v_num := v_num+1;

  8. end loop;

  9. end;

  10. /

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

这里写图片描述


游标

游标的概念

游标(游标):一个结果集 
不能把一个集合赋值给一个基本型变量,否则会抛出 too many rows的异常。

可带参数 ,可不带参数。


游标的语法

这里写图片描述

 
  1. cursor 游标名 [(参数名 数据类型[,参数名,数据类型]...)]

  2. is select 语句;

  • 1
  • 2

比如

cursor c1 is select enama from emp;
  • 1

操作游标的步骤

打开游标

open c1;(打开游标,执行查询 即执行游标的查询语句)
  • 1

取一行游标的值

fetch c1 into v_name;(  取一行到变量中)
  • 1

最开始时,游标指向集合的第一条记录,记录返回后,指针移动到下一条记录。

这里写图片描述

关闭游标

close c1 ;(关闭游标释放资源)
  • 1

栗子

使用游标查询员工姓名和工资,并打印。

Loop循环游标

推荐写法

 
  1. ....

  2. Loop

  3. Fetch 游标名 InTo 临时记录或属性类型变量;

  4. Exit When 游标名%NotFound;

  5. End Loop;

  6. .....

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
 
  1. /*使用游标查询员工姓名和工资,并打印*/

  2. set serveroutput on ;

  3.  
  4. declare

  5. --定义 游标 推荐将游标和游标对应的变量写在一块,比较好维护

  6. cursor cemp is select e.ename , e.sal from emp e ;

  7. --定义游标对应的变量 这里使用引用型变量

  8. v_name emp.ename%type ;

  9. v_sal emp.sal%type;

  10.  
  11. begin

  12. --打开游标

  13. open cemp ;

  14. --循环获取游标中的值

  15. loop

  16. --取一条数据

  17. fetch cemp into v_name,v_sal ; -- into 后变量的顺序一定要和定义游标时select的字段对应

  18. exit when cemp%notfound ;

  19. --打印

  20. dbms_output.put_line(v_name||'的工资是'||v_sal);

  21. end loop;

  22. --关闭游标

  23. if cemp%isopen then

  24. close cemp;

  25. dbms_output.put_line('Closing...');

  26. end if;

  27. end;

  28. /

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

这里写图片描述


或者

For 循环游标

循环游标隐式打开游标,自动滚动获取一条记录,并自动创建临时记录类型变量存储记录。处理完后自动关闭游标。

 
  1. For 变量名 In 游标名

  2. Loop

  3. 数据处理语句;

  4. End Loop;

  • 1
  • 2
  • 3
  • 4
 
  1. /*使用游标查询员工姓名和工资,并打印*/

  2. set serveroutput on ;

  3.  
  4. declare

  5. --定义 游标 推荐将游标和游标对应的变量写在一块,比较好维护

  6. cursor cemp is select e.ename , e.sal from emp e ;

  7. --定义游标对应的变量 这里使用引用型变量

  8. v_name emp.ename%type ;

  9. v_sal emp.sal%type;

  10.  
  11. begin

  12. --循环获取游标中的值

  13. for c in cemp LOOP

  14. select c.ename , c.sal into v_name,v_sal from dual;

  15. dbms_output.put_line(v_name||'的工资是'||v_sal);

  16. end loop;

  17.  
  18. end;

  19. /

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

这里写图片描述


实例:给员工涨工资

 
  1. /*

  2. 给员工涨工资。总裁涨1000,经理涨800,普通员工涨400;

  3. */

  4. declare

  5. --定义游标

  6. cursor c_emp is select empno,empjob from emp;

  7. --定义游标对应的变量

  8. p_empno emp.empno%type;

  9. p_empjob emp.empjob%type;

  10. begin

  11. --打开游标

  12. open c_emp;

  13. --取出一个员工

  14. loop

  15. fetch c_emp into p_empno,p_empjob;

  16. exit when c_emp%notfound;

  17. --判断职位

  18. if p_empjob = 'PRESIDENT' then

  19. update emp set sal=sal+1000 where empno=p_empno;

  20. elsif p_empjob = 'MANAGER' then

  21. update emp set sal= sal+800 where empno = p_empno;

  22. else

  23. update emp set sal=sal+400 where empno=p_empno;

  24. end if;

  25. end loop;

  26. --关闭游标

  27. if c_emp%isopen then

  28. close c_emp;

  29. end if ;

  30. --提交事务,oracle默认的隔离级别是read committed ,不同连接只能读取提交之后的;

  31. commit;

  32. dbms_output.put_line('涨工资完成');

  33. end;

  34. /

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34

游标的属性

游标的4个属性

  • %Found :Fetch语句(获取记录)执行情况 True or False
  • %NotFound : 最后一条记录是否提取出 True or False
  • %ISOpen : 游标是否打开True or False
  • %RowCount :游标当前提取的行数

游标数的限制

Oracle默认的一个会话最多可以打开300个游标.

可以通过使用 show parameter cursors; (表示模糊查询 %cursors%)

查看包含cursors的参数设置

这里写图片描述

修改游标数的限制

使用DBA权限的用户

alter system set open_cursors=400 scope = both;
  • 1

其中scope的取值:both,memory,spfile

  • memory:表示只更改当前实例,不更改参数文件
  • spfile:表示只更改参数文件,不更改当前示例,数据库服务需要重启
  • both:表示上边两个同时更改

带参数的游标

注意 定义(带参数) 和打开游标(传递参数)时的区别。其余的和无参的游标一样。

 
  1. set serveroutput on ;

  2.  
  3. declare

  4. --定义带参数的游标

  5. cursor cemp (dno number) is select ename from emp where deptno =dno;

  6. --定义游标中对应的变量

  7. v_name emp.ename%type;

  8.  
  9. begin

  10. --打开游标 传入对应的入参

  11. open cemp(10);

  12. --loop循环 遍历游标

  13. loop

  14. fetch cemp into v_name;

  15. exit when cemp%notfound;

  16. dbms_output.put_line(v_name);

  17. end loop;

  18. close cemp;

  19. end;

  20. /

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

这里写图片描述


显式游标和隐式游标

上面介绍的是显式游标,下面说下隐式游标

DML操作和单行SELECT语句会使用隐式游标,它们是:

  • 插入操作:INSERT
  • 更新操作:UPDATE
  • 删除操作:DELETE
  • 单行查询操作:SELECT … INTO …

隐式游标的名字为SQL,这是由ORACLE 系统定义的。 
对于隐式游标的操作,如定义、打开、取值及关闭操作,都由ORACLE 系统自动地完成,无需用户进行处理。用户只能通过隐式游标的相关属性,来完成相应的操作。

格式调用为: SQL%

隐式游标可以使用名字SQL来访问,但要注意,通过SQL游标名总是只能访问前一个DML操作或单行SELECT操作的游标属性。所以通常在刚刚执行完操作之后,立即使用SQL游标名来访问属性。

游标的属性有四种,如下所示。

 
  1. 隐式游标的属性 返回值类型 意 义

  2. SQL%ROWCOUNT 整型 代表DML语句成功执行的数据行数

  3. SQL%FOUND 布尔型 值为TRUE代表插入、删除、更新或单行查询操作成功

  4. SQL%NOTFOUND 布尔型 与SQL%FOUND属性返回值相反

  5. SQL%ISOPEN 布尔型 DML执行过程中为真,结束后为假

  • 1
  • 2
  • 3
  • 4
  • 5
 
  1. DECLARE

  2. v_rows NUMBER;

  3. BEGIN

  4. --更新数据

  5. UPDATE employees SET salary = 30000

  6. WHERE department_id = 90 AND job_id = 'AD_VP';

  7. --获取默认游标的属性值

  8. v_rows := SQL%ROWCOUNT;

  9. DBMS_OUTPUT.PUT_LINE('更新了'||v_rows||'个雇员的工资');

  10.  
  11. --删除指定雇员;如果部门中没有雇员,则删除部门

  12. DELETE FROM employees WHERE department_id=v_deptno;

  13. IF SQL%NOTFOUND THEN

  14. DELETE FROM departments WHERE department_id=v_deptno;

  15. END IF;

  16. END;

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

例外

例外的概念

在oracle中错误被叫做例外:分为系统例外和自定义例外。

系统例外 比如: 
- No_data_found 没有找到数据 
- Too_many_rows select..into语句匹配多个行 
- Zero_Divide 被零除 
- Value_error 算数或转换错误,算术错误比如说负数开平方 
- Timeout_on_resource 在等待资源时发生超时,常见于分部署数据库。


系统例外之no_data_found

 
  1. /*系统例外 no_data_found*/

  2. set serveroutput on ;

  3. --查询empno为222的姓名

  4. declare

  5. --定义引用型变量

  6. v_name emp.ename%type;

  7.  
  8. begin

  9. --业务sql

  10. select ename into v_name from emp where empno=222;

  11. exception

  12. when no_data_found then dbms_output.put_line('no data found');

  13. when others then dbms_output.put_line('others exception');

  14. end;

  15. /

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

这里写图片描述


系统例外之too_many_rows

 
  1. /*系统例外 too_many_rows */

  2. set serveroutput on ;

  3. --查询 10号部门的员工

  4. declare

  5. --定义引用型变量

  6. v_name emp.ename%type;

  7.  
  8. begin

  9. --业务sql

  10. select ename into v_name from emp where deptno=10;

  11. exception

  12. when too_many_rows then dbms_output.put_line('too_many_rows');

  13. when others then dbms_output.put_line('others exception');

  14. end;

  15. /

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

这里写图片描述


系统例外之zero_divide

 
  1. /*系统例外 zero_divide */

  2. set serveroutput on ;

  3. declare

  4. --定义引用型变量

  5. v_num number;

  6.  
  7. begin

  8. --业务sql

  9. v_num := 1/0;

  10. exception

  11. when zero_divide then dbms_output.put_line('zero_divide');

  12. dbms_output.put_line('0不能做除数');

  13. when others then dbms_output.put_line('others exception');

  14. end;

  15. /

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

这里写图片描述


系统例外之value_error

 
  1. /*系统例外 value_error */

  2. set serveroutput on ;

  3. declare

  4. --定义引用型变量

  5. v_num number;

  6.  
  7. begin

  8. --业务sql

  9. v_num := 'xiaogongjiang';

  10. exception

  11. when value_error then dbms_output.put_line('value_error');

  12. when others then dbms_output.put_line('others exception');

  13. end;

  14. /

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

这里写图片描述


自定义例外

步骤

  1. 定义变量,类型是exception
  2. 使用raise抛出自定义意外

栗子

 
  1. --自定义例外:(没有查找到的例外no_emp_found)

  2. declare

  3. cursor c_emp is select ename from emp where deptno=50;

  4. p_ename emp.ename%type;

  5. --定义一个例外

  6. no_emp_found exception;

  7. begin

  8. open c_emp;

  9. --获取一条记录

  10. fetch c_emp into p_ename;

  11. --如果没有查到则抛出自定义例外

  12. if c_emp%notfound then

  13. raise no_emp_found;

  14. end if;

  15. --此处,当前一句抛出例外执行完exception后,

  16. --oracle会自动启动一个pmon(process monitor)的一个进程

  17. --将pl/sql程序中未关闭的资源释放

  18. --所以 close c_emp; 还是会执行的

  19. close c_emp;

  20. --捕获例外

  21. exception

  22. when no_emp_found then dbms_output.put_line('没有该部门下的员工');

  23. when others then dbms_output.put_line('其他例外');

  24. end;

  25. /

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

这里写图片描述


PL/SQL调测

可以在pl/sql工具中 新建测试窗口,调测过程和调测存过的方式一样,可以一步一步的跟踪sql执行的过程。

这里写图片描述

案例

运用瀑布模型完成PLSQL程序的设计

瀑布模型

  • 1.需求分析
  • 2.设计 
    • 2.1概要设计
    • 2.2详细设计
  • 3.编码coding
  • 4.测试Testing
  • 5.上线(部署) 
    这里写图片描述

拿到一个需求后,不找着急写程序,先分析明白了

  • sql语句
  • 变量初始值
  • 变量如何获取
  • ….

案例:统计每年入职的员工人数

分析过程: 
每年入职的员工人数 
1.所有的年份集合–>定义cursor保存 
2.每个员工的入职年份–>定义v_hiredate保存 
3.每年入住的人数总和–>定义v_count_XX保存

先把大框架打起来

 
  1. declare

  2. ---定义变量balabala

  3. begin

  4. ---业务逻辑

  5. end;

  6. /

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

然后再一步一步的填充业务逻辑。

 
  1. set serveroutput on ;

  2. declare

  3.  
  4. --入职年份的游标

  5. cursor hiredate_cursor is select to_char(hiredate,'yyyy') from emp;

  6.  
  7. --定义入职年份

  8. v_hiredate varchar2(4);

  9.  
  10. --定义每个年份入职人员总数

  11. v_count_80 number :=0;

  12. v_count_81 number :=0;

  13. v_count_82 number :=0;

  14. v_count_87 number :=0;

  15.  
  16. begin

  17. --打开游标

  18. open hiredate_cursor ;

  19. --loop循环遍历游标

  20. loop

  21. --取值

  22. fetch hiredate_cursor into v_hiredate ;

  23. exit when hiredate_cursor%notfound ;

  24.  
  25. if v_hiredate = '1980' then v_count_80 :=v_count_80+1 ;

  26. elsif v_hiredate = '1981' then v_count_81 :=v_count_81+1 ;

  27. elsif v_hiredate ='1982' then v_count_82 :=v_count_82+1;

  28. elsif v_hiredate ='1987' then v_count_87 :=v_count_87+1;

  29. end if ;

  30. end loop;

  31. --关闭游标

  32. close hiredate_cursor;

  33.  
  34. dbms_output.put_line('总共入职人数:'||(v_count_80+v_count_81+v_count_82+v_count_87));--加括号

  35. dbms_output.put_line('80入职的:'||v_count_80);

  36. dbms_output.put_line('81入职的:'||v_count_81);

  37. dbms_output.put_line('82入职的:'||v_count_82);

  38. dbms_output.put_line('87入职的:'||v_count_87);

  39. end;

  40. /

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40

这里写图片描述


案例:员工涨工资问题

案例2:涨工资问题,从最低工资的员工开始涨起,没人涨10%,工资总额不能超过50000,返回涨工资的人数和涨后的工资总额.

 
  1. /*

  2. 分析:

  3. 1、用到的sql语句:

  4. select empno,sal from emp order by sal;

  5. select sum(sal) into totalsal from emp;

  6. 2、需要声明的变量:

  7. 工资总额:totalsal 涨工资人数:count

  8. 3、循环推出的条件:

  9. 工资总额>5W or 全部员工都涨完工资*/

  10.  
  11. declare

  12. cursor cemp is select empno,sal from emp order by sal;

  13. p_no emp.empno%type;

  14. p_sal emp.sal%type;

  15. countemp number:=0;--涨工资人数

  16. totalsal emp.sal%type;

  17. begin

  18. --获取初始工资总额

  19. select sum(sal) into totalsal from emp;

  20. open cemp;

  21. --判断当前工资总额是否大于5W

  22. if totalsal<50000 then

  23. loop

  24. fetch cemp into p_no,p_sal;

  25. exit when cemp%notfound;

  26. --获取当前员工涨工资后的工资总额

  27. --如果工资总额超过5W直接退出循环

  28. exit when (totalsal+p_sal*0.1)>50000;

  29. update emp set sal=sal*1.1 where empno=p_no;

  30. --涨工资人数加1

  31. countemp:=countemp+1;

  32. end loop;

  33. end if;

  34. close cemp;

  35. commit;

  36. dbms_output.put_line('共有'countemp'人涨工资,工资总额为:'totalsal);

  37. end;

  38. /

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38

案例:涉及两张表的员工涨工资问题

这里写图片描述

 
  1. declare

  2. --获取所有部门

  3. cursor c_dept is select deptno from dept;

  4. --各部门编号

  5. p_dno dept.deptno%type;

  6. --各部门总金额

  7. p_totalsal number;

  8. --各工资分段人数:

  9. num1 number;

  10. num2 number;

  11. num3 number;

  12. --定义一个游标存放该部门下所有员工(带参数)

  13. cursor c_emp(dno number) is select sal from emp where deptno = dno;

  14. --员工的薪水

  15. p_sal number;

  16. begin

  17. --打开部门游标

  18. open c_dept;

  19. loop

  20. --部门循环

  21. fetch c_dept

  22. into p_dno;

  23. exit when c_dept%notfound;

  24. --初始化变量:

  25. p_totalsal := 0;

  26. num1 := 0;

  27. num2 := 0;

  28. num3 := 0;

  29. -- --获取本部门下所有员工,打开员工游标

  30. open c_emp(p_dno);

  31. loop

  32. --员工循环

  33. fetch c_emp into p_sal;

  34. exit when c_emp%notfound;

  35. if p_sal < 3000 then

  36. num1 := num1 + 1;

  37. elsif p_sal >= 3000 and p_sal <= 6000 then

  38. num2 := num2 + 1;

  39. elsif p_sal > 6000 then

  40. num3 := num3 + 1;

  41. end if;

  42. --获取总金额

  43. p_totalsal := p_totalsal + p_sal;

  44. end loop;

  45. close c_emp;

  46. --保存统计结果到sal_msg

  47. insert into sal_msg values (p_dno, num1, num2, num3, p_totalsal);

  48. end loop;

  49. close c_dept;

  50. commit;

  51. dbms_output.put_line('统计完成');

  52. end;

  53. /

转载 https://blog.youkuaiyun.com/thy822/article/details/80967434

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值