oracle入门(二)

### 一,视图


```
1. 什么是视图
    视图是一张虚表, 可以把视图看成表使用(增删改查),视图中没有数据,所有的数据都在基本表中(tables)
    封装了一个复杂的DQL
    操作视图就是操作table
    视图一般不作为修改数据使用, 只用作查询
2. 语法
    create [or replace] view 视图名 as DQL;
3. 栗子
    create or replace view emp_view as select * from emp;

    select * from emp_view;
    select * from emp;

    insert into emp_view(empno ,ename) values(1000, 'rose');
    -- 作用一 : 可以屏蔽敏感列
    create or replace view employee
    as select empno ,ename ,job ,mgr, hiredate ,deptno from emp;  
    select * from emp_view;
    -- 作用二:可以定义只读的视图
    create or replace view employee
    as select empno ,ename ,job ,mgr, hiredate ,deptno from emp with read only;  
    -- 错误:virtual column not allowed here
    insert into employee(empno ,ename) values(1001, 'rose');
    
    
```

### 二、索引

```
1. 索引的目的
    提高检索效率
    前提:百万条记录以上, 不经常的修改列 , 经常查询的列
2. 语法: create index 索引  on 表(列1,列2)
3. 测试效率
    准备表,数据
    create table a(
       b number primary key ,
       c varchar2(200)
    )

    create sequence a_seq;

    select sys_guid() from dual;

    declare

    begin
           for i in 1..1000000 loop
               insert into a values(a_seq.nextval ,sys_guid() );
           end loop;
    end;
    添加索引前查询
    select * from a where c = '1407AB1D7E26496797E63E2C7DA76DB9'; -- 0.546
    添加索引
    create index a_index on a(c);
    添加索引后查询
    select * from a where c = '9C4CD7D2251348E4B02756BEE7558C46'; -- 0.063
4. 复合索引的触发条件(了解)   address  age
   where address = '' and age = ''   触发索引
   where address = '' or age = ''    不能触发索引
   where address = ''     触发索引
   where age = ''    不能触发索引   
```

### 三、plsql的基本语句(过程化语言)

```
1. plsql  P: procedure 过程化
2. 过程化语言的基本结构
    declare
        -- 声明语句
    begin
        --- 过程化语言
    [exception]
    end;
3. 声明变量
    declare
        -- 声明普通变量
        i  number;
        -- 声明变量且赋值
        j number default 200;
        -- 引用类型:引用某表.某列的类型
        pname emp.ename%type;
        pjob emp.job%type;
        -- 记录类型:引用表的一行的列
        emp_row emp%rowtype;
    begin
        -- 赋值语句
        i := 100;
        -- 打印
        dbms_output.put_line(i);
        --  || 连接符
        dbms_output.put_line('j = ' || j);
        --赋值语句: select 查询的列 into 变量;
        select ename, job into pname,pjob from emp where empno = 7788;
        dbms_output.put_line(pname || '::' ||pjob);

        select * into emp_row from emp where empno = 7788;
        dbms_output.put_line(emp_row.ename || '::' ||emp_row.job || emp_row.hiredate);
    end;
4 . if语句
    格式
    if 条件 then
         SQL语句;
    end if;
    if 条件 then
         SQL语句;
    else
        sql语句;
    end if;
    if 条件 then
         SQL语句;
    elsif 条件 then
        sql语句;
    ....
    else
        sql语句;
    end if;
-- 输入一个整数,如果大于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 集合|游标 loop
         
         end loop;
     */-- 打印1-10所有的整数
declare

begin
  for i in 1..10 loop
    dbms_output.put_line(i);
  end loop;
end;
2) 语法
    /*    
        loop
            循环体;
            退出条件; exit when 条件;
        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 条件 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. 什么是游标(主要使用在存储过程中)
    就是一个集合
    主要作用处理返回多行记录的问题
2. 声明游标
    cursor 游标名 is DQL;
3. 遍历游标
    1) 打开游标
        open 游标名
    2) 提取单行的记录
        fetch 游标名 into 变量中;
    3) 使用循环
        结束语句: exit when 游标名%notfound;
    4) 关闭游标
        close 游标名;
4. 栗子
    -- 使用游标在控制台打印20号部门的员工信息
    declare
       cursor cur is select * from emp where deptno = 20;
       erow emp%rowtype;
    begin
        open cur;
         loop
          fetch cur into erow;
          exit when cur%notfound;  -- 判断是否提取到了记录
          dbms_output.put_line(erow.empno || erow.ename ||erow.job);   
        end loop;
        close cur;
    end;
    select * from emp where deptno = 20;
-- 给20号部门的所有员工涨工资
declare
   cursor cur is select empno from emp where deptno = 20;
begin
   -- for 特点:自动打开和关闭游标
   -- a 是一个记录变量, 存放游标一行的记录
   for a in  cur loop
     update emp set sal = sal +1 where empno = a.empno;
   end loop;
   commit;
end;
```

