Oracle从入门到精通第四天

Oracle第四天

一、回顾

	DDL语言

		表空间,用户(权限管理:角色权限(基本权限,开发人员权限,dba)),表(数据类型,修改表,约束)

		视图 (view) , 序列(sequence),索引(index),同义词(synonym)

		DML语句,事务概念,数据库的备份:导出(exp),导入(imp)

二、学习目标

1. plsql的基本语法
2. 游标&例外
3. 存储过程
4. 存储函数
5. JDBC访问Oracle数据库
6. 触发器

三、plsql的基本语法

    /**
      过程化语言的基本结构
        declare
          -- 声明变量
        begin
          -- 过程化语言
        exception
          --异常的处理
        end;
    
    */
    -- 声明变量
       declare
         -- 声明普通的变量
         i  number;
         -- 声明变量且赋初始值
         j number default 20;
         -- 引用类型:引用员工表姓名列的类型
         pname emp.ename%type;
         pjob emp.job%type;
         -- 记录类型: 记录一行的内容,一般对应 *
         e_row emp%rowtype;
       begin
         -- 对变量进行赋值
         i := 100;
         -- 在控制台打印i的值
         dbms_output.put_line(i);
         dbms_output.put_line('j =' || j);
         -- 把员工的姓名查询出来赋值给变量
         select ename,job into pname,pjob from emp where empno = 7654; --
         dbms_output.put_line('7788的姓名是:' || pname||',工作是:'|| pjob);
         select * into e_row from emp where empno = 7654; --
         dbms_output.put_line('7788的姓名是:' || e_row.ename||',工作是:'||e_row.job);
       end;
    
    ---- 分支语句 (if)
    /*
    if 条件 then
       sql语句;
    end if;
    */
    -- 输入一个整数,如果大于0打印正数,否则打印非正数
    declare
       i number;
    begin
       i := &请输入;
       if i > 0 then
         dbms_output.put_line('正数');
       end if;
       if i <= 0 then
         dbms_output.put_line('非正数');
       end if;
    end;
    /*
    if 条件 then
       sql语句;
    else 
       sql语句
    end if;
    */
    -- 输入一个整数,如果大于0打印正数,否则打印非正数
    declare
       i number;
    begin
       i := &请输入;
       if i > 0 then
         dbms_output.put_line('正数');
       else
         dbms_output.put_line('非正数');
       end if;
    end;
    
    /*
    if 条件 then
       sql语句;
    elsif 条件  then
       sql语句
    elsif 条件  then
       sql语句
       ....
    else 
       sql语句
    end if;
    */
    -- 输入一个整数,如果大于0打印正数,如果是0,打印零,否则打印负数
    declare
       i number;
    begin
       i := &请输入;
       if i > 0 then
         dbms_output.put_line('正数');
       elsif i = 0 then
         dbms_output.put_line('零');    
       else
         dbms_output.put_line('负数');
       end if;
    end;
    ---- 循环
    -- for循环
    /*
    for 变量 in 游标|1..10 loop
      
    end loop;
    */
    -- 打印1-10 十个整数
    declare
    
    begin
      for i in 1..10 loop
        dbms_output.put_line(i);  
      end loop;
    end;
    /**
      loop循环
      
      loop
          循环体;
          退出条件;
          迭代部分
      end loop;
    */
    -- 打印1-10 十个整数
    declare
      i number default 1;
    begin
      loop
        dbms_output.put_line(i);
        exit when i = 10;
        i := i + 1;
      end loop;
    end;
    /*
    while 条件 loop
          循环体
          迭代
    end loop;
    */
    -- 打印1-10 十个整数
    declare
      i number default 1;
    begin
      while i <= 10 loop 
        dbms_output.put_line(i);
        i := i + 1;
      end loop;
    end;

