oracle练习-day04

---------------------------Oracle day04----------------------------------
--01.什么是PL/SQL?
通俗理解:plsql:Procedure language sql 过程化语言
plsql是一组sql语句集合,在这个plsql中集合语句中可以处理复杂业务逻辑
(申明变量、条件分支、循环语句、异常处理)

优点:如果使用plsql语言,编写存储过程 函数,提供java代码调用,减少访问数据频率
缺点:对程序员要求比较高(高级程序员 或 专业DBA),存储过程 函数 移植不方便
--02.PL/SQL基本语法
declare
       --声明变量 (普通变量、常量、引用型变量、记录型变量)
begin
       --DML语句(逻辑语句)
end;

--03.普通变量和常量使用
--类似于java中 private String myname ="老王";
declare
       myname varchar2(30) :='老王';
begin
       myname := '隔壁老王';
       
       select e.ename into myname from emp e where e.empno = '7369';
       --打印变量
       dbms_output.put_line(myname);
end;


--类似于java中private final String myname ="老王";
declare
       myname constant varchar2(30) :='老王';
begin
       --myname := '隔壁老王';
       --打印变量
       dbms_output.put_line(myname);
end;

--04.引用型变量 --emp.ename%type(推荐使用这种方式)
declare
       myname emp.ename%type :='老王';
begin
       select e.ename into myname from emp e where e.empno = '7369';
       --打印变量
       dbms_output.put_line(myname);
end;


--05.记录型变量
declare
       v_row emp%rowtype;
begin
       select * into v_row from emp e where e.empno = '7369';
       --打印变量
       dbms_output.put_line(v_row.empno||'=='||v_row.ename);
end;


--06.条件分支
语法:
if 条件 then
  --语句
end if;

if 条件 then
  --语句
else
  --语句
end if;

if 条件 then
  --语句
elsif 条件 then
  --语句
else
  --语句
end if;

--07.根据输入的年龄判断小于18输出未成年人,18-60成年人,60以上老年人
declare
    v_age number(10):=&age;
begin
    if v_age < 18 then
       dbms_output.put_line('未成年人');
    elsif v_age < 60 then
       dbms_output.put_line('成年人');
    else
       dbms_output.put_line('老年人');
    end if;
end;
--08.loop循环
语法:
for 变量名 in 起始值..结束值---(不用指定条件退出循环)
  loop
    --语句
  end loop;
  
loop --需要执行条件退出循环
  --通过条件退出循环
  exit when 条件;
end loop;


while 条件 --需要设置条件
 loop
    --语句
 end loop;

--09.输出1100的数字
declare
begin
  for i in 1..100
    loop
      dbms_output.put_line(i);
    end loop;
end;

declare
    v_number number:= 1;
begin
    loop 
      exit when v_number > 100;
      dbms_output.put_line(v_number);
      v_number := v_number +1;
    end loop;
end;

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

--10.游标 cursor
什么是游标?
定义游标用于接收多条数据

语法:
定义游标:cursor 游标名称[(参数名 数据类型)]  is select 查询语句;
使用游标:
open 游标名称[(参数名 数据类型)];
     loop
       --从游标中取出一条数据放入记录型变量
       fetch 游标名称 into 记录型变量
       --语句
       --游标没有数据则退出
       exit when 游标名称%notfound;
     end loop;
close 游标名称;


--11.通过游标输出emp表中所有员工的信息
declare
      --定义一个游标
      cursor c_emp is select * from emp;
      v_row emp%rowtype;
begin
      open c_emp;--打开游标
      loop       --循环
        fetch c_emp into v_row;--取出游标一条数据放入记录型变量中
        exit when c_emp%notfound;--退出游标循环
        dbms_output.put_line(v_row.empno || '=='|| v_row.ename);--打印
      end loop;
      close c_emp;--关闭
end;

--12.通过游标输出指定部门的员工信息

