断弯刀

导航

oracle第四天笔记

游标

/*
  序列: ORACLE使用来模拟ID自动增长的
  
*/
create sequence seq_test4;

create table test2(
   tid number primary key,
   tname varchar2(10)    
);

insert into test2 values(seq_test4.nextval,'张三');
select * from test2;

/*
       PLSQL编程: 过程语言,编写一些复杂业务逻辑  
       
       输出星号:
                abs(y) + abs(x) <= m   
                
       vsal  emp.sal%type --引用型变量
       row  emp%rowtype  --记录型变量
       
       select sal into vsal from emp where empno=7788;
*/
declare
   m number := 3;             
begin
   for y in -m..m loop
     for x in -m..m loop
         if abs(y) + abs(x) <= m then
            dbms_output.put('*');
         else  
            dbms_output.put(' ');  
         end if;
     end loop;
     dbms_output.new_line();
   end loop;  
end;


/*
   游标(光标): 是用来操作查询结果集,相当于是JDBC中ResultSet
       
       语法: cursor 游标名[(参数名 参数类型)] is 查询结果集
       
       开发步骤:
           1. 声明游标
           2. 打开游标       open 游标名
           3. 从游标中取数据  fetch 游标名 into 变量
                         游标名%found :找到数据
                         游标名%notfound : 没有找到数据 
           4. 关闭游标       close 游标名
           
      系统引用游标
           1. 声明游标 : 游标名 sys_refcursor
           2. 打开游标: open 游标名 for 结果集
           3. 从游标中取数据
           4. 关闭游标
                
     for循环遍历游标:
           不需要声明额外变量
           不需要打开游标
           不需要关闭游标      
*/
--输出员工表中所有的员工姓名和工资(不带参数游标)
/*
   游标:所有员工
   声明一个变量,用来记录一行数据  %rowtype
*/
declare
   --游标
   cursor vrows is select * from emp;
   --声明变量,记录一行数据
   vrow emp%rowtype;      #vrow类型是emp中的rowtype类型
begin
   --1.打开游标  
   open vrows;
   --2.从游标提取数据
   --循环取数据
   loop
       fetch vrows into vrow; 
       exit when vrows%notfound;  
       dbms_output.put_line('姓名:'||vrow.ename ||' 工资: ' || vrow.sal);
   end loop;
   --3.关闭游标
   close vrows;
end;

--输出指定部门下的员工姓名和工资
/*
   游标: 指定部门的所有员工
   声明一个变量记录一行数据
*/
declare
   --声明游标
   cursor vrows(dno number) is select * from emp where deptno = dno;
   --声明变量
   vrow emp%rowtype;
begin
  --1.打开游标 , 指定10号部门
  open vrows(10);
  --2. 循环遍历,取数据
  loop
     fetch vrows into vrow;
     exit when vrows%notfound;    
      dbms_output.put_line('姓名:'||vrow.ename ||' 工资: ' || vrow.sal);
  end loop;
  close vrows;
end;

--系统引用游标
--输出员工表中所有的员工姓名和工资
declare
  --声明系统引用游标
  vrows sys_refcursor;
  --声明一个变量
  vrow emp%rowtype;
begin
  --1.打开游标
  open vrows for select * from emp;
  --2.取数据
  loop
    fetch vrows into vrow;    
    exit when vrows%notfound;
     dbms_output.put_line('姓名:'||vrow.ename ||' 工资: ' || vrow.sal);
  end loop;
  close vrows;
end;
 
--扩展内容----使用for循环遍历游标
declare
  --声明一个游标
  cursor vrows is select * from emp;
begin
  for vrow in vrows loop        #自己打开游标,自己赋值
     dbms_output.put_line('姓名:'||vrow.ename ||' 工资: ' || vrow.sal || '工作:'|| vrow.job);
  end loop;
end;

select * from emp;



--按照员工工作给所有员工涨工资,总裁涨1000,经理涨800,其他人涨400
/*
    游标 : 所有员工
    声明一个记录一行数据   
*/
declare
   --声明游标
   cursor vrows is select * from emp;
   --声明一个变量
   vrow emp%rowtype; 
begin
  --1.打开游标
  open vrows;
  --2.循环取数据
  loop
       --取数据
       fetch vrows into vrow;
       --退出条件
       exit when vrows%notfound;  
       --根据不同的职位,涨工资 总裁涨1000,经理涨800,其他人涨400
       if vrow.job = 'PRESIDENT' then
          update emp set sal = sal + 1000 where empno = vrow.empno;
       elsif vrow.job = 'MANAGER' then
          update emp set sal = sal + 800 where empno = vrow.empno;
       else
          update emp set sal = sal + 400 where empno = vrow.empno; 
       end if;       
  end loop;
  --3.关闭游标
  close vrows;
  --4.提交事务
  commit;