四、游标&例外

    ------ 游标:(集合) ,处理返回多行记录的问题
    declare
       pname varchar2(20);
    begin
       -- select into : 只能操作返回一行记录
       select ename into pname from emp where deptno = 20;
       dbms_output.put_line(pname);
    end;
    
    
    --  声明游标
        --语法: cursor 游标名 is DQL;
    --  遍历游标
        /*
              1.打开游标, open 游标名;
              2.从游标中提取一行的记录:fetch 游标名 into 变量名,...;
              3.使用循环, exit when 游标名%notfound;
              4.关闭游标, close 游标名;
        */
    --- 通过游标的方式打印20号部门的员工姓名
    declare
        -- 声明游标
        cursor cur is select ename,job from emp where deptno = 20;
        pname emp.ename%type;
    begin
        -- 遍历游标
        --1.打开游标, open 游标名;
        open cur;
        loop
        --2.从游标中提取一行的记录:fetch 游标名 into 变量名,...;
        fetch cur into pname;
        exit when cur%notfound; -- 来判断是否提取到了记录
        dbms_output.put_line(pname);
        --3.使用循环, exit when 游标名%notfound;
        end loop;
        --4.关闭游标, close 游标名;
        close cur;
    end;
    
    --- 举例:(使用游标)给20号部门员工涨工资
    declare
        cursor cur is select empno from emp where deptno = 20;
    begin
        -- for 循环:自动打开和关闭游标
        for i in cur loop
          update emp set sal = sal + 1 where empno = i.empno;
        end loop;
        commit;
    end;
    --- 例外(异常) :(了解)
    declare
       pname varchar2(20);
       i number ;
    begin
       i := 10 / 0;
       -- select into : 只能操作返回一行记录
       select ename into pname from emp where deptno = 20;
       dbms_output.put_line(pname);
    exception
       when no_data_found then
           dbms_output.put_line('没有返回记录');
       when too_many_rows then
           dbms_output.put_line('返回太多的记录');
       when value_error then
           dbms_output.put_line('赋值类型错误');
       when zero_divide then
           dbms_output.put_line('除0异常');
       when others then
           dbms_output.put_line('其他异常');
    end;
    
    ---- 自定义异常(了解)
    --- 游标中没有记录的异常
    declare
       cursor cur is select ename from emp where deptno = 40;
       pname emp.ename%type;
       -- 声明异常
       not_found exception;
    begin
       open cur;
       fetch cur into pname;
       if cur%notfound then
         -- 抛出异常
          raise not_found;
       end if;
    exception
       when not_found then  -- 捕获异常
         dbms_output.put_line('没有记录');
    end;
    

五、存储过程

    ----- 存储过程(dba声明,得授予dba权限): 封装了一组sql语句,提前编译好,效率较高 ,存储在服务端
      -- 场景:网购:数据库发生什么改变
        -- 库存量-1(update) ,订单增加(insert),钱(update),物流(insert) , 日志(insert)
    --- 语法
     /*
     create [or replace] procedure 存储过程名称(参数名  in|out 类型,....)
     as | is 
        -- 声明变量
     begin
       -- 过程化语言
     end;  
        
     */
    --- 举例:给某员工涨工资(打印涨前的工资,和涨后的工资)
    create or replace procedure updateSal(eno in number , psal in number)
    as
        oldsal emp.sal%type;
        newsal emp.sal%type;
    begin
        -- 涨前的工资
        select sal into oldsal from emp where empno = eno;
        dbms_output.put_line('原工资:' || oldsal);
        -- 涨工资
        update emp set sal = sal + psal where empno = eno;
        -- 涨后的工资
        select sal into newsal from emp where empno = eno;
        dbms_output.put_line('现工资:' || newsal);
    end;
    
    --- 访问存储过程
     -- call  存储过程名(参数);  -- 只能访问只有输入参数的存储过程
    call updateSal(7788, 5);
    
    ---  举例: 查询某员工的年薪
    
    create or replace procedure getYearSal(eno in number , yearsal out number)
    is 
    
    begin
        select sal * 12 + nvl(comm,0) into yearsal from emp where empno = eno;   
    end;
    -- 访问存储过程
    declare
      yearsal number;
    begin
      getYearSal(7788 , yearsal);
      dbms_output.put_line('年薪:'||yearsal);
    end;
    
    --- 举例(返回游标): 获取某部门的所有员工信息
    create or replace procedure getEmpsByDeptno(dno in number ,emps out sys_refcursor)
    as
    
    begin
        open emps for select * from emp where deptno = dno;   
    end;
    
    --- 访问带有游标的存储过程
    declare
      emps sys_refcursor;--定义一个游标类型的变量
      e_row emp%rowtype;
     begin
       getemployees(20,emps);
       --变量游标
       loop
         fetch emps into e_row;
         exit when emps%notfound;--退出循环的条件
         dbms_output.put_line(e_row.empno || e_row.ename);
        end loop;
     end; 
    