declare
      --定义一个游标
      cursor c_emp(v_deptno emp.deptno%type) is select * from emp where deptno = v_deptno;
      v_row emp%rowtype;
begin
      open c_emp(&deptno);--打开游标
      loop       --循环
        fetch c_emp into v_row;--取出游标一条数据放入记录型变量中
        exit when c_emp%notfound;--退出游标循环
        dbms_output.put_line(v_row.empno || '=='|| v_row.ename);--打印
        
      end loop;
      close c_emp;--关闭
end;


--13.异常
异常用来增强程序健壮性和容错性
oracle中异常分为两类:
自带异常(预定义异常)
自定义异常

--除以0的异常plsql程序  自带异常(预定义异常)
declare
      v_number number;
begin
      v_number :=10/0
      exception when ZERO_DIVIDE then 
        dbms_output.put_line('除数不能为0');
end;

--一个变量容量不够时异常plsql程序  自带异常(预定义异常)
declare
      v_number number(1);
begin
      v_number :=20
      exception when VALUE_ERROR then
        dbms_output.put_line('容量不够异常');
end;

--根据输入年龄判断,如果年龄大于150,抛异常
--自定义异常
语法:
定义异常 变量名称 exception;

declare
     myexception exception;--定义异常变量
     v_age number(10):=&age;--定义年龄变量
begin
     if v_age > 150 then  --自定义的业务异常
        raise myexception;--抛异常
     end if;
     exception when myexception then --捕获异常
        dbms_output.put_line('活不到150');
end;
--------------------------存储过程------------------
--14.存储过程
存储过程是在大型数据中,一组为了完成特定sql语句集。
存储过程名称 可以有输入参数 输出结果
存储过程可以编写非常复杂业务逻辑,实际可以替换java代码中业务逻辑

语法:
create [or replace] procedure 存储过程名(参数名 [in] 数据类型,参数名 out 数据类型)
is|as
  --定义变量(普通变量 常量  记录型变量 引用型变量)
begin
  --DML语句 (业务逻辑sql语句)
end;

--使用存储过程,输出指定员工的年薪
create or replace procedure pro_emp(v_empno in emp.empno%type)
is
  --定义一个变量接收员工年薪 打印到output窗口
  v_sal emp.sal%type;
begin
  select sal*12+nvl(comm,0) into v_sal from emp where empno = v_empno;
  dbms_output.put_line(v_sal);
end;

--通过plsql工具Test进行测试
--通过plsql过程语言测试存储过程
declare
begin
  pro_emp(7369);
end;

--指定员工的年薪,用out参数返回年薪(工作中)
create or replace procedure pro_emp(v_empno emp.empno%type,out_sal out emp.sal%type)
is
begin
  select sal*12+nvl(comm,0) into out_sal from emp where empno = v_empno;
end;

--测试
declare
  --定一个变量来接收out返回结果
  out_sal emp.sal%type;
begin
  pro_emp(7369,out_sal);
  dbms_output.put_line(out_sal);
end;

--15.函数(自定义函数)
存储过程和函数区别:
1.存储过程没有return 返回值  函数一定return返回值
2.存储过程 函数都有输入和输出参数 但函数中输出参数一般不用,使用return返回结果
3.函数可以有参数 也可以没有参数
4.存储过程可以有参数 也没有参数
5.存储过程可以没有out返回值,但是那这么定义就没有任何意义。
6.函数都是被存储过程调用

oracle多行函数 单行函数 to_char('xxx')


---语法
create [or replace] function 函数名称(参数名 数据类型,参数名 out 数据类型)
return 返回值的数据类型
is|as
begin
end;

--计算某个员工年薪并返回
create or replace function fun_emp(v_empno emp.empno%type)
return number
is
  --接收年薪变量
  v_sal number;
begin
  select sal*12+nvl(comm,0) into v_sal from emp where empno = v_empno;
  return v_sal;
end;
--测试
declare
  v_sal number;
begin
  v_sal := fun_emp(7369);
  dbms_output.put_line(v_sal);
