一、控制结构

  控制结构包括:判断语句(条件分支语句)、循环语句、顺序控制语句三种。

  1、条件分支语句

  • if--then:简单条件判断

    --编写一个过程,可以输入一个雇员名,如果该雇员名的工资低于2000,就给该雇员工资增加10%
    create or replace procedure pro_addSal(v_ename varchar2) is
    --定义变量
    v_sal emp.sal%type;
    begin
    select sal into v_sal from emp where ename=v_ename;
    --判断
    if v_sal<2000 then
    update emp set sal=sal+sal*0.1 where ename=v_ename;
    end if;
    end;
    /
  • if--then--else:二重条件分支
    --编写一个过程,可以输入一个雇员名,如果该雇员名的工资低于2000,就给该雇员工资增加10%,否则减少10%
    create or replace procedure pro_addSal(v_ename varchar2) is
    --定义变量
    v_sal emp.sal%type;
    begin
    select sal into v_sal from emp where ename=v_ename;
    --判断
    if v_sal<2000 then
    update emp set sal=sal+sal*0.1 where ename=v_ename;
    else
    update emp set sal=sal-sal*0.1 where ename=v_ename;
    end if;
    end;
    /
  • if--then--elsif--else:多重条件分支
    create or replace procedure pro_addSal(eNo number) is
    v_job emp.job%type;
    begin
    select job into v_job from emp where empno=eNo;
    if v_job='PRESIDENT' then
    update emp set sal=sal+1000 where empno=eNo;
    elsif v_job='MANAGER' then
    update emp set sal=sal+500 where empno=eNo;
    else
    update emp set sal=sal+200 where empno=eNo;
    end if;
    end;
    /

  2、循环语句

  • loop循环:pl/sql中最简单的循环语句,以loop开头,以exit()作为结束判断语句,以end loop结尾。(至少循环一次)

    --编写一个过程,输入用户名,并循环添加10个用户到users表中
    create table users(userId number(5),userName varchar(20));--为了后面操作先创建一个users表
    create or replace procedure pro_addUser(eName varchar2) is
    --定义变量
    v_num number:=1;
    begin
    loop
    insert into users values(v_num,eName);
    exit when v_num=10;--判断退出条件
    v_num:=v_num+1;--自增
    end loop;
    end;
    /
  • while循环:其实就是使用while语句来代替loop循环的exit语句。
    --编写一个过程,删除users表中的编号1—10的个用户
    --用户编号从1开始增加。
    create or replace procedure pro_delUser is
    --定义变量
    v_num number:=1;
    begin
    while v_num<=10 loop
    delete from users where userId=v_num;
    v_num:=v_num+1;--自增
    end loop;
    end;
    /
  • for循环:自带变量和循环退出条件
    create or replace procedure pro_addUser is
    begin
    for i in 1..10 loop
    insert into users values(i,'lucy');
    end loop;
    end;
    /

  3、顺序控制语句

  • goto语句:用于跳转到特定标号去执行语句。注:由于使用gogo语句会增加程序的复杂性,并使得应用程序可读性变差,因此建议不要使用goto语句。
    语法:goto lable,其中lable是已经定义好的标号名,如<<标记名>>,<<>>是标记符号,常用来跳出循环。

    --循环输出i=1..10,最后跳出循环后打印“循环结束”
    declare
    i int:=1;
    begin
    loop
    dbms_output.put_line('i='||i);
    if i=10 then
    goto end_loop;
    end if;
    i:=i+1;
    end loop;
    <<end_loop>>
    dbms_output.put_line('循环结束');
    end;
    /
  • null语句:null语句不会执行任何操作,并且会直接将控制传递到下一条语句。(类似Java中的continue的用法)
    declare
    v_sal emp.sal%type;
    v_ename emp.ename%type;
    begin
    select ename,sal into v_ename,v_sal from emp where empno=&no;
    if v_sal<3000 then
    update emp set comm=sal*0.1 where ename=v_ename;
    else
    null;
    end if;
    end;
    /