六、存储函数

    ---- 存储函数:封装了一组sql语句,提前编译好,效率较高 ,存储在服务端
         --- 存储函数必须有一个返回值,存储函数可以用select语句中
       /**
       create or replace function 函数名(参数名  in|out 类型,....)
       return type
       as | is
       
       begin
          return 值;
       end;
       
       */
    --- 举例: 查询某员工的年薪
    create or replace function getYearSalFun(eno in number)
    return number    
    as
          yearsal number;   
    begin
         select sal * 12 + nvl(comm,0) into yearsal from emp where empno = eno;
         return yearsal;  
    end;
        
    --- 访问函数
    declare
      yearsal number;
    begin
      yearsal := getYearSalFun(7788);
      dbms_output.put_line(yearsal);
    end; 
         
    -- 在select中访问存储函数
    select getYearSalFun(7788) from dual;

七、JDBC访问Oracle数据库

    package cn.itcast.test;
    
    
    import java.sql.CallableStatement;
    import java.sql.Connection;
    import java.sql.DriverManager;
    import java.sql.PreparedStatement;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    
    import org.junit.After;
    import org.junit.Before;
    import org.junit.Test;
    
    import oracle.jdbc.driver.OracleCallableStatement;
    import oracle.jdbc.driver.OracleTypes;
    
    public class JDBCTest {
    	
    	String driver = "oracle.jdbc.driver.OracleDriver";
    	String url = "jdbc:oracle:thin:@192.168.174.129:1521:orcl";
    	String username = "scott";
    	String password = "tiger";
    	Connection conn ;
    	PreparedStatement pst;
    	CallableStatement cst;
    	ResultSet rs;
    	/**
    	 * 初始化配置信息
    	 */
    	@Before
    	public void init(){
    		try {
    			Class.forName(driver);
    			conn = DriverManager.getConnection(url,username, password);
    		} catch (Exception e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    		}
    	}
    	
    	/**
    	 * 存储过程的sql语句语法: {call <procedure-name>[(<arg1>,<arg2>, ...)]}
    	 * create or replace procedure getYearSal(eno in number , yearsal out number)
    	 * @throws SQLException 
    	 * 
    	 */
    	@Test
    	public void testProcedure() throws SQLException{
    		String sql  = "{call getYearSal(?,?)}";
    		//创建CallableStatement对象
    		cst = conn.prepareCall(sql);
    		//设置输入参数
    		cst.setInt(1, 7788);
    		//注册输出类型
    		cst.registerOutParameter(2, OracleTypes.NUMBER);
    		//执行sql语句
    		cst.execute();
    		// 得到输出参数的值
    		int yearsal = cst.getInt(2);
    		System.out.println(yearsal);
    	}
    	/**
    	 * 返回游标的存储过程
    	 * create or replace procedure getEmpsByDeptno(dno in number ,emps out sys_refcursor)
    	 * 存储过程的sql语句语法: {call <procedure-name>[(<arg1>,<arg2>, ...)]}
    	 * @throws SQLException 
    	 */
    	@Test
    	public void testProcedureOutCursor() throws SQLException{
    		String sql = "{call getEmpsByDeptno(?,?)}";
    		cst = conn.prepareCall(sql);
    		cst.setInt(1, 20);
    		cst.registerOutParameter(2, OracleTypes.CURSOR);
    		cst.execute();
    //		OracleCallableStatement ocst = (OracleCallableStatement) cst;
    //		rs = ocst.getCursor(2);
    		Object o = cst.getObject(2);
    		rs = (ResultSet) o;
    		while(rs.next()){
    			System.out.println("编号:"+rs.getInt("empno") + ",姓名:"+ rs.getString("ename"));
    		}
    	}
    	/**
    	 * {?= call <procedure-name>[(<arg1>,<arg2>, ...)]}
    	 * 
    	 * create or replace function getYearSalFun(eno in number) return number
    	 * @throws SQLException 
    	 */
    	@Test
    	public void testFunction() throws SQLException{
    		String sql = "{?= call getYearSalFun(?)}";
    		cst = conn.prepareCall(sql);
    		cst.registerOutParameter(1, OracleTypes.NUMBER);
    		cst.setInt(2, 7788);
    		cst.execute();
    		int yearsal = cst.getInt(1);
    		System.out.println(yearsal);
    	}
    	/**
    	 * select getYearSalFun(7788) from dual
    	 * 
    	 * 传统的方式访问存储函数
    	 * @throws SQLException
    	 */
    	@Test
    	public void testFunction2() throws SQLException {
    		String sql = "select getYearSalFun(?) yearsal from dual";
    		pst = conn.prepareStatement(sql);
    		pst.setInt(1, 7788);
    		rs = pst.executeQuery();
    		while(rs.next()){
    			System.out.println(rs.getInt("yearsal"));
    		}
    	}
    	@Test
    	public void test() throws SQLException {
    		String sql = "select * from emp";
    		pst = conn.prepareStatement(sql);
    		rs = pst.executeQuery();
    		while(rs.next()){
    			System.out.println("编号:"+rs.getInt("empno") + ",姓名:"+ rs.getString("ename"));
    		}
    	}
    	
    	/**
    	 * 关闭资源
    	 * @throws SQLException 
    	 */
    	@After
    	public void close() throws SQLException{
    		if(rs != null){
    			rs.close();
    		}
    		if(pst != null){
    			pst.close();
    		}
    		if(cst != null){
    			cst.close();
    		}
    		if(conn != null){
    			conn.close();
    		}
    		
    	}
    
    }
    