end;


select * from emp;

例外

/*
   例外:(意外)程序运行的过程发生异常,相当于是JAVA中的异常
   
   declare
       --声明变量
   begin
       --业务逻辑
   exception
       --处理异常
       when 异常1 then
         ...
       when 异常2 then
         ...
       when others then
         ...处理其它异常
   end;
   
   zero_divide : 除零异常
   value_error : 类型转换异常
   too_many_rows : 查询出多行记录,但是赋值给了rowtype记录一行数据变量
   no_data_found : 没有找到数据
       
   
   自定义异常:
       异常名  exception;
       raise 异常名          
*/
declare
   vi number;
   vrow emp%rowtype;
begin
   --vi := 8/0;  
   --vi := 'aaa';
   --select * into vrow from emp;
   select * into vrow from emp where empno=1234567;
exception
  when zero_divide then
    dbms_output.put_line('发生了除零异常');
  when value_error then
     dbms_output.put_line('发生了类型转换异常');
  when too_many_rows then
    dbms_output.put_line(' 查询出多行记录,但是赋值给了rowtype记录一行数据变量');
  when no_data_found then
    dbms_output.put_line('没有找到数据异常');
  when others then
     dbms_output.put_line('发生了其它异常' || sqlerrm);     
end;

--查询指定编号的员工,如果没有找到,则抛出自定义的异常
/*
     --错误的演示
     
     1.声明一个变量 %rowtype
     2.查询员工信息,保存起来
     3.判断员工信息是否为空
     4. 如果是 则抛出异常
*/
declare
  --   1.声明一个变量 %rowtype
  vrow emp%rowtype;
  --2 .声明一个自定义的异常
  no_emp exception;  
begin
  --查询员工信息,保存起来
  select * into vrow from emp where empno = 8888;   --抛出异常
  
  if vrow.sal is null then
    raise no_emp; --抛出自定义的异常
  end if;
exception
  when no_emp then
     dbms_output.put_line('输出了自定义的异常');  
  when others then
     dbms_output.put_line('输出了其它异常'||sqlerrm);  
end;

--查询指定编号的员工,如果没有找到,则抛出自定义的异常
/*
     游标来判断
       %found %notfound
    声明一个游标
    声明一个变量,记录数据
    从游标中取记录
       如果有,则不管它
       如果没有就抛出自定义的异常
*/
declare
  --声明游标
  cursor vrows is select * from emp where empno=8888;   
  --声明一个记录型变量
  vrow emp%rowtype;
  --声明一个自定义异常
  no_emp exception;  
begin
  --1.打开游标
  open vrows;
  --2.取数据
  fetch vrows into vrow;
  --3.判断游标是否有数据
  if vrows%notfound then
    raise no_emp;
  end if;
  close vrows;
exception
  when no_emp then
    dbms_output.put_line('发生了自定义的异常');
end;

存储过程

/*
    存储过程: 实际上是封装在服务器上一段PLSQL代码片断,已经编译好了的代码
              1.客户端取调用存储过程,执行效率就会非常高效
         语法:
              create [or replace] procedure 存储过程的名称(参数名 in|out 参数类型,参数名 in|out 参数类型)
              is | as
               --声明部分
              begin
               --业务逻辑 
              end; 
             
              
*/
--给指定员工涨薪,并打印涨薪前和涨薪后的工资
/*
    参数 : in 员工编号
    参数 : in 涨多少
    
    声明一个变量 : 存储涨工资前的工资
    
    查询出当前是多少
    打印涨薪前的工资
    更新工资
    打印涨薪后的工资          
*/
create or replace procedure proc_updatesal(vempno in number,vnum in number)
is
   --声明变量.记录当前工资
   vsal number;    
begin
  --查询当前的工资
  select sal into vsal from emp where empno = vempno;
  --输出涨薪前的工资
  dbms_output.put_line('涨薪前:'||vsal);
  --更新工资
  update emp set sal = vsal + vnum where empno = vempno;
  --输出涨薪后的工资
  dbms_output.put_line('涨薪后:'||(vsal+vnum));
  --提交
  commit;
end;

--方式1
call proc_updatesal(7788,10);

--方式2 用的最多的方式
declare

begin
  proc_updatesal(7788,-100);
end;

存储函数