### 五、存储过程(重点)

```
1. 什么是存储过程
    封装了一组sql语句, 提前编译,存储在服务端,
    一般是由dba定义
2. 存储过程的使用场景
    网上买了一件商品:商品表(update),订单表(insert), 余额表(update),销量表(update),物流表(insert)
                   日志表(insert)
3. 语法
    create or replace procedure 过程名(参数1 in | out 数据类型, 参数2  in | out 数据类型,...)
    as|is
        -- 声明语句
    begin
        -- plsql
    end;
4 .栗子  -- 只有输入参数
给某员工涨工资, 在控制台上打印涨前的工资和涨后的工资
create or replace procedure updateSal(eno in number, psal  in number)
is
        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;
      commit;
      select sal into newsal from emp where empno = eno;
      dbms_output.put_line('涨后的工资:'||newsal );
end;

-- 访问存储过程
call updateSal(7788,5);
5. 栗子   有输入有输出参数的存储过程
-- 获取某员工的年薪
create or replace procedure getYearSalByEno(eno in number, yearsal  out number)
as

begin
    select sal * 12 + nvl(comm , 0) into yearsal from emp where empno = eno;

end;

-- 访问存储过程
declare
   yearsal number;
begin
  getYearSalByEno(7902, yearsal);
  dbms_output.put_line('年薪:' || yearsal);
end;
6. 栗子: 输出参数为游标类型
-- 获取某部门的所有员工信息
-- cursor :静态的游标
-- sys_refcursor:动态的游标,动态的指定sql语句
create or replace procedure getEmpsByDeptno(dno in number ,emps out sys_refcursor)
as

begin
       -- 动态游标赋值sql语句
    open emps for
         select * from emp where deptno = dno;
end;
-- 访问带有输出参数为游标的存储过程
declare
   emps sys_refcursor;
   erow emp%rowtype;
begin
   getEmpsByDeptno(20 ,emps);
   -- 遍历游标
   loop
      fetch emps into erow;
      exit when emps%notfound;
      dbms_output.put_line(erow.empno ||erow.ename);
   end loop;
   close emps;
end;
```

### 六、存储函数(重点)

```
1. 什么是存储过程
    封装了一组sql语句, 提前编译,存储在服务端
    一般是由dba定义
    一般在存储函数中没有输出参数
2. 与存储过程的区别
    存储函数必须有一个返回值
    存储函数可以使用select语句访问
3. 语法
    create or replace function 函数名(参数1 in | out 数据类型, 参数2  in | out 数据类型,...)
    retrun 类型
    as|is
    
    begin
        return 值;
    end;
4. 栗子: 获取某员工的年薪
create or replace function getYearSalByEnoFun(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 := getYearSalByEnoFun(7902);
  dbms_output.put_line(yearsal);
end;
5. 可以用在select语句中    
select getYearSalByEnoFun(7902) from dual;
select length('abc') from dual;    
    
```

### 七,触发器

```
1. 什么是触发器
    监听器: 监听表的数据是否发生改变(insert ,update ,delete)
2. 语法
    create or replace trigger 触发器名称
    before | after
    insert |update |delete
    on 表
    触发器的级别
        表级触发器:默认的
        行级触发器:更新一条记录,执行一次,
            如果使用old,new ,必须使用行级触发器
            for each row
    declare
    
    begin
    
    end;
3. 栗子1
    添加一条记录,在控制台打印 ’添加了一条记录‘
create or replace trigger insertEmp
after
insert
on emp

declare
    
begin
    dbms_output.put_line('添加了一条记录');
end;


insert into emp(empno ,ename) values(1002 ,'mike');
4. 栗子2:不能给员工降薪
create or replace trigger notUpdateEmpLowerSal
before
update
on emp
for each row
declare

begin
  if :old.sal > :new.sal  then
       -- 提示:不能降薪
    --dbms_output.put_line('不能降薪');
    -- raise_application_error(p1,p2)
       -- p1 : 错误的编号(-20001 ~~~ -20999)
       -- p2: 错误的提示信息
       raise_application_error(-20001, '不能降薪');
  end if;
end;

update emp set sal = sal + 1 where empno = 7788;
    
```