八、触发器

    ----- 触发器(监听器):监听表中的数据是否发生了改变
          -- 增删改 操作
    /*
    create or replace trigger 触发器名
    after | before 在改变之前还是之后执行触发器
    insert | delete | update  监听表的哪个操作
    on 表  对哪张表的监听
    触发器的级别:表级触发器, 行级触发器
          表级触发器不能使用old,new
          行级触发器:可以使用old,new
    declare
    
    begin
      
    end;
    
    */
    --- 栗子: 添加一条记录,打印添加了一条记录
    create or replace trigger insertEmp
    after
    insert
    on emp
    declare
    
    begin
      dbms_output.put_line('添加了一条记录');
    end;
    
    insert into emp (empno ,ename) values(1000,'mike');
    
    --- 栗子:不能给员工降薪
    create or replace trigger notUpdateLowerSal
    before
    update
    on emp
    for each row  -- 行级触发器
    declare
    
    begin
       if :new.sal < :old.sal then
          -- raise_application_error(p1,p2)
             -- p1:错误的编号: -20000 --   -20999
             -- p2 : 错误的提示信息
          raise_application_error(-20000, '不能给员工降薪!!!');
       end if;
    end;
    
    update emp set sal = sal + 1 where empno = 7788;
    
    
    
    --- 触发器小应用 -- 模拟mysql中自增效果
    create sequence b_seq;
    create or replace trigger auto_incrementTri
    before
    insert
    on emp
    for each row
    declare
    
    begin
       select b_seq.nextval into :new.empno from dual;
    end;
    
    insert into emp(ename) values('zhangsan');







猜你喜欢

转载自blog.csdn.net/luo609630199/article/details/81007962