二、使用Java程序调用存储过程

  1、无返回值的存储过程

  创建一个表book,表结构如下:

   

  • create table book(bId number(4) primary key,bName varchar(30) not null,publisher varchar(30));

  编写一个过程,向book表添加书籍信息,要求可以通过java程序调用该过程:

  • 使用命令行创建:

    create or replace procedure pro_addBook(bookId number,bookName varchar2,pub varchar2) is
    begin
    insert into book values(bookId,bookName,pub);
    end;
    /
  • 使用Java调用无返回值的过程:
     package test;
    import java.sql.CallableStatement;
    import java.sql.Connection;
    import java.sql.DriverManager;
    import java.sql.SQLException; import org.junit.Test; /**
    * 使用java调用Oracle创建的过程pro_addBook
    */
    public class callPro_addBook {
    @Test
    public void test(){
    Connection conn = null;
    CallableStatement cs = null;
    try{
    //连接数据库
    Class.forName("oracle.jdbc.driver.OracleDriver");
    conn = DriverManager.getConnection(
    "jdbc:oracle:thin:@192.168.183.1:1521:orcl","scott","tiger");
    //获得执行对象
    cs = conn.prepareCall("{call pro_addBook(?,?,?)}");
    //传参
    cs.setInt(1, 1001);
    cs.setString(2, "五年模拟三年高考");
    cs.setString(3, "教育出版社");
    //执行
    cs.execute();
    }catch(Exception e){
    e.printStackTrace();
    }finally{
    try {
    cs.close();
    conn.close();
    } catch (SQLException e) {
    e.printStackTrace();
    }
    }
    }
    }

    callPro_addBook

  2、有返回值的存储过程(返回若干值)

  编写一个过程,要求输入book表的书号就返回书籍信息:书名和出版社

  • 使用命令行创建过程:

    create or replace procedure pro_showBook
    (bookId in number,bookName out varchar2,pub out varchar2) is
    begin
    select bName,publisher into bookName,pub from book where bId=bookId;
    end;
    /
  • 使用Java调用返回值是若干数据的过程
     package test;
    import java.sql.CallableStatement;
    import java.sql.Connection;
    import java.sql.DriverManager;
    import java.sql.SQLException; import org.junit.Test; /**
    * 使用java调用Oracle创建的过程pro_addBook
    */
    public class callPro_showBook {
    @Test
    public void test(){
    Connection conn = null;
    CallableStatement cs = null;
    try{
    //连接数据库
    Class.forName("oracle.jdbc.driver.OracleDriver");
    conn = DriverManager.getConnection(
    "jdbc:oracle:thin:@192.168.183.1:1521:orcl","scott","tiger");
    //获得执行对象
    cs = conn.prepareCall("{call pro_showBook(?,?,?)}");
    //传入参数
    cs.setInt(1, 1001);
    cs.registerOutParameter(2, oracle.jdbc.OracleTypes.VARCHAR );
    cs.registerOutParameter(3, oracle.jdbc.OracleTypes.VARCHAR );
    //执行
    cs.execute();
    //获取out参数
    String bookName = cs.getString(2);
    String pub = cs.getString(3);
    System.out.println("书名:"+bookName);
    System.out.println("出版社:"+pub);
    }catch(Exception e){
    e.printStackTrace();
    }finally{
    try {
    cs.close();
    conn.close();
    } catch (SQLException e) {
    e.printStackTrace();
    }
    }
    }
    }

    callPro_showBook

  3、有返回值的存储过程(返回一个列表)

  为了方便说明,我们再往book表中添加几条数据:

  

  现在的需求是:创建一个过程,要求返回指定出版社如“知乎周刊”出版的书籍信息。

    如表所示,返回结果是三本书,而这种查询结果集我们一般放在一个list即列表中,而在oracle在接受返回值时需要使用包package,并用游标来进行参数输出:

  • --建立包,在该包中,定义一个游标类型test_cursor
    create or replace package testpackage as
    type test_cursor is ref cursor;
    end;
    /
  • 使用命令行创建过程:
    create or replace procedure pro_showPubBook
    (pub in varchar2,my_cursor out testpackage.test_cursor) is
    begin
    open my_cursor for select * from book where publisher=pub;
    end;
    /
  • 使用Java调用返回值是列表的过程:
     package test;
    import java.sql.CallableStatement;
    import java.sql.Connection;
    import java.sql.DriverManager;
    import java.sql.ResultSet;
    import java.sql.SQLException; import org.junit.Test; /**
    * 使用java调用Oracle创建的过程pro_addBook
    */
    public class callPro_showPubBook {
    @Test
    public void test(){
    Connection conn = null;
    CallableStatement cs = null;
    try{
    //连接数据库
    Class.forName("oracle.jdbc.driver.OracleDriver");
    conn = DriverManager.getConnection(
    "jdbc:oracle:thin:@192.168.183.1:1521:orcl","scott","tiger");
    //获得执行对象
    cs = conn.prepareCall("{call pro_showPubBook(?,?)}");
    //传入参数
    cs.setString(1, "知乎周刊");
    cs.registerOutParameter(2,oracle.jdbc.OracleTypes.CURSOR );//游标类型
    //执行
    cs.execute();
    //获得结果集
    ResultSet rs = (ResultSet) cs.getObject(2);
    System.out.println("知乎周刊出版社书籍:");
    if(rs!=null)
    while(rs.next()){
    System.out.println("书号:"+rs.getInt(1)+" "+"书名:《"+rs.getString(2)+"》");
    }
    else
    System.out.println("暂无书籍");
    }catch(Exception e){
    e.printStackTrace();
    }finally{
    try {
    cs.close();
    conn.close();
    } catch (SQLException e) {
    e.printStackTrace();
    }
    }
    }
    }

    callPro_showPubBook