end;

--一般函数都是提供给存储过程调用
create or replace procedure pro_emp(v_empno emp.empno%type,out_sal out emp.sal%type)
is
begin
  --select sal*12+nvl(comm,0) into out_sal from emp where empno = v_empno;
  
  select fun_emp(v_empno) into out_sal from dual;
end;


--重要
--统一开发环境 eclipse 4.5.2  jdk1.7
--新建一个空的工作空间   修改字体大小 以及  jsp编码
--16.通过java代码测试jdbc连接

/**
 * 根据员工编号查询员工信息 (测试连接oracle数据库)
 */
public static void findEmpByEmpno(Long empno) {
  Connection conn = null;
  PreparedStatement prepareStatement = null;
  ResultSet rs = null;
  // 获取连接
  try {
    conn = BaseDao.getConn();
    // 预处理对象
    prepareStatement = conn.prepareStatement("select * from emp where empno = ?");
    prepareStatement.setLong(1, empno);
    rs = prepareStatement.executeQuery();
    while (rs.next()) {
      System.out.println(rs.getObject(1) + "==" + rs.getObject(2));
    }
  } catch (SQLException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
  } finally {
    BaseDao.closeAll(rs, prepareStatement, conn);
  }
}
--17.jdbc调用存储过程 函数
/**
 * pro_emp(v_empno emp.empno%type,out_sal out emp.sal%type)
 * jdbc调用存储过程
 * 需求:根据员工编号 得到年薪(存储过程)
 */
public static void proYearSalByEmpno(Long empno) {
  Connection conn = null;
  ResultSet rs = null;
  CallableStatement prepareCall =null;//处理存储过程和函数对象
  /**
   * 
   *  {?= call <procedure-name>[(<arg1>,<arg2>, ...)]} //函数 有返回值 也有输出结果(一般不用输出结果)
     *  {call <procedure-name>[(<arg1>,<arg2>, ...)]}    //存储过程没有返回值 但有输出结果
   * 
   */
  // 获取连接
  try {
    conn = BaseDao.getConn();
    // 预处理对象
    prepareCall = conn.prepareCall("{call pro_emp(?,?)}");
    prepareCall.setLong(1, empno);//设置一个参数 员工编号
    prepareCall.registerOutParameter(2, OracleTypes.NUMBER);//设置第二个参数返回的数据类型
    prepareCall.execute();//执行
    System.out.println(prepareCall.getLong(2));
  } catch (SQLException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
  } finally {
    BaseDao.closeAll(rs, prepareCall, conn);
  }
}
  
/**
 * function fun_emp(v_empno emp.empno%type)
 * jdbc调用函数 
 * 需求:根据员工编号得到年薪(函数)
 * 
 */
public static void funYearSalByEmpno(Long empno) {
  Connection conn = null;
  ResultSet rs = null;
  CallableStatement prepareCall =null;//处理存储过程和函数对象
  // 获取连接
  try {
    conn = BaseDao.getConn();
    // 预处理对象
    prepareCall = conn.prepareCall("{?= call fun_emp(?)}");
    prepareCall.registerOutParameter(1, OracleTypes.NUMBER);//设置返回值的数据类型
    prepareCall.setLong(2, empno);
    prepareCall.execute();//执行
    System.out.println(prepareCall.getLong(1));
  } catch (SQLException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
  } finally {
    BaseDao.closeAll(rs, prepareCall, conn);
  }
}
--18.out游标接收指定部门员工的所有信息
--sys_refcursor返回多行数数据(第一步)
create or replace procedure pro_emp(v_deptno in emp.deptno%type,out_cursor out sys_refcursor)
is
begin
  open out_cursor for select * from emp where deptno = v_deptno;
  
  --select fun_emp(v_empno) into out_sal from dual;
end;
--第二步:
/**
 * procedure pro_emp(v_deptno in emp.deptno%type,out_cursor out sys_refcursor)
 * jdbc调用存储过程
 * 需求:根据部门编号获取部门下所有员工信息
 */
