PLSQL(一)

简介: PLSQL(一)

plsql : procedure language sql 是Oracle特有的数据库编程语言,普通的sql可以实现对数据的增删改查,一条sql很难做到 即查询,又根据不同的条件筛选数据,修改数据,而PLSQL可以帮我们实现


基本语法#


分成三部分:

  1. 声明部分 (声明一些临时变量,后续使用)
  2. 可执行部分 (我们的业务逻辑)
  3. 异常处理部分(可执行部分出现的异常信息,并处理)


declare
    -- 声明部分
    自定义的变量名 变量类型; 
    -- 变量可以边声明变初始化
    v_name varchar(2) :='zhangsan';
begin
    -- 执行业务逻辑
exception
    -- 异常处理部分
end;


Oracle 常用的 变量类型#


  1. 定义数据表时使用的变量类型(varchar2 number) 都能用
  2. 列级变量-- 表名.列名%type
  3. 行级变量-- 表名%rowtype


PLSQL的 分支 选择 判断#


分支判断#


if 条件1 then 
-- 处理逻辑
elsif 条件2 then 
-- 处理逻辑
else
-- 处理逻辑
end if;


while循环:#


while  条件  loop
-- 处理逻辑
end loop;


例: 输出1-10


declare
    i number:=1;
    begin
    while i<=10 loop
      dbms_output.put_line(i);
      i:= i+1;
    end loop;
  end;


while 循环,不满足条件自动退出


for 循环#


for 自定义的变量X in [reverse反转终止值和初始值] 初始值..终止值 loop
-- 业务逻辑
end loop;


输出10 - 1:


declare
begin
  for i in reverse 1 ..10 loop   -- 起始的条件不需要声明
    dbms_output.put_line(i);
     --  i:=i+1;  循环遍历的下标   不能作为 赋值目标
  end loop;
end;


for循环中循环遍历的下标不能作为赋值的对象

for循环中,自定义的变量X 不用提前在 declare和begin之间声明

for循环的退出条件是 自定义的变量X 从起始值到终止值为止


loop 循环#


loop 
-- 业务逻辑
exit when 退出条件
end loop;


输出 1-10


declare
    i number := 1;
  begin
    loop
       i := i+1;
       dbms_output.put_line(i);
      exit when i=10;
    end loop;
  end;


loop退出循环的条件是 exit when 我们手动控制


  • 例: 查询7369的工资, 并且打印出来
    -- 知道什么是 引用类型(单列的值) 表名.列名%type
    -- 如何赋值 into


declare
      sval emp.sal%type;
   begin
      select sal into sval from EMP where EMPNO=7369;
      -- 打印
      dbms_output.put_line(sval);
   end;


  • 例 查询7369 的员工信息, 并且打印出来
    -- 关键是 找个变量把结果存起来 行级变量 %rowtype


--sql  select * from EMP where EMPNO=7369;
   declare
      vrow emp%rowtype;   -- 行级变量   表名%rowtype
    begin
      select * into vrow from EMP where EMPNO=7369;
       --  dbms_output.put_line(vrow);   不能直接打印行!!! 而是 行.列 打印出来
        dbms_output.put_line('员工名称'||vrow.ENAME||'员工编号'||vrow.EMPNO);
   end;


注意点: -- dbms_output.put_line(vrow); 不能直接打印行!!! 而是 行.列 打印出来


游标#


什么是游标? 能给我们带来什么?#


我们可以把游标理解成一个指针,或者一个箭头,他可以指向我们给定的结果集(这个结果集其实就是我们的sql select的查询结果),然后我们可以用plsql的代码,控制游标遍历这个结果集,在遍历的时候我们可以取出当前行,也能取出当前行的具体一列的值,然后我们根据取出的值进行判断,下一步是该 update表数据,还是delete表数据

最开始当游标指向这个结果集的时候,它指向的是结果集的表头部分,每当我们fetch一下,他就把下一行的数据(也就是第一行)给我们,再fetch 就给我们第二行