三、分页编程

  案例:编写一个存储过程,要求可以输入表名、每页显示记录数、当前页,返回总记录数、总页数和返回的结果集。

  1、使用rownum分页查询

  • select * from emp;
    select t1.*,rownum rn from (select * from emp) t1;
    select t1.*,rownum rn from (select * from emp) t1 where rownum<=10;
    select * from (select t1.*,rownum rn from (select * from emp) t1 where rownum<=10) where rownum>=6;

  2、编写分页的存储过程

  • --编写分页的存储过程
    create or replace procedure fenye
    (tableName in varchar2,--in表名
    myPageSize in number,--in记录数
    pageNow in number,--in当前页
    myRows out number,--out总记录数
    myPageCount out number,--out总页数
    p_cursor out testpackage.test_cursor--out结果集
    )is v_sql varchar2(500);--定义sql语句
    v_begin number:=(pageNow-1)*myPageSize+1;--定义起始页
    v_end number:=pageNow*myPageSize;--定义尾页 begin
    --执行分页查询语句
    v_sql:='select * from (select t1.*,rownum rn from (select * from '||tableName||
     ') t1 where rownum<='||v_end||') where rn>='||v_begin;
    --把游标和sql语句关联
    open p_cursor for v_sql;
    --计算myRows
    v_sql:='select count(*) from '||tableName;
    execute immediate v_sql into myRows;
    --计算myPageCount
    if mod(myRows,myPageSize)=0 then
    myPageCount:=myRows/myPageSize;
    else
    myPageCount:=myRows/myPageSize+1;
    end if;end;
    /

  3、使用Java调用分页过程

  •  import java.sql.CallableStatement;
    import java.sql.Connection;
    import java.sql.DriverManager;
    import java.sql.ResultSet; public class Test {
    public static void main(String[] args) {
    // TODO Auto-generated method stub
    Connection ct = null;
    CallableStatement cs = null;
    try {
    Class. forName("oracle.jdbc.driver.OracleDriver");
    ct = DriverManager.getConnection("jdbc:oracle:thin:@127.0.0.1:1521:orcl", "scott", "***" ); cs = ct.prepareCall( "{call fenye(?,?,?,?,?,?)}"); // 赋值
    cs.setString(1, "emp");
    cs.setInt(2, 5);
    cs.setInt(3, 1); // 注册总记录数
    cs.registerOutParameter(4, oracle.jdbc.OracleTypes.INTEGER );
    // 注册总页数
    cs.registerOutParameter(5, oracle.jdbc.OracleTypes.INTEGER );
    // 注册返回的结果集
    cs.registerOutParameter(6, oracle.jdbc.OracleTypes.CURSOR ); cs.execute();
    // 取出总记录数
    int rowNum = cs.getInt(4);
    // 取出总页数
    int pageCount = cs.getInt(5);
    ResultSet rs = (ResultSet) cs.getObject(6); // 显示
    System. out.println( "rowNum=" + rowNum);
    System. out.println( "pageCount=" + pageCount); while ( rs.next()) {
    System. out.println( "编号:" + rs .getInt(1) + ",姓名:" + rs .getString(2));
    }
    } catch (Exception e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
    } finally {
    try {
    // 关闭资源
    cs.close();
    ct.close();
    } catch (Exception e1) {
    // TODO Auto-generated catch block
    e1.printStackTrace();
    }
    }
    }
    }

  