public static void proEmpByDetpno(Long deptno) {
  Connection conn = null;
  ResultSet rs = null;
  CallableStatement prepareCall =null;//处理存储过程和函数对象
  // 获取连接
  try {
    conn = BaseDao.getConn();
    // 预处理对象
    prepareCall = conn.prepareCall("{call pro_emp(?,?)}");
    prepareCall.setLong(1, deptno);//设置一个参数部门编号
    prepareCall.registerOutParameter(2, OracleTypes.CURSOR);//设置第二个参数返回的数据类型
    prepareCall.execute();//执行
    //OracleCallableStatement 通这个接口调用获取游标方法
    OracleCallableStatement oc = (OracleCallableStatement)prepareCall;
    rs = oc.getCursor(2);
    while (rs.next()) {
      System.out.println(rs.getObject(1) + "==" + rs.getObject(2)+ "==" + rs.getObject(3));
    }
  } catch (SQLException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
  } finally {
    BaseDao.closeAll(rs, prepareCall, conn);
  }
}



--19.触发器
触发器是跟表有关联关系,当改变表(insert update delete)的数据的时候,
oracle会自动执行触发器中编写的语句。
--触发没有参数 没有返回值 等
emp
语法:
create or replace trigger 触发器名称
before|after--指定触发器在改变这个表之前还是之后触发
insert | update | delete--在什么触发触发器
on 表名 
for each row ---重点看如何去用
declare
   --声明变量
begin
   --语句
end;

--向emp表中插入数据时,输出一句话(新员工入职了)
create or replace trigger tri_emp
after
insert
on emp
declare
begin
  dbms_output.put_line('新员工入职了');
end;
--测试
insert into emp(empno,ename,sal) values(123,'隔壁老王',50000);
commit;
select * from emp;


--假如'2018/08/16'系统维护,不能修改emp表的数据
create or replace trigger tri_emp
before
update of sal --如果没有加of sal 是针对当前表中所有列 如果加上就是针对当前列
on emp
declare
   --定一个时间变量接收系统时间
   v_date varchar2(30);
begin
   select to_char(sysdate,'yyyy/mm/dd') into v_date from dual;
   --oracle中条件分支判断 =   相当于java中==
   if v_date = '2018/08/16' then
     dbms_output.put_line('系统维护,不能修改emp表的数据');
   end if;
end;

select * from emp;
update emp set sal = 10000 where empno = 123;

--触发器的真实使用(备份员工的工资) 
select * from emp;
--将涨薪前后的薪水保存到表中
--第一步建表
create table sal_log  --薪资日志表
(
       empno NUMBER(4), --员工编号
       ENAME VARCHAR2(10),--姓名
       b_sal NUMBER(7,2),--涨薪前薪水
       a_sal NUMBER(7,2),--涨薪后薪水
       mydate date --薪资时间
);
--第二步:创建触发器
create or replace trigger tri_emp
before
update of sal
on emp
for each row--行级触发器 将for each row放开
--:new :old
declare
begin
    insert into sal_log values(:old.empno,:old.ename,:old.sal,:new.sal,sysdate);
end

--测试
select * from sal_log;
select * from emp where empno =123;
update emp set sal = 5000 where empno =123;


--模拟msql主键自动增长
--第一步:创建表
create table mytable
(
       myid number(10) primary key ,
       myname varchar2(30)
);
--需求:
insert into mytable(myname) values('xxx');

--第二步:创建序列
create sequence seq_mytable;
--第三步:新建触发器
create or replace trigger tri_emp
before 
insert
on mytable 
for each row
declare
begin
  select seq_mytable.nextval  into :new.myid from dual;
end;  
--测试
insert into mytable(myname) values('yyyy');
select * from mytable;

猜你喜欢

转载自www.cnblogs.com/coder-wf/p/12199062.html