Oracle学习笔记(四)

一 , 什么是PLSQL?

PL/SQL Developer是一个集成开发环境,专门开发面向Oracle数据库的应用。PL/SQL也是一种程序语言,叫做过程化SQL语言(Procedural Language/SQL)。PL/SQL是Oracle数据库对SQL语句的扩展。在普通SQL语句的使用上增加了编程语言的特点,所以PL/SQL把数据操作和查询语句组织在PL/SQL代码的过程性单元中,通过逻辑判断、循环等操作实现复杂的功能或者计算。PL/SQL 只有 Oracle 数据库有。

二、plsql的基本语法

1, 过程化语言的基本结构

  • declare
    • 声明变量
  • begin
    • 过程化语言
  • exception
    • 异常的处理
  • end;

2. 声明变量–declare

- i  number;
- 声明变量且赋初始值
  -  j number default 20;
- 引用类型:引用员工表姓名列的类型
  -  pname emp.ename%type;
  -  pjob emp.job%type;
- 记录类型: 记录一行的内容,一般对应 *
  - e_row emp%rowtype;

3. 过程化语言

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;

4. 分支语句(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;

5. 循环

1) 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;
2) 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;
3) while循环
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;

三、游标

1, 基本语法

声明游标
    --语法: cursor 游标名 is DQL;
遍历游标
      1.打开游标, open 游标名;
      2.从游标中提取一行的记录:fetch 游标名 into 变量名,...;
      3.使用循环, exit when 游标名%notfound;
      4.关闭游标, close 游标名;
------ 游标:(集合) ,处理返回多行记录的问题
declare
   pname varchar2(20);
begin
   -- select into : 只能操作返回一行记录
   select ename into pname from emp where deptno = 20;
   dbms_output.put_line(pname);
end;

2. 具体实例

--- 通过游标的方式打印20号部门的员工姓名
declare
    -- 声明游标
    cursor cur is select ename,job from emp where deptno = 20;
    pname emp.ename%type;
    pjob emp.job%type;
begin
    -- 遍历游标
    --1.打开游标, open 游标名;
    open cur;
    loop
    --2.从游标中提取一行的记录:fetch 游标名 into 变量名,...;
    fetch cur into pname,pjob;
    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;

四. 例外(异常)

1. 常见例外

--- 例外(异常) :(了解)
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;

2. 自定义例外

--- 游标中没有记录的异常
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;

五、存储过程

1. 什么是存储过程

  • 存储过程(dba声明): 封装了一组sql语句,提前编译好,效率较高 ,存储在服务端
  • 场景:网购:数据库发生什么改变
    • 库存量-1(update) ,订单增加(insert),钱(update),物流(insert) , 日志(insert)

2. 语法

 create [or replace] procedure 存储过程名称(参数名  in|out 类型,....)
 as | is 
    -- 声明变量
 begin
   -- 过程化语言
 end;  

3. 实例

--- 举例:给某员工涨工资(打印涨前的工资,和涨后的工资)
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
  getEmpsByDeptno(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;

六、存储函数

1. 基本语法

---- 存储函数:封装了一组sql语句,提前编译好,效率较高 ,存储在服务端
     --- 存储函数必须有一个返回值,存储函数可以用select语句中
   create or replace function 函数名(参数名  in|out 类型,....)
   return type
   as | is

   begin
      return 值;
   end;

2. 实例

--- 举例: 查询某员工的年薪
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 com.lyric.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.189.128: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();
        }
    }
}

八、触发器

1. 什么是触发器

触发器(监听器):监听表中的数据是否发生了改变

2. 基本语法

create or replace trigger 触发器名
after | before 在改变之前还是之后执行触发器
insert | delete | update  监听表的哪个操作
on 表  对哪张表的监听
触发器的级别:表级触发器, 行级触发器
      表级触发器不能使用old,new
      行级触发器:可以使用old,new
declare

begin

end;

3. 实例

--- 添加一条记录,打印添加了一条记录
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/lr19941226/article/details/80991917