四、例外处理

  1、分类

  • 预定义例外:用于处理常见的oracle错误。
  • 非预定义例外:用于处理与预定义例外不能处理的例外。
  • 自定义例外:用于处理与oracle错误无关的其他情况。

  2、一个简单的例外处理

  编写一个过程,可接收雇员的编号,并显示该雇员的姓名。如果输入的雇员编号不存在,如何处理?

  • --例外
    declare
    v_ename emp.ename%type;
    begin
    select ename into v_ename from emp where empno=&no;
    dbms_output.put_line('名字:'||v_ename);
    exception when no_data_found then
    dbms_output.put_line('编号不存在,请重新输入!');
    end;
    /

  3、预定义例外

  由pl/sql所提供的系统例外。当pl/sql应用程序违反了oracle规定的限制时,则会隐含触发一个内部例外。pl/sql为开发人员提供了20多个预定义例外。

  •  case_not_found:when子句中没有包含必须的条件分支,就会触发case_not_found的例外。

    --case_not_found
    create or replace procedure sp_pro11(spno number) is
    v_sal emp.sal%type;
    begin
    select sal into v_sal from emp where empno=spno;
    case
    when v_sal<1000 then
    update emp set sal=sal+100 where empno=spno;
    when v_sal<2000 then
    update emp set sal=sal+200 where empno=spno;
    end case;
    exception
    when case_not_found then
    dbms_output.put_line('case语句没有与'||v_sal||'相匹配的条件');
    end;
    /
  • cursor_already_open当重新打开已经打开的游标时,会隐含触发例外cursor_already_open。
    --cursor_already_open
    declare
    cursor emp_cursor is select ename,sal from emp;
    begin
    open emp_cursor;
    for emp_reacord1 in emp_cursor loop
    dbms_output.put_line(emp_record1.ename);
    end loop;
    exception
    when cursor_already_open then
    dbms_output.put_line('游标已经打开');
    end;
    /
  • dup_val_on_index:在唯一索引所对应的列上插入重复的值时,会隐含触发例外dup_val_on_index。
  • invalid_cursor:当试图在不合法的有表上执行操作时,会触发该例外。例如:试图从没有打开的游标提取数据,或是关闭没有打开的游标,则会触发该例外。
  • invalid_number当输入的数据有误时,会触发该例外。数字100写成了1oo就会触发该例外。
  • no_data_found当执行select into没有返回值时,就会触发该例外。
    --no_data_found
    declare
    v_sal emp.sal%type;
    begin
    select sal into v_sal from emp where ename= '&name';
    exception
    when no_data_found then
    dbms_output.put_line( '不存在该员工' );
    end;
  • too_many_rows执行select into语句时,如果返回超过了一行,则会触发该例外。
    --too_many_rows
    declare
    v_ename emp.ename%type;
    begin
    select ename into v_ename from emp;
    exception when too_many_rows then
    dbms_output.put_line('返回了多行');
    end;
    /
  • zero_divide当执行除法运算时,如果分母为0,则会触发该例外。
  • value_error在执行赋值操作时,如果变量的长度不足以容纳实际数据,则会触发该例外value_error。
  • login_denide:用户非法登录。
  • not_logged_on:用于未登录就执行dml操作。
  • storage_error:超出了内存空间或是内存被损坏。
  • timeout_on_resource:oracle在等待资源时,出现超时。

  4、非预定义例外

  非预定义例外:用于处理与与定义例外无关的oracle错误。预定义例外只可以处理21个oracle错误,而当使用pl/sql开发应用程序时,可能会遇到其它的一些oracle错误。比如在pl/sql块中执行dml语句时,违反了约束规定等。在这样的情况下,也可以处理oracle的各种例外。

  5、自定义例外

  自定义例外与oracle错误没有任何关联,是由开发人员为特定情况所定义的例外。编写一个pl/sql块,接收一个雇员的编号,并给该雇员工资增加1000元,如果该雇员不存在,请提示。