/*
  存储函数: 实际上是一段封装是Oracle服务器中的一段PLSQL代码片断,它是已经编译好了的代码片段
        
        语法: 
             create [or replace] function 存储函数的名称(参数名 in|out 参数类型,参数名 in|out 参数类型) return 参数类型
             is | as
             
             begin
               
             end;
        存储过程和函数的区别:
             1.它们本质上没有区别
             2.函数存在的意义是给过程调用   存储过程里面调用存储函数
             3.函数可以在sql语句里面直接调用
             4.存储过程能实现的,存储函数也能实现,存储函数能实现的,过程也能实现
             
        默认是 in       
*/
--查询指定员工的年薪
/*
    参数 : 员工的编号
    返回 : 年薪          
*/
create or replace function func_getsal(vempno number) return number
is
  --声明变量.保存年薪
  vtotalsal number;     
begin
  select sal*12 + nvl(comm,0) into vtotalsal from emp where empno = vempno;
  return vtotalsal;
end;

--调用存储函数
declare
  vsal number;
begin
  vsal := func_getsal(7788);
  dbms_output.put_line(vsal);
end;


--查询员工的姓名,和他的年薪
select ename,func_getsal(empno) from emp;
--查询员工的姓名和部门的名称


--查询指定员工的年薪--存储过程来实现
--参数: 员工编号
--输出: 年薪
create or replace procedure proc_gettotalsal(vempno in number,vtotalsal out number)
is
       
begin
  select sal*12 + nvl(comm,0) into vtotalsal from emp where empno = vempno;
end;


declare
  vtotal number;
begin
  proc_gettotalsal(7788,vtotal);
  dbms_output.put_line('年薪:'||vtotal);
end;

select *  from emp where empno = 8888; 

JAVA调用存储过程

/*
    JAVA调用存储过程
       JDBC的开发步骤:
          1.导入驱动包
          2.注册驱动
          3.获取连接
          4.获取执行SQL的statement
          5.封装参数
          6.执行SQL
          7.获取结果
          8.释放资源   
*/

/*
   封装一个存储过程 : 输出所有表中的记录
   
   输出类型 : 游标  
*/
create or replace procedure proc_getemps(vrows out sys_refcursor)
is

begin
  --1.打开游标, 给游标赋值
  open vrows for select * from emp;
end;

java代码     java连接oracle的jar包

package com.itheima.test;

import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;

import org.junit.Test;

import oracle.jdbc.OracleCallableStatement;
import oracle.jdbc.driver.OracleTypes;

/*

  1.导入驱动包
  2.注册驱动
  3.获取连接
  4.获取执行SQL的statement
  5.封装参数
  6.执行SQL
  7.获取结果
  8.释放资源   
 */
public class TestProcedure {

    @Test
    /*
     create or replace procedure proc_gettotalsal(vempno in number,vtotalsal out number)
    is
           
    begin
      select sal*12 + nvl(comm,0) into vtotalsal from emp where empno = vempno;
    end;
     * */
    public void test1() throws Exception{
        //注册驱动
        Class.forName("oracle.jdbc.driver.OracleDriver");
        //2.获取连接
        String url = "jdbc:oracle:thin:@192.168.80.100:1521:orcl";
        String username = "dakang";
        String password = "dakang";
        Connection conn = DriverManager.getConnection(url, username, password);
        //3.获取执行SQL的statement.这是一个固定格式  proc_gettotalsal是一个存储函数
        String sql = "{call proc_gettotalsal(?,?)}";
        CallableStatement state = conn.prepareCall(sql);
        //设置输入参数
        state.setInt(1, 7788);//设置员工编号
        //注册输出参数类型
        state.registerOutParameter(2, OracleTypes.NUMBER);
        
        //4.执行statement
        state.execute();
        
        //5.获取执行结果
        int totalsal = state.getInt(2);
        
        //输出结果
        System.out.println("工资:"+ totalsal);
        
        //6.释放资源
        state.close();
        conn.close();
    }
    
    //调用存储函数
    /*
     create or replace function func_getsal(vempno number) return number
    is
      --声明变量.保存年薪
      vtotalsal number;     
    begin
      select sal*12 + nvl(comm,0) into vtotalsal from emp where empno = vempno;
      return vtotalsal;
    end; 
     */
    @Test
    public void test2() throws Exception{
        //注册驱动
        Class.forName("oracle.jdbc.driver.OracleDriver");
        //2.获取连接
        String url = "jdbc:oracle:thin:@192.168.80.100:1521:orcl";
        String username = "dakang";
        String password = "dakang";
        Connection conn = DriverManager.getConnection(url, username,password);
        //3.获取执行SQL的statement
        String sql = " {?= call func_getsal(?)}";
        CallableStatement state = conn.prepareCall(sql);
        //4.封装参数
        //注册返回类型参数
        state.registerOutParameter(1, OracleTypes.NUMBER);
        //设置第二个参数
        state.setInt(2, 7788);
        //5.执行SQL
        state.execute();        
        //6.获取结果
        int totalsal = state.getInt(1);
        System.out.println("年薪 :  ====" +totalsal);        
        //7.释放资源
        state.close();
        conn.close();
    }
    