![1535171397687](assets/1535171397687.png)    

### 八,JDBC调用存储过程和存储函数

```
package com.jjy;


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

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.***.***:1521:orcl";
    String username = "scott";
    String password = "tiger";
    Connection conn;
    PreparedStatement pst;
    CallableStatement cst;
    ResultSet rs;
    /**
     * 在执行test方法之前执行before
     */
    /**
     * 初始化信息
     * @throws Exception
     */
    @Before
    public void init() throws Exception{
        Class.forName(driver);
        conn = DriverManager.getConnection(url, username, password);
    }
    
    /**
     * 访问存储过程
     * create or replace procedure getYearSalByEno(eno in number, yearsal  out number)
     * {call <procedure-name>[(<arg1>,<arg2>, ...)]}
     */
    @Test
    public void testProcedure()throws Exception{
        //声明sql语句
        String sql = "{call getYearSalByEno(?,?)}";
        //创建statement对象
        cst = conn.prepareCall(sql);
        //设置占位符
        cst.setInt(1, 7902);
        //设置输出参数的类型
        cst.registerOutParameter(2, OracleTypes.NUMBER);
        //执行sql语句
        cst.executeUpdate();
        //获取结果
        int yearsal = cst.getInt(2);
        System.out.println("年薪:" + yearsal);
    }
    /**
     * 访问存储过程
     * create or replace procedure getEmpsByDeptno(dno in number ,emps out sys_refcursor)
     * {call <procedure-name>[(<arg1>,<arg2>, ...)]}
     */
    @Test
    public void testProcedureOutCursor() throws Exception{
        //声明sql语句
        String sql = "{call getEmpsByDeptno(?,?)}";
        //创建statement对象
        cst = conn.prepareCall(sql);
        //设置占位符
        cst.setInt(1, 20);
        //设置输出参数的类型
        cst.registerOutParameter(2, OracleTypes.CURSOR);
        //执行sql语句
        cst.executeUpdate();
//        //获取结果
        //OracleCallableStatement ocst = (OracleCallableStatement) cst;
        
//        rs = (ResultSet) cst.getObject(2);
//        while(rs.next()){
//            System.out.println(rs.getInt("empno")   + rs.getString("ename"));
//        }
    }
    
    /**
     * 访问存储函数
     * {?= call <procedure-name>[(<arg1>,<arg2>, ...)]}
     * create or replace function getYearSalByEnoFun(eno in number) return number
     */
    @Test
    public void testFunction()throws Exception{
        //声明sql语句
        String sql = "{?= call getYearSalByEnoFun(?)}";
        //创建statement对象
        cst = conn.prepareCall(sql);
        //设置占位符
        cst.registerOutParameter(1, OracleTypes.NUMBER);
        cst.setInt(2, 7902);
        //执行sql语句
        cst.executeUpdate();
        //获取结果
        int yearsal = cst.getInt(1);
        System.out.println("年薪:" + yearsal);
    }
    
    /**
     * 查询员工中的记录
     * @throws Exception
     */
    @Test
    public void test()throws Exception {
        String sql = "select * from emp";
        pst = conn.prepareStatement(sql);
        rs = pst.executeQuery();
        while(rs.next()){
            System.out.println(rs.getInt("empno")   + rs.getString("ename"));
        }
    }
    /**
     * 关闭资源
     */
    @After
    public void close()throws Exception{
        if(rs != null){
            rs.close();
        }
        if(pst != null){
            pst.close();
        }
        if(cst != null){
            cst.close();
        }
        if(conn != null){
            conn.close();
        }
    }

}

```

原文地址:https://www.cnblogs.com/itworkerlittlewrite/p/9537728.html