Oracle学习(十一):PL/SQL

版权声明:本文为博主原创文章。未经博主同意不得转载。 https://blog.csdn.net/v123411739/article/details/30231659

1.知识点:能够对照以下的录屏进行阅读

PL/SQL程序结构
declare
      说明部分    (变量说明,光标申明,例外说明 〕
begin
      语句序列   (DML语句〕… 
exception
      例外处理语句   
End;
/
------------------------------------------------------------------
--第一个PL/SQL程序:HelloWorld
set serveroutput on	--假设要在屏幕上输出信息,须要将serveroutput开关打开
declare
  --变量的说明
begin
   --程序体
   --程序包package
   dbms_output.put_line('Hello World');
end;
/
------------------------------------------------------------------

--引用型变量: 查询并打印7839的姓名和薪水

set serveroutput on

declare 
  --定义变量,
  pename emp.ename%type;		--将pename的类型设置为emp表中ename的类型
  psal   emp.sal%type;	--将psal的类型设置为emp表中sal的类型

begin

  --查询: 
  --ename,sal into pename,psal:将查询的ename,sal分别赋值给pename,psal按顺序赋值
  select ename,sal into pename,psal from emp where empno=7839;
  
  --打印
  dbms_output.put_line(pename||'的薪水是'||psal);

end;
/
------------------------------------------------------------------

--记录型变量 查询并打印7839的姓名和薪水

set serveroutput on

declare 
  --定义变量,代表一行
  emp_rec emp%rowtype;
begin
  --得到一行,赋值给emp_rec
  select * into emp_rec from emp where empno=7839;

  dbms_output.put_line(emp_rec.ename||'的薪水是'||emp_rec.sal);

end;
/
------------------------------------------------------------------
--if语句:推断用户输入的数字

set serveroutput on

/*
1.提示信息
2. 接收键盘输入
num 是一个地址值

SQL优化: num绑定变量(尽量使用绑定变量)
select * from emp where deptno=10;  --> 运行计划
select * from emp where deptno=20;  --> 运行计划
-->
select * from emp where deptno=#

*/
accept num prompt '请输入一个数字';

declare
  --变量保存输入的数字
  pnum number  := #
begin
  --推断
  if pnum = 0 then
    dbms_output.put_line('您输入的是0');
  elsif pnum = 1 then
    dbms_output.put_line('您输入的是1');
  elsif pnum = 2 then
    dbms_output.put_line('您输入的是2');
  else
    dbms_output.put_line('其它数字');
  end if;    
end;
/
------------------------------------------------------------------
--循环: 打印1~10,有3中方法。见下图

set serveroutput on

declare
  pnum number := 1;	--给pnum赋值1,pnum类型为number
begin
  loop
    --退出:成立退出,不成立循环
    exit when pnum > 10;
    
    --隐式转换。number转varchar2
    dbms_output.put_line(pnum);
    
    pnum := pnum + 1;

  end loop;
end;
/
------------------------------------------------------------------

光标(Cursor)==ResultSet

说明光标语法:
CURSOR  光标名  [ (參数名  数据类型[,參数名 数据类型]...)]
      IS  SELECT   语句;
用于存储一个查询返回的多行数据
比如:
cursor c1 is select ename from emp;
打开光标:      open c1;    (打开光标运行查询)
取一行光标的值:fetch c1 into pjob; (取一行到变量中)
关闭光标:      close  c1;(关闭游标释放资源)
注意: 上面的pjob必须与emp表中的job列类型一致:
定义:pjob emp.empjob%type;
------------------------------------------------------------------
--光标: 使用游标查询员工姓名和工资。并打印

/*
光标的3个属性:
%isopen 是否被打开:打开true,关闭false;
%rowcount 行数
%notfound 是否有值:没有值true,有值false;
*/

set serveroutput on

declare
  --光标
  cursor cemp is select ename,sal from emp;
  pename emp.ename%type;		--将用来接收的变量类型设置为和查询的类型同样
  psal   emp.sal%type;	
begin
  open cemp;		--打开光标运行查询
  loop
    --从集合中取值
    fetch cemp into pename,psal;
    --光标中没有值时退出
    exit when cemp%notfound;
    
    dbms_output.put_line(pename||'的薪水是'||psal);

  end loop;
  close cemp;
end;
/
------------------------------------------------------------------
--光标练习:给员工涨工资  总裁1000 经理800 其它400
--光标同一时候能打开的最大数量默觉得300个
/*
SQL> show parameters cursor

NAME                                 TYPE        VALUE
------------------------------------ ----------- --------
cursor_sharing                       string      EXACT
cursor_space_for_time                boolean     FALSE
open_cursors                         integer     300
session_cached_cursors               integer     20

*/
set serveroutput on
declare
  --光标代表员工
  cursor cemp is select empno,job from emp;
  pempno emp.empno%type;
  pjob   emp.job%type;
begin
  open cemp;
  loop
    fetch cemp into pempno,pjob;
    exit when cemp%notfound;
    
    --推断
    if pjob = 'PRESIDENT' then update emp set sal=sal+1000 where empno=pempno;	--总裁涨1000
      elsif pjob = 'MANAGER' then update emp set sal=sal+800 where empno=pempno; --经理涨800
      else update emp set sal=sal+400 where empno=pempno;	--其它涨400
    end if;
  end loop;
  close cemp;
  
  --提交: 隔离级别
  commit;
  
  dbms_output.put_line('完毕');
end;
/
------------------------------------------------------------------
--带參数的光标:查询某个部门的员工姓名 

set serveroutput on

declare
  --定义一个带參数的光标
  cursor cemp(pdno number) is  select ename from emp where deptno=pdno;
  pename emp.ename%type;
begin
  open cemp(20);	--打开光标,并传递參数
  loop
    fetch cemp into pename;
    exit when cemp%notfound;
    
    dbms_output.put_line(pename);


  end loop;
  close cemp;
end;
/
------------------------------------------------------------------
Oracle的异常处理
1.系统定义例外
No_data_found    (没有找到数据)
Too_many_rows          (select …into语句匹配多个行) 
Zero_Divide   ( 被零除)
Value_error     (算术或转换错误)
Timeout_on_resource      (在等待资源时发生超时)

--举例 Zero_Divide   ( 被零除)

set serveroutput on
declare
  pnum number;
begin

  pnum := 1/0;

exception
  when Zero_Divide then dbms_output.put_line('1: 0不能做被除数');
                        dbms_output.put_line('2: 0不能做被除数');
  when Value_error then dbms_output.put_line('算术错');
  when others then dbms_output.put_line('其它例外');
end;
/

用户自己定义的例外
--自己定义例外: 查询50号部门的员工姓名

set serveroutput on

declare
  cursor cemp is select ename from emp where deptno=50;
  pename emp.ename%type;
  
  --自己定义例外
  no_emp_found exception;
begin
  open cemp;
  --取一个员工
  fetch cemp into pename;
  if cemp%notfound then 
    raise no_emp_found;
  end if;

/*
  if cemp%isopen then 
    close no_emp_found;
  end if;
*/
  close cemp;
  
exception 
  when no_emp_found then dbms_output.put_line('没有找到员工');
  when others then dbms_output.put_line('其它例外');
  
end;
/



图:循环的3种写法,文中採用右上角的方法。

2.在Sqlplus下实际运行的结果录屏

SQL> --第一个PL/SQL程序:HelloWorld
SQL> set serveroutput on	--假设要在屏幕上输出信息,须要将serveroutput开关打开
SQL> declare
  2    --变量的说明
  3  begin
  4     --程序体
  5     --程序包package
  6     dbms_output.put_line('Hello World');
  7  end;
  8  /
 
Hello World
 
PL/SQL procedure successfully completed
 
SQL> 
SQL> --引用型变量: 查询并打印7839的姓名和薪水
SQL> set serveroutput on
SQL> declare
  2    --定义变量,
  3    pename emp.ename%type;		--将pename的类型设置为emp表中ename的类型
  4    psal   emp.sal%type;	--将psal的类型设置为emp表中sal的类型
  5  
  6  begin
  7  
  8    --查询:
  9    --ename,sal into pename,psal:将查询的ename,sal分别赋值给pename。psal按顺序赋值
 10    select ename,sal into pename,psal from emp where empno=7839;
 11  
 12    --打印
 13    dbms_output.put_line(pename||'的薪水是'||psal);
 14  
 15  end;
 16  /
 
KING的薪水是5000
 
PL/SQL procedure successfully completed
 
SQL> 
SQL> -记录型变量 查询并打印7839的姓名和薪水
SQL> set serveroutput on
SQL> declare
  2    --定义变量,代表一行
  3    emp_rec emp%rowtype;
  4  begin
  5    --得到一行,赋值给emp_rec
  6    select * into emp_rec from emp where empno=7839;
  7  
  8    dbms_output.put_line(emp_rec.ename||'的薪水是'||emp_rec.sal);
  9  
 10  end;
 11  /
 
KING的薪水是5000
 
PL/SQL procedure successfully completed
 
SQL> 
SQL> --if语句:推断用户输入的数字
SQL> set serveroutput on
SQL> /*
  2  1.提示信息
  3  2. 接收键盘输入
  4  num 是一个地址值
  5  
  6  SQL优化: num绑定变量(尽量使用绑定变量)
  7  select * from emp where deptno=10;  --> 运行计划
  8  select * from emp where deptno=20;  --> 运行计划
  9  -->
 10  select * from emp where deptno=#
 11  
 12  */
 13  accept num prompt '请输入一个数字';
SQL> declare
  2    --变量保存输入的数字
  3    pnum number  := #
  4  begin
  5    --推断
  6    if pnum = 0 then
  7      dbms_output.put_line('您输入的是0');
  8    elsif pnum = 1 then
  9      dbms_output.put_line('您输入的是1');
 10    elsif pnum = 2 then
 11      dbms_output.put_line('您输入的是2');
 12    else
 13      dbms_output.put_line('其它数字');
 14    end if;
 15  end;
 16  /
 
您输入的是2
 
PL/SQL procedure successfully completed
 
SQL> 
SQL> --循环: 打印1~10
SQL> set serveroutput on
SQL> declare
  2    pnum number := 1;	--给pnum赋值1,pnum类型为number
  3  begin
  4    loop
  5      --退出:成立退出,不成立循环
  6      exit when pnum > 10;
  7  
  8      --隐式转换,number转varchar2
  9      dbms_output.put_line(pnum);
 10  
 11      pnum := pnum + 1;
 12  
 13    end loop;
 14  end;
 15  /
 
1
2
3
4
5
6
7
8
9
10
 
PL/SQL procedure successfully completed
 
SQL> 
SQL> -光标: 使用游标查询员工姓名和工资。并打印
SQL> /*
  2  光标的3个属性:
  3  %isopen 是否被打开:打开true,关闭false;
  4  %rowcount 行数
  5  %notfound 是否有值:没有值true,有值false。
  6  */
SQL> set serveroutput on
SQL> declare
  2    --光标
  3    cursor cemp is select ename,sal from emp;
  4    pename emp.ename%type;		--将用来接收的变量类型设置为和查询的类型同样
  5    psal   emp.sal%type;
  6  begin
  7    open cemp;		--打开光标运行查询
  8    loop
  9      --从集合中取值
 10      fetch cemp into pename,psal;
 11      --光标中没有值时退出
 12      exit when cemp%notfound;
 13  
 14      dbms_output.put_line(pename||'的薪水是'||psal);
 15  
 16    end loop;
 17    close cemp;
 18  end;
 19  /
 
SMITH的薪水是800
ALLEN的薪水是1600
WARD的薪水是1250
JONES的薪水是2975
MARTIN的薪水是1250
BLAKE的薪水是2850
CLARK的薪水是2450
SCOTT的薪水是3000
KING的薪水是5000
TURNER的薪水是1500
ADAMS的薪水是1100
JAMES的薪水是950
FORD的薪水是3000
MILLER的薪水是1300
 
PL/SQL procedure successfully completed
 
SQL> --光标练习:给员工涨工资  总裁1000 经理800 其它400
SQL> --先查询没涨之前的
SQL> select * from emp;
 
EMPNO ENAME      JOB         MGR HIREDATE          SAL      COMM DEPTNO
----- ---------- --------- ----- ----------- --------- --------- ------
 7369 SMITH      CLERK      7902 1980/12/17     800.00               20
 7499 ALLEN      SALESMAN   7698 1981/2/20     1600.00    300.00     30
 7521 WARD       SALESMAN   7698 1981/2/22     1250.00    500.00     30
 7566 JONES      MANAGER    7839 1981/4/2      2975.00               20
 7654 MARTIN     SALESMAN   7698 1981/9/28     1250.00   1400.00     30
 7698 BLAKE      MANAGER    7839 1981/5/1      2850.00               30
 7782 CLARK      MANAGER    7839 1981/6/9      2450.00               10
 7788 SCOTT      ANALYST    7566 1987/4/19     3000.00               20
 7839 KING       PRESIDENT       1981/11/17    5000.00               10
 7844 TURNER     SALESMAN   7698 1981/9/8      1500.00      0.00     30
 7876 ADAMS      CLERK      7788 1987/5/23     1100.00               20
 7900 JAMES      CLERK      7698 1981/12/3      950.00               30
 7902 FORD       ANALYST    7566 1981/12/3     3000.00               20
 7934 MILLER     CLERK      7782 1982/1/23     1300.00               10
 
14 rows selected
 
SQL> --运行PLSQL语句
SQL> 
SQL> set serveroutput on
SQL> declare
  2    --光标代表员工
  3    cursor cemp is select empno,job from emp;
  4    pempno emp.empno%type;
  5    pjob   emp.job%type;
  6  begin
  7    open cemp;
  8    loop
  9      fetch cemp into pempno,pjob;
 10      exit when cemp%notfound;
 11  
 12      --推断
 13      if pjob = 'PRESIDENT' then update emp set sal=sal+1000 where empno=pempno;	--总裁涨1000
 14        elsif pjob = 'MANAGER' then update emp set sal=sal+800 where empno=pempno; --经理涨800
 15        else update emp set sal=sal+400 where empno=pempno;	--其它涨400
 16      end if;
 17    end loop;
 18    close cemp;
 19  
 20    --提交: 隔离级别
 21    commit;
 22  
 23    dbms_output.put_line('完毕');
 24  end;
 25  /
 
完毕
 
PL/SQL procedure successfully completed
 
SQL> --查询运行PLSQL语句后的薪水
SQL> select * from emp;
 
EMPNO ENAME      JOB         MGR HIREDATE          SAL      COMM DEPTNO
----- ---------- --------- ----- ----------- --------- --------- ------
 7369 SMITH      CLERK      7902 1980/12/17    1200.00               20
 7499 ALLEN      SALESMAN   7698 1981/2/20     2000.00    300.00     30
 7521 WARD       SALESMAN   7698 1981/2/22     1650.00    500.00     30
 7566 JONES      MANAGER    7839 1981/4/2      3775.00               20
 7654 MARTIN     SALESMAN   7698 1981/9/28     1650.00   1400.00     30
 7698 BLAKE      MANAGER    7839 1981/5/1      3650.00               30
 7782 CLARK      MANAGER    7839 1981/6/9      3250.00               10
 7788 SCOTT      ANALYST    7566 1987/4/19     3400.00               20
 7839 KING       PRESIDENT       1981/11/17    6000.00               10
 7844 TURNER     SALESMAN   7698 1981/9/8      1900.00      0.00     30
 7876 ADAMS      CLERK      7788 1987/5/23     1500.00               20
 7900 JAMES      CLERK      7698 1981/12/3     1350.00               30
 7902 FORD       ANALYST    7566 1981/12/3     3400.00               20
 7934 MILLER     CLERK      7782 1982/1/23     1700.00               10
 
14 rows selected
 
SQL> --对照涨之前,能够看出涨工资成功
SQL> 
SQL> --带參数的光标:查询某个部门的员工姓名
SQL> set serveroutput on
SQL> declare
  2    --定义一个带參数的光标
  3    cursor cemp(pdno number) is  select ename from emp where deptno=pdno;
  4    pename emp.ename%type;
  5  begin
  6    open cemp(20);	--打开光标。并传递參数
  7    loop
  8      fetch cemp into pename;
  9      exit when cemp%notfound;
 10  
 11      dbms_output.put_line(pename);
 12  
 13  
 14    end loop;
 15    close cemp;
 16  end;
 17  /
 
SMITH
JONES
SCOTT
ADAMS
FORD
 
PL/SQL procedure successfully completed
 
SQL> 
SQL> --举例 Zero_Divide   ( 被零除)
SQL> set serveroutput on
SQL> declare
  2    pnum number;
  3  begin
  4  
  5    pnum := 1/0;
  6  
  7  exception
  8    when Zero_Divide then dbms_output.put_line('1: 0不能做被除数');
  9                          dbms_output.put_line('2: 0不能做被除数');
 10    when Value_error then dbms_output.put_line('算术错');
 11    when others then dbms_output.put_line('其它例外');
 12  end;
 13  /
 
1: 0不能做被除数
2: 0不能做被除数
 
PL/SQL procedure successfully completed
 
SQL> 
SQL> --自己定义例外: 查询50号部门的员工姓名
SQL> set serveroutput on
SQL> declare
  2    cursor cemp is select ename from emp where deptno=50;
  3    pename emp.ename%type;
  4  
  5    --自己定义例外
  6    no_emp_found exception;
  7  begin
  8    open cemp;
  9    --取一个员工
 10    fetch cemp into pename;
 11    if cemp%notfound then
 12      raise no_emp_found;
 13    end if;
 14  
 15    close cemp;
 16  
 17  exception
 18    when no_emp_found then dbms_output.put_line('没有找到员工');
 19    when others then dbms_output.put_line('其它例外');
 20  
 21  end;
 22  /
 
没有找到员工
 
PL/SQL procedure successfully completed
 
SQL> spool off
Stopped spooling to c:\PLSQL.txt


3.样例

/*
实例1:统计每年入职的员工个数。

用到的SQL:
select to_char(hiredate,'yyyy') from emp;
*/
set serveroutput on
declare
  cursor cemp is select to_char(hiredate,'yyyy') from emp;
  phiredate varchar2(4);
  
  --计数器
  count80 number := 0;
  count81 number := 0;
  count82 number := 0;
  count87 number := 0;
begin
  open cemp;
  loop
    --取一个员工
    fetch cemp into phiredate;
    exit when cemp%notfound;
    
    --推断
    if phiredate = '1980' then count80:=count80+1;
      elsif phiredate = '1981' then count81:=count81+1;
      elsif phiredate = '1982' then count82:=count82+1;
      else count87 := count87+1;
    end if;      

  end loop;
  close cemp;
  
  --输出
  dbms_output.put_line('total:'||(count80+count81+count82+count87));
  dbms_output.put_line('1980:'|| count80);
  dbms_output.put_line('1981:'|| count81);
  dbms_output.put_line('1982:'|| count82);
  dbms_output.put_line('1987:'|| count87);
end;
/
------------------------------------------------------------------

/*
实例2:为员工长工资。从最低工资调起每人长10%,但工资总额不能超过5万元,
请计算长工资的人数和长工资后的工资总额。并输出输出长工资人数及工资总额。

可能的SQL:
员工:  select empno,sal from emp order by sal;
长工资后的工资总额:1. 对sal进行累加: 新的工资总额=旧的工资 + sal*0.1;
                2. sum(sal): 查询数据库
练习: 工资不能超过5w
*/
set serveroutput on
declare
  --员工
  cursor cemp is select empno,sal from emp order by sal;
  pempno emp.empno%type;
  psal   emp.sal%type;
  
  --长工资的人数
  countEmp number := 0;
  --工资总额
  salTotal number;
begin

   --涨前工资总额
  select sum(sal) into salTotal from emp;

  open cemp;
  loop
    --工资总额>5w
    exit when salTotal > 50000;
    --取一个员工
    fetch cemp into pempno,psal;
    exit when cemp%notfound;
    
    --涨工资
    update emp set sal=sal*1.1 where empno=pempno;
    --人数
    countEmp := countEmp +1;
    --工资总额
    salTotal := salTotal + psal * 0.1;
  end loop;
  close cemp;
  
  commit;

  --输出
  dbms_output.put_line('长工资的人数:'|| countEmp);
  dbms_output.put_line('工资总额:'|| salTotal);

end;
/
------------------------------------------------------------------
/*
实例3:用PL/SQL语言编写一程序,实现按部门分段(6000以上、(6000,3000)、3000元以下)
统计各工资段的职工人数、以及各部门的工资总额(工资总额中不包含奖金)

SQL语句:
部门:  select deptno from dept;
员工的工资: select sal from emp where deptno=???
工资总额:  select sum(sal) from emp where deptno=??

? */ set serveroutput on declare --部门 cursor cdept is select deptno from dept; pdno dept.deptno%type; --部门中的员工 cursor cemp(dno number) is select sal from emp where deptno=dno; psal emp.sal%type; --各个段的人数 count1 number;count2 number;count3 number; --部门的工资总额 salTotal number; begin open cdept; loop --取部门 fetch cdept into pdno; exit when cdept%notfound; --初始化 count1 :=0;count2:=0;count3:=0; select sum(sal) into salTotal from emp where deptno=pdno; --取部门中的员工 open cemp(pdno); loop fetch cemp into psal; exit when cemp%notfound; --推断 if psal<3000 then count1:=count1+1; elsif psal>=3000 and psal<6000 then count2:=count2+1; else count3:=count3+1; end if; end loop; close cemp; --保存当前部门 insert into msg1 values(pdno,count1,count2,count3,nvl(salTotal,0)); end loop; close cdept; commit; dbms_output.put_line('完毕'); end; /



猜你喜欢

转载自www.cnblogs.com/mqxnongmin/p/10548065.html