    /*
     create or replace procedure proc_getemps(vrows out sys_refcursor)
        is
        
        begin
          --1.打开游标, 给游标赋值
          open vrows for select * from emp;
        end;
     * */
    @Test
    public void test3() throws Exception{
        //注册驱动
        Class.forName("oracle.jdbc.driver.OracleDriver");
        //2.获取连接
        String url = "jdbc:oracle:thin:@192.168.80.100:1521:orcl";
        String username = "dakang";
        String password = "dakang";
        Connection conn = DriverManager.getConnection(url, username,password);
        //3.获取执行SQL的statement
        String sql = "{call proc_getemps(?)}";
        CallableStatement call = conn.prepareCall(sql);
        //接口  --- > 对象 -->实现类的名称
        System.out.println(call.getClass().getName());
        OracleCallableStatement oracleCall = (OracleCallableStatement)call;
        //4.注册输出类型的参数
        call.registerOutParameter(1, OracleTypes.CURSOR);
        //5.执行SQL
        call.execute();
        //6.获取执行的结果
        ResultSet resultSet = oracleCall.getCursor(1);
        while(resultSet.next()){
            int empno = resultSet.getInt("empno");
            String name = resultSet.getString("ename");
            System.out.println(empno +" ==== "+name);
        }
        
        //7.释放资源
        resultSet.close();
        call.close();
        conn.close();
    }
}

触发器

/*
   触发器: 当用户执行了 insert | update | delete 这些操作之后, 可以触发一系列其它的动作/业务逻辑
       作用 : 
            在动作执行之前或者之后,触发业务处理逻辑
            插入数据,做一些校验
            
       语法:
           create [or replace] trigger 触发器的名称
           before | after
           insert | update | delete 
           on 表名
           [for each row]
           declare
           
           begin
             
           end;
           
       触发器的分类:
           语句级触发器:   不管影响多少行, 都只会执行一次
           
           行级触发器:     影响多少行,就触发多少次
                  :old  代表旧的记录, 更新前的记录
                  :new  代表的是新的记录
       
*/
--新员工入职之后,输出一句话: 欢迎加入黑马程序员
create or replace trigger tri_test1
after
insert
on emp
declare

begin
  dbms_output.put_line('欢迎加入黑马程序员');
end;

insert into emp(empno,ename) values(9527,'HUAAN');

--数据校验, 星期六老板不在, 不能办理新员工入职
--在插入数据之前
--判断当前日期是否是周六
--如果是周六,就不能插入
create or replace trigger tri_test2
before
insert 
on emp
declare
 --声明变量
 vday varchar2(10);
begin
  --查询当前
  select trim(to_char(sysdate,'day')) into vday from dual;
  --判断当前日期:
  if vday = 'saturday' then
     dbms_output.put_line('老板不在,不能办理入职');
     --抛出系统异常
     raise_application_error(-20001,'老板不在,不能办理入职');
  end if;
end;

insert into emp(empno,ename) values(9528,'HUAAN2');

--更新所有的工资 输出一句话
create or replace trigger tri_test3
after
update
on emp 
for each row
declare

begin
  dbms_output.put_line('更新了数据');
end;

update emp set sal = sal+10;



--判断员工涨工资后的工资一定要大于涨工资前的工资
/*
   200 --> 100
   触发器 : before
      旧的工资 
      新的工资
      如果旧的工资大于新的工资 , 抛出异常,不让它执行成功   
      
      
   触发器中不能提交事务,也不能回滚事务 
*/
create or replace trigger tri_updatesal
before
update
on emp
for each row
declare

begin
  if :old.sal > :new.sal then
    raise_application_error(-20002,'旧的工资不能大于新的工资');
  end if;
end;

update emp set sal = sal + 10;
select * from emp;

update emp set sal = sal - 100;


/*
   模拟mysql中ID的自增属性 auto_increment 
   insert into person(null,'张三');  
   
   触发器:
   
   pid=1  insert  pid=1
   
   序列 : create sequence seq_person_pid;       
*/
create table person(
    pid number primary key,
    pname varchar2(20)   
);

insert into person values(null,'张三'); 

create sequence seq_person_pid;

--触发器
create or replace trigger tri_add_person_pid
before
insert
on person
for each row
declare

begin
  dbms_output.put_line(:new.pname);
  --给新记录 pid 赋值
  select seq_person_pid.nextval into :new.pid from dual;
end;

insert into person values(null,'张三'); 


select * from person;

 

posted on 2018-11-05 18:42  断弯刀  阅读(208)  评论(0编辑  收藏  举报