游标的分类#


  • 静态游标
  • 隐式游标
  • 没参数的,显示游标
  • 动态游标
  • 带参数的显示游标(ref)
  • 系统游标


静态游标和动态游标的最大区别是啥?#


  1. 静态游标在编译的时候就知道了结果集是什么, 因为它没有任何需要我们传递进去的参数
  2. 动态游标带参数,参数具体是几,只有真正运行的时候才知道,它的结果集是运行时才知道的,称它是动态游标


语法#


cursor 游标名 in 查询结果集 -- 不带参数
cursor 游标名[参数名, 参数类型] in 查询结果集  -- 带参数


隐式游标的使用:#


begin 
update t_student set sname='张三丰' where sid='10001';
if sql%found then
dbms_output.put_line('表已经更新');
end if;
end;


每条sql默认自带一个隐式游标,游标的名字是 sql, 自动打开/关闭


没参数的静态游标#


开发步骤;
1. 声明游标
  -- cursor 游标名 is  查询结果集
2. 打开游标
  -- open 游标名
3. 取出游标中的数据( 游标相当于鼠标,每次都是从表头开始的, fetch一下,往下移动一行,正好从第一行开始)
  -- fetch 游标名 into 变量
    -- 游标名%find   找到数据
    -- 游标名%notfound  没有找到数据