--自定义例外
create or replace procedure ex_test(spNo number) is
--定义一个例外
myex exception;
begin
update emp set sal=sal+100 where empno=spNo;
if sql%notfound then
raise myex;--触发例外myex
end if;
exception
when myex then
dbms_output.put_line('没有更新任何例外');
end;
/
--说明:sql%notfound返回的数据类型是一个布尔值。布尔值与前一条sql语句相关。当最近的一条sql语句没有操作任何行的时候,返回true。否则返回false。

五、Oracle视图View

  1、概念

  视图是一个虚拟表,其内容由查询定义。同真实的表一样,视图包含一系列带有名称的列和行数据。视图并不在数据库中以存储的数据值集形式存在。航和列数据来自由定义视图的查询所引用的表,并且在引用视图时动态生成。

  2、视图与表的区别

  • 表需要占用磁盘空间,视图不占用。
  • 视图不能添加索引。
  • 使用视图可以简化复杂查询:比如学生选课系统。
  • 视图有利于提高安全性:比如不同用户查看不同视图。

  3、创建视图

  create or replace view 视图名 as select语句 [with read only]
  • --创建视图,把emp表的sal<1000的雇员映射到该视图
    create view myview as select * from emp where sal<1000;

  4、删除视图

  drop view 视图名;

  5、简单地使用视图

  比如说有表图书book(id,name,prise....)读者reader(id.....)借阅关系 borrow( bookid,readerid,date)。

  如果要查询读者借阅情况,我们需要多表查询比较麻烦,但是我们可以建立个视图,view1:

  • select * from book,reader,borrow where book.id=bookid and reader.id=readerid

    这样只要查询select * from view1 就可以看到谁借了什么书了,包括所有的详细内容。

Oracle实战笔记(第七天)之PL/SQL进阶的更多相关文章

  1. Oracle实战笔记(第六天)之PL/SQL基础

    一.PL/SQL介绍 1.概念 PL/SQL也是一种程序语言,叫做过程化SQL语言(Procedural Language/SQL).PL/SQL是Oracle数据库对SQL语句的扩展.在普通SQL语 ...

  2. oracle学习笔记(十六) PL/SQL 异常和goto语句

    PL/SQL 异常和goto语句 异常 预定义异常 oracle常见预定义异常: 错误号 异常错误信息名称 说明 ORA-0001 DUP_VAL_ON_INDEX 试图破坏一个唯一性限制 ORA-0 ...

  3. oracle学习笔记(十五) PL/SQL语法结构以及使用

    PL/SQL 简介 PL/SQL 是过程语言(Procedural Language)与结构化查询语言(SQL)结合而成的编程语言. PL/SQL 是对 SQL 的扩展. 支持多种数据类型,如大对象和 ...

  4. oracle学习笔记(十八) PL/SQL 游标

    游标 说明 查询结果的光标,相当于java中的一个迭代器,方便遍历操作 可使用的属性 %FOUND SQL语句查询或影响了一行或多行时为 TRUE.如:mycursor%FOUND %NOTFOUND ...

  5. pl/sql进阶--例外处理

    在pl/sql的执行过程中发生异常时系统所作的处理称为一个例外情况(exception).通常例外情况的种类有三种: 1.预定义的oracle例外情况oracle预定义的例外情况大约有24个,对于这种 ...

  6. pl/sql进阶一控制结构

    在任何计算机语言(c,java,c#,c++)都有各种控制语句(条件语句,循环结构,顺序控制结构…),在pl/sql中也存在这样的控制结构. 在本部分学校完毕后,希望大家达到: 1)使用各种if语句 ...

  7. Oracle实战笔记(第一天)

    导读 笔记内容来自韩顺平老师的视频<玩转Oracle实战教程>,可以结合笔记进行观看.第一天视频中还有Oracle的介绍和安装等内容,很容易搜索到,这里就不再进行总结. 目录 1.命令行工 ...

  8. Oracle 学习笔记 (七)

    一.数据库的启动 启动数据库的三个阶段: nomount, mount,open mount 阶段:. 1.读参数文件 2.分配内存 3.启动后台进程 4.初始化部分v$视图 mount 阶段: 读参 ...

  9. oracle 11g 64w 用32位的pl/sql

    1.  下载64位Oracle,解压两文件,解压完成后将文件合并,安装: 2.  下载PL/SQL,安装: 3.  下载instantclient-basic-win32-11.2.0.1.0.zip ...

随机推荐

  1. [Spark性能调优] 第三章 : Spark 2.1.0 中 Sort-Based Shuffle 产生的内幕

    本課主題 Sorted-Based Shuffle 的诞生和介绍 Shuffle 中六大令人费解的问题 Sorted-Based Shuffle 的排序和源码鉴赏 Shuffle 在运行时的内存管理 ...

  2. 记一次电信反射xss的挖掘与利用

    0X0.前言 早上起床,打开手机习惯性刷刷新闻,却发现网络无法连接,本以为是光猫出现了问题,后来发现是忘记续费,欠费了. 在网上充值完之后,等了有将近十分钟,网依旧没恢复.随打了个电话给客服,客服在后 ...

  3. swift中的AnyHashable

    AnyHashable是调和objc和Swift的产物 典型的场景是,objc下无泛型的NSDictionary到了Swift下,会变成什么?(典型例子是苹果来的推送消息) [Any: Any],这肯 ...

  4. php的二维数组排序

    //建立一个$arr的二维数组 $arr=array( array('name'=>'e','age'=>5), array('name'=>'b','age'=>2), ar ...

  5. .net4.5中HttpClient使用注意点

    .net4.5中的HttpClinet是个非常强大的类,但是在最近实际项目运用中发现了些很有意思的事情. 起初我是这样用的: using (var client = new HttpClient()) ...

  6. Canvas-图片旋转

    Canvas-图片旋转 众所周知canvas是位图,你可以在里面渲染你要的东西,不过你只能操作canvas的属性来进行编辑.就是说你并不能操作画进canvas的东西,例如我在canvas里添加一幅画, ...

  7. JSP和Servlet笔记

    一.JSP的3个编译指令   作用:page指令用于设置整个jsp页面相关的属性,比如页面的编码格式.所包含的文件等等,它们包含在<%@ page %>标记中.  1)page 指令  以 ...

  8. Python_day1

    一.HelloWorld >>>print("Hello World!") >>>Hello World! 二.变量    1.什么是变量 : ...

  9. DOM拓展

    DOM拓展 1.选择符API 所谓选择符API即是根据css选择符选择与某个模式相匹配的DOM元素,jQuery的核心就是通过css选择符查询DOM文档取得元素的引用,从而抛弃了原有繁琐的getELe ...

  10. jQuery_serialize的用法

    jQuery_serialize(form表单序列化)用于在前端要传很多值往后端的时候: <!DOCTYPE html> <html lang="en"> ...