4. 关闭游标
  -- close 游标名`


  • 例 输出员工表中的员工姓名和工资(不带参数游标)


-- select ename,SAL from emp ;
-- 游标 -- 结果集: 所有员工
-- 声明一个变量,用来记录一行数据  %rowtype
declare
  -- 声明游标
  cursor vrows is  select * from emp ;
  -- 声明一个中间标量, 中转上面游标中获取的每一行
  vrow emp%rowtype;
  begin
  -- 开启游标
  open vrows;
     -- 遍历打印
      loop
        fetch vrows into vrow;    -- 取一条数据
        exit when vrows%notfound; -- 判断数据是否为空
        ---   再次强调一下,不能直接打印行
        -- dbms_output.out_line(vrow);
        ---
        dbms_output.put_line('姓名'||vrow.ename||'员工号'||vrow.EMPNO);  -- 这里输出的 内容,一定要存在于  上面的游标中
      end loop;
  -- 关闭游标
  close vrows;
end;


如果没有判断下一次获取的是否为空,当游标循环遍历到结果集的最后一行的时候,再次fetch,依然是最后一行的数据,还可能是内存溢出,所以判断条件紧跟在fetch之后,然后再是我的业务逻辑


系统游标#


--- 系统 引用游标


1. 声明游标
      -- 游标名  游标类型   sys_refcursor   (后面的 refcursor是个合成词)
  2. 打开游标,关联结果集
  3. 从游标中取数据
  4. 关闭游标


例:

-- 例 输出员工表中的员工姓名和工资(不带参数游标)


declare
-- 声明系统游标
vrows sys_refcursor;
-- 声明行级变量
vrow  emp%rowtype;
begin
  --  打开游标
  open vrows for select * from EMP ;  -- 系统游标在打开时,关联结果集
    -- 循环
    loop
        fetch vrows into vrow;  -- 获取值
        exit when vrows%notfound;
        dbms_output.put_line('姓名'||vrow.ename);
    -- 结束循环
    end loop;
-- 关闭游标
  close vrows;
end;


带参数的动态游标#


--- 输出指定部门下的员工的姓名和工资( 带参数的游标 )


/*
  游标: (操作的结果集) 指定部门的所有员工
  声明  一个变量记录一行数据
*/
declare
  -- 声明带有参数的游标
  cursor vrows(dno number) is select * from EMP where DEPTNO = dno;
  -- 声明 行级 中转变量
  vrow  emp%rowtype;
begin
   -- 打开游标  (传入参数)
      open vrows(10);
  -- 循环
      loop
         -- 取出数据
          fetch vrows into vrow ;
        -- 判断 结束循环
          exit when vrows%notfound;
        -- 输出结果
          dbms_output.put_line('姓名'||vrow.ENAME||'工资'||vrow.sal);
      end loop;
   -- 关闭游标
      close vrows;
end;


系统游标 和 不带参的静态游标#


  • 相同点: 都是指向了某个结果集,可以一条一条的遍历结果集,获取出每一条数据
  • 不同点: 获取结果集的位置不同
  • 普通游标获取操作集合的位置是 declare 和 begin 之间
  • 系统游标 在begin 和 end 之间 的打开游标位置 ,使用的关键字不再是 is 而是 for
  • 声明方式不同!
  • 普通游标可带参数
  • 系统游标的声明固定写法; 名 sys_refcursor;


for 循环遍历游标#


上面的例子都是使用loop循环遍历游标,控制退出的条件是 游标%notfound

下面使用for循环遍历游标


declare
    cursor v_rows is select * from emp;
  begin
    for  v_row in  v_rows loop
      dbms_output.put_line(v_row.JOB);
    end loop;
  end;


不用提前声明 需要中转每一行的变量

不用手动控制循环退出的条件


小案例#


  • 游标的小案例:
    -- .1 按照员工工作 给员工涨工资, 总裁涨1000 经理涨 800 其他人 400


/*    -- 查询经理信息
  select mgr from emp where mgr is not null;
  -- 不查询不为空的员工信息
*/
declare
  --   声明游标
  cursor vrows is select * from emp;
  -- 声明变量
  vrow emp%rowtype;
begin
   -- 打开游标
  open vrows;
    -- 遍历游标
    loop
        -- 获取游标的当前值
      fetch vrows into vrow;
      -- 判断是否为空
      exit when vrows%notfound;
      -- 不为空,就加工资
      if vrow.job = 'MANAGER' then
        -- 注意点:  set的字段是 不能是  中专变量名.列名  而直接是类名 , 因为我们是在更新表中的字段,而不是在更新中转的那个变量
        update emp set sal  = sal+400 where empno = vrow.EMPNO;   -- plsql里面的  等于 :=  和 sql里面的  等于 = 区分开
      elsif vrow.job = 'CLERK' then
        update emp set  sal  = sal+800 where empno = vrow.EMPNO;
      end if;
    end loop;
  close vrows;
end;


注意点 : plsql和sql混在一起写的时候,注意点就是 sql中的赋值运算符是 = 但是plsql中的赋值运算符是 := 别整混了


异常#


有啥用?#


通常异常用来处理我们在业务逻辑阶段可能出现的逻辑错误,我们捕获了相应的异常之后自己处理掉,不至于影响后续的操作


语法#


declare 
  -- 声明异常
begin
    -- 业务逻辑
exception   -- 声明下面是处理异常的部分
    -- 处理异常
    when 异常1 then
    -- 处理逻辑
    when 异常2 then
    -- 处理逻辑
end;


Oracle中异常的分类#


  • 系统异常


  • zero_divide : 除零异常
  • value_error : 类型转换异常
  • too_many_row: 查询查了多行的记录,但是却把它赋值给一个行级的变量
  • no_data_found: 没有找到记录
  • raise_application_error(状态码,'异常信息') : 运行时,系统异常


  • 用户自定义异常


用户自定义异常,分三步

  1. 声明异常
  2. raise 抛出异常
  3. 在exception后面捕获异常
  4. 我们操作的结果集只能来自游标的结果集,(下面解释)


#


  • 看下面的代码,查询一个不存在的人的信息,看着貌似会捕获处理我们自定义的异常,但是其实并不会! 因为执行到 select的时候就会出现异常 no_data_found, 直接停止往下执行,我们的逻辑根本不会触发


declare
  vrow emp%rowtype; -- 声明行级变量
  no_emp exception; -- 自定义异常
 begin
    select * into vrow from emp where EMPNO = 8888;  -- 如果这是 888 没有这个员工,执行到这句就会 抛出异常,而不会往下执行抛出 自定义的异常
    if vrow.SAL is not null then  -- vrow一直是有的,我们通过里面的一个字段判断他是否为空
       dbms_output.put_line('员工信息'||vrow.sal);  -- 注意这里的,依然不能使用  dbms 输出一整行
    else
      raise no_emp;  -- 抛出自定义的异常  (自己抛出,自己捕获)
    end if;
 exception
  when no_emp then
  dbms_output.put_line('没有该员工信息');
  when others then
  dbms_output.put_line('其他异常'||sqlerrm);   -- sqlerrm 输出其他异常
end;


  • 简单的select 查询没办法完成我们的需求, 因为它碰到异常会报错,但是游标不会!!! %found %notfound
  • 解决
  • 声明 行级变量
  • 声明游标(带参数), 获取要操作的结果集
  • 遍历游标, 判断,如果为空,抛出我们自定义的异常


declare 
      vrow emp%rowtype;   -- 声明行级变量
      --- 注意点: 游标的查询语句中去 *  再赋值给行级变量 (不然报错))
      cursor emp_Info(empno NUMBER) is select * from EMP; -- 声明游标,并获取结果集
      no_emp exception ; -- 自定义异常
     begin
      -- 打开=游标  传入参数
        open emp_Info(1);
        loop
            fetch emp_Info into vrow;  -- 获取元素
            if emp_Info%notfound then   -- 如果找不到
                 raise no_emp;  -- ,退出循环
                 exit ;         -- 退出循环
            end if;
        end loop;
      -- 关闭游标
        close emp_Info;
    -- 捕获异常
     exception
      when no_emp then
        dbms_output.put_line('没找到信息');
      when others then
        dbms_output.put_line('其他异常'||sqlerrm);
   end;


用户自定义异常的例子#


declare
begin
end;


捕获系统异常的例子:#


declare
    vi number;
  vrow emp%rowtype;  -- 行级变量
begin
  vi:=vi/0; -- 除零异常
  vi:='abc'; -- 类型转换异常
  select * into vrow from EMP;  -- 多行数据赋给 行级变量
  select * into vrow from DEPT where DEPTNO=123;  -- 没找到异常信息异常
exception
  when zero_divide then
    dbms_output.put_line('发生了除零异常');
  when value_error then
    dnms_output.put_line('类型转换异常');
  when to_many_rows then
    dbms_output.putline('多对 一  异常');
  when no_data_found  then
    dbms_output.put_line('没找到数据');
  when others then
    dbms_output.put_line('发生了其他异常');
end;


相关文章
|
4月前
PLSQL Developer Oracleclient
PLSQL Developer Oracleclient
48 1
|
7月前
|
SQL
PLSQL查看实际执行计划
PLSQL查看实际执行计划
|
Oracle 网络协议 关系型数据库
PLSQL Developer连接数据库报错ora-12514解决
PLSQL Developer连接数据库报错ora-12514解决
324 0
使用PLSQL Developer时中文乱码问题
如何解决使用PLSQL Developer时中文乱码问题
199 0
使用PLSQL Developer时中文乱码问题
|
存储 SQL Oracle
PLSQL(二)
PLSQL(二)
237 0
|
SQL Oracle 关系型数据库
[20180511]PLSQL与fetchsize.txt
[20180511]PLSQL与fetchsize.txt --//测试看看PLSQL中cursor的fetchsize: 1.环境: SCOTT@book> @ ver1 PORT_STRING                    VERSION  ...
1170 0
|
Oracle 关系型数据库 Linux
[20171231]PLSQL使用绑定变量.txt
[20171231]PLSQL使用绑定变量.txt --//一些应用程序开发使用的绑定变量是:1,:2之列的语句,要调优这些语句必须写成如下: 1.环境: SCOTT@book> @ &r/ver1 PORT_STRING              ...
1203 0
|
索引 关系型数据库 Oracle
SQLLDR 命令
SQL*Loader 是一个命令行下的操作工具,对应的操作系统命令是SQLLDR 语法:SQLLDR keyword=value [,keyword=value,...] 选项:   userid -- ORACLE 用户名/口令   control -- 控制文件名   log ...
1453 0