pl/sql的介绍

为什么需要pl/sql编程?

因为使用纯的sql语句来操作数据库,有先天性的技术缺陷:

1、不能模块编程;

2、执行速度慢;

3、安全性有问题;

4、浪费带宽。

 

pl/sql是什么?

pl/sql(procedural language/sql)是oracle在标准的sql语言上的扩展。pl/sql不仅允许嵌入sql语言,还可以定义变量和常量,允许使用条件语句和循环语句,允许使用例外处理各种错误,这样使得它的功能变得更加强大。

 

学习必要性

1、提高应用程序的运行性能;

2、模块化的设计思想[分页的过程,订单的过程,转账的过程...];

3、减少网络传输量;

4、提高安全性。

缺点:移植性不好。

 

sqlplus开发工具

sqlplus是oracle公司提供的一个工具。

举一个简单案例:

编写一个存储过程,该过程可以向某表中添加记录。

创建add_emp存储过程:

create procedure add_emp is

begin

insert into emp (empno,ename) values(4444,'4444');

end;

/

执行:

exec add_emp;

 

pl/sql developer开发工具

pl/sql developer是用于开发pl/sql块的集成开发环境(IDE),他是一个独立的产品,而不是oracle的一个附带品。

举一个简单案例:

编写一个存储过程,该过程可以删除某表记录。

创建删除del_emp过程

create procedure del_emp(in_empno number) is

begin

delete from emp where empno=in_empno;

end;

/

执行过程:

exec del_emp(4444);

 

创建过程基本语法:

create procedure 过程名(参数1,...)

is

begin

    执行语句;

end;

/

 

执行过程语法:

exec 过程名(传入参数,...)

 

pl/sql基础知识--介绍

开发人员使用pl/sql编写应用模块时,不仅需要掌握sql语句的编写方法,还要掌握pl/sql语句及语法规则。pl/sql编程可以使用变量和逻辑控制语句,从而可以编写非常有用的功能模块。

比如:分页存储过程模块、订单管理存储过程模块、转账存储过程模块。而且如果使用pl/sql编程,我们可以轻松的完成非常复杂的查询要求。

 

pl/sql简单分类

块(编程)包含:

过程(存储过程)、函数、触发器、包

 

pl/sql基础知识--编写规范

1、注释

单行注释:--

多行注释:/*...*/来划分

 

2、标识符号的命名规范

1)当定义变量时,建议用v_作为前缀;如:v_sal

2)当定义常量时,建议用c_作为前缀;如:c_rate

3)当定义游标时,建议用_cursor作为后缀;如:emp_cursor

4)当定义例外时,建议用e_作为前缀;如:e_error

 

pl/sql块介绍

块(block)是pl/sql的基本程序单元,编写pl/sql程序实际上就是编写pl/sql块。要完成相对简单的应用功能,可以只需要编写一个pl/sql块;但是如果要想实现复杂的功能,可能需要在一个pl/sql块中嵌套其它的pl/sql块。

 

块结构示意图:

pl/sql块由三个部分构成:定义部分、执行部分、例外处理部分。

如下所示:

declare

/*定义部分----定义常量、变量、游标、例外、复杂数据类型*/

begin

/*执行部分----要执行的pl/sql语句和sql语句*/

exception

/*例外处理部分----处理运行的各种错误*/

end;

 

重要说明:

1、定义部分是从declare开始的,该部分是可选的;

2、执行部分是从begin开始的,该部分是必需的;

3、例外处理部分是从exception开始的,该部分是可选的。

 

pl/sql实例:只包括执行部分的pl/sql块

案例:输出hello,world

相关说明:dbms_output是oracle所提供的包(类似java的开发包),该包包含一些过程,put_line就是dbms_output的一个过程。

--开发一个只包括执行部分的block块

set serveroutput on;

begin

dbms_output.put_line('hello,world');

end;

/

特别说明:在默认情况下,dbms_output.put_line是不输出内容的,需要set serveroutput on;才可输出。

 

实例2:包含定义部分和执行部分的pl/sql块

案例:根据用户输入的雇员编号,显示该雇员的名字

相关说明:

&表示要接收从控制台输入的变量

||表示把两个串拼接起来

declare

--定义变量的格式:变量名称 变量类型

v_ename varchar2(64);

begin

select ename into v_ename from emp where empno=&empno;--把查询到的ename放到v_ename变量中

--输出v_ename

dbms_output.put_line('雇员名字:'||v_ename);

end;

/

 

将上面的块改为过程

create procedure pro3(in_empno number) is

v_ename varchar2(64);

begin

select ename into v_ename from emp where empno=in_empno;

dbms_output.put_line('雇员名字:'||v_ename);

end;

/

 

实例3--包含定义部分、执行部分和例外处理部分

为了避免pl/sql程序的运行错误,提高pl/sql的健壮性,应该对可能的错误进行处理,这个很有必要:

1、比如在实例2中,如果输入了不存在的雇员号,应当做例外处理;

2、有时出现异常,希望用另外的逻辑处理。比如,如果不存在就加入编号为1,名字为“马大哈”这么一个人。

我们看看如何完成1的要求:

相关说明:oracle事先预定义了一些例外,no_data_found就是找不到数据的例外。

可以查看pl/sql官方文档看看oracle提供了哪些例外。

declare

v_ename varchar2(64);

begin

select ename into v_ename from emp where empno=&empno;

dbms_output.put_line('雇员名字:'||v_ename);

exception

when no_data_found then

dbms_output.put_line('你查询的雇员信息不存在!');

end;

/

对该案例的细节说明:

这里我们涉及到异常处理,

异常的基本语法:

exception

when 异常的名称1 then

//对异常处理的代码

when 异常的名称2 then

//对异常处理的代码

end;

 

异常处理的作用:

1、可以捕获异常,可以给出明确提示;

2、有时可以利用异常来进行业务处理。

 

declare

v_ename varchar2(64);

begin

select ename into v_ename from emp where empno=&empno;

dbms_output.put_line('雇员名字:'||v_ename);

exception

when no_data_found then

dbms_output.put_line('你查询的雇员信息不存在!加入一条信息');

isnert into emp (empno,ename) values(1,'马大哈');

end;

/

 

java捕获异常

try{

  //如果用户输入字串

  int abc=Integer.parse(str);

}catch(Exception e){

  //对不起你输入的字串不是一个数

}

 

 

pl/sql基础知识--过程快速入门

过程

过程用于执行特定的操作,当建立过程时,既可以指定输入参数(in),也可以指定输出参数(out)。通过在过程中使用输入参数,可以将数据传递到执行部分;通过使用输出参数,可以将执行部分的数据传递到应用环境。在sqlplus中可以使用create procedure命令来建立过程。

实例如下:

1、请考虑编写一个过程,可以输入雇员名,新工资可修改雇员的工资

2、如何调用过程有两种方法:

exec 过程名(参数值,..)

call 过程名(参数值,..)

 

创建存储过程基本语法:

create or replace procedure 过程名(变量 in 变量类型,..,变量 out 变量类型) is

//定义变量

begin

//执行语句;

end;

/

特别说明:or replace在创建存储过程中可带也可不带。带or replace是指在存储过程名字相同时将其覆盖。不带则无法覆盖。在使用or replace时要小心,建议不使用or replace对原存储过程进行覆盖。

 

举例:请考虑编写一个过程,可以输入雇员名,新工资可修改雇员的工资

create procedure update_sal(in_name in varchar2,in_new_sal in number) is

begin

update emp set sal=in_new_sal where ename=in_name;

dbms_output.put_line('更新成功!');

end;

/

特别说明:当编写过程出现错误时,查看具体错误信息。输入show error;

 

java中调用过程

3、如何在java程序中调用一个存储过程?

动手体验:我们写一个java程序来调用前面的存储过程。

课堂小练习:编写一个过程,可以接受id和薪水,更新薪水,如果id不存在,需要在exception中捕获,并给出提示!

如何使用过程返回值?

特别说明:对于过程我们会在以后给大家详细具体的介绍,现在请大家先有一个概念。

Java代码:

package com.test;

 

import java.sql.CallableStatement;

import java.sql.Connection;

import java.sql.DriverManager;

public class TestProcedure {

    //调用oracle中update_sal存储过程

    public static void main(String[] args) {

        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", "tiger");

            //创建CallableStatement接口引用对象

            cs=ct.prepareCall("{call update_sal(?,?)}");

            //给?赋值

            cs.setString(1, "BOSS");

            cs.setFloat(2, 8888f);

            //执行我们的语句

            cs.execute();

            //提交

            ct.commit();

        } catch (Exception e) {

            e.printStackTrace();

        }finally{

            try {

                if(cs!=null){

                    cs.close();

                }

                if(ct!=null){

                    ct.close();

                }

            } catch (Exception e2) {

                e2.printStackTrace();

            }

            cs=null;

            ct=null;

        }

    }

}

对前面的java程序的SQLHelper类进行升级,添加一个可以调用存储过程的方法

代码如下:

private static CallableStatement cs=null;

    //调用存储过程的方法

    public static void executeProcedure(String sql,String [] parameters){

        try {

            ct=DriverManager.getConnection(url,username,password);

            cs=ct.prepareCall(sql);

            if(parameters!=null){

                for(int i=0;i<parameters.length;i++){

                    cs.setString(i+1, parameters[i]);

                    System.out.println(parameters[i]);

                }

            }

            //执行

            cs.execute();

        } catch (Exception e) {

            e.printStackTrace();

            throw new RuntimeException(e.getMessage());

        }finally{

            close(rs, cs, ct);

        }

    }

调用方法:

        //当我们需要去调用过程的时候传SQL语句

        String sql="{call update_sal(?,?)}";

        String paras[]={"BOSS","1520"};

        SQLHelper.executeProcedure(sql, paras);

课堂小练习:编写一个过程,可以接受id和薪水,更新薪水,如果id不存在,需要在exception中捕获,并给出提示!需要在控制台和java程序中都调用。

oracle控制台

create procedure update_sal2(in_empno in number,in_new_sal in number) is

v_ename varchar2(32);

begin

select ename into v_ename from emp where empno=in_empno;

update emp set sal=in_new_sal where empno=in_empno;

dbms_output.put_line('更新成功!');

exception

when no_data_found then

dbms_output.put_line('输入的ID不存在!');

end;

/

 

 

 

pl/sql基本知识--函数快速入门

oracle函数

函数用于返回特定的数据,当建立函数时,在函数头部必须包含return子句,而在函数体内必须包含return语句返回的数据。我们可以使用create function来建立函数,实际案例:

建立函数的基本语法:

create function 函数名(参数1,...)

return 数据类型 is

    定义变量;

begin

    执行语句;

end;

/

 

函数调用的基本语法:

var 变量名 变量类型

call 函数名(参数值,...) into :变量名;

print 变量名

 

select 函数名(参数,...) from dual;

 

案例:请编写一个函数,可以接收用户名并返回该用户的年薪。

create function inName_outSal(v_in_name varchar2)

return number is

    v_annual_sal number;

begin

    select (sal+nvl(comm,0))*13 into v_annual_sal from emp where ename=v_in_name;

    return v_annual_sal;

end;

/

 

函数和过程的区别:

1、函数必须有返回值,而过程可以没有;

2、函数和过程在java中调用的方式不一样;

java中调用oracle函数可以在select语句中直接调用,如:select 自定义的函数名(参数) from 表;

过程则是使用CallableStatement完成调用。

 

Java调用函数方式

package com.test;

import java.sql.ResultSet;

import java.sql.SQLException;

public class TestFunction {

    //如何在java中调用自己编写的函数

    public static void main(String[] args) {

        String sql="select inName_outSal('KING') annual from dual";

        ResultSet rs=SQLHelper.executeQuery(sql, null);

        try {

            if(rs.next()){

                System.out.println(rs.getDouble("annual"));//此处可以用数字或别名接收返回值

            }

        } catch (SQLException e) {

            e.printStackTrace();

        }finally{

            if(rs!=null){

                try {

                    rs.close();

                } catch (SQLException e) {

                    e.printStackTrace();

                }

            }

            rs=null;

        }

    }

}

pl/sql基本知识--包

包用于在逻辑上组合过程和函数,它由包规范和包体两部分组成。

1、我们可以使用create package命令来创建包

建包基本语法:

create [or replace] package 包名 is

    procedure 过程名(变量名 变量类型,...);

    function 函数名(变量名 变量类型,...) return 数据类型;

end;

/

包的规范只包含了过程和函数的说明,但是没有过程和函数的实现代码。包体用于实现包规范的过程和函数。

 

请编写一个包,该包有一个过程,该过程可以接收用户名和新的薪水。(将来用于通过用户去更新薪水)还有一个函数,该函数可以接收一个用户名(将来要实现得到该用户的年薪是多少)

create package emp_package is

    procedure update_sal(v_in_ename varchar2,v_in_newsal number);

    function inName_outSal(v_in_name varchar2) return number;

end;

2、建立包体可以使用create package body 命令

建立包体基本语法:

create or replace package body 包名 is

    procedure 过程名(变量名 变量类型,...) is

       --声明变量;

    begin

       --执行语句;

    exception

    when 异常名 then

       --异常处理;

    end;

    function 函数名(变量名 变量类型,...)

    return 数据类型 is

       --声明变量;

    begin

       --执行语句;

    end;

end;

/

 

案例:请实现前面定义的包中的过程和函数。

create or replace package body emp_package is

    procedure update_sal(v_in_ename varchar2,v_in_newsal number) is

        v_empno number;

    begin

        select empno into v_empno from emp where ename=v_in_ename;

        update emp set sal=v_in_newsal where ename=v_in_ename;

        dbms_output.put_line('员工号为:'||v_empno||'的薪水更新成功');

    exception

    when no_data_found then

        dbms_output.put_line('您输入的人员信息不存在!');

    end;

    function inName_outSal(v_in_name varchar2)

    return number is

        v_annual_sal number;

    begin

        select (sal+nvl(comm,0))*13 into v_annual_sal from emp where

 

ename=v_in_name;

        return v_annual_sal;

    end;

end;

/

 

细节说明:

1、包体中要现实的函数或过程,应当在包规范中声明;

2、在调用包中的某个函数或过程的时候,需要使用对应的方法才可以调用。

3、如何调用包的过程或函数

当调用包的过程或是函数时,在过程和函数前需要带有包名,如果要访问其它方案的包,还需要在包名前加方案名。

调用基本方法:

exec 方案名.包名.过程名(参数,...);

call 方案名.包名.函数名(参数,...);

也可以直接用select 方案名.包名.函数名(参数,...) from dual;

 

在java中调用oracle包下的过程和函数与之前java调用的过程和函数是一致的,只是尽可能的将方案名.包名加上。

String sql="{call scott.emp_package.update_sal(?,?)}";//过程

String sql="select scott.emp_package.inName_outSal('KING') annual from dual";//函数

 

特别说明:包是pl/sql中非常重要的部分,在使用过程分页时,将会再次体验它的威力。

 

pl/sql基础知识--触发器

触发器简单介绍

触发器是指隐含的执行的存储过程。当定义触发器时,必须要指定触发的事件和触发的操作,常用的触发事件包括insert/update/delete语句,而触发操作实际就是一个pl/sql块。可以使用create trigger 来建立触发器。

特别说明:

我们会在后面详细为大家介绍触发器的使用,因为触发器是非常有用的,可维护数据库的案例和一致性。

 

pl/sql基础知识--定义并使用变量

介绍

在编写pl/sql程序时,可以定义变量和常量;在pl/sql程序中包括有:

1、标量类型(scalar)

2、复合类型(composite)

3、参照类型(reference)

4、lob(large object)

 

标量(scalar)--常用类型

在编写pl/sql块时,如果要使用变量,需在定义部分定义变量。

pl/sql中定义变量和常量的语法如下:

identifier [constant] datatype [not null] [:=|default expr]

名称 [指定常量] 数据类型 [不为null] [:=(赋初值) | default(默认值) expr(指定初始值)]

说明:

identifier:名称

constant:指定常量。需要指定它的初始值,且其值是不能改变的。

datatype:数据类型

not null:指定变量值不能为null

:=给变量或是常量指定初始值

default:用于指定初始值

expr:指定初始值的pl/sql表达式,可是文本值、其它变量、函数等。

 

标量定义的案例:

1、定义一个变长字符串

v_ename varchar2(10);

2、定义一个小数范围-9999.99~9999.99

v_sal number(6,2);

3、定义一个小数并给一个初始值为5.4 :=是pl/sql的赋值号

v_sal2 number(6,2):=5.4

4、定义一个日期类型的数据

v_hiredate date;

5、定义一个布尔变量,不能为空,初始值为false

v_valid boolean not null default false;

 

特别说明:pl/sql在定义一个变量的时候,如果要赋初值,则需要使用:=,如果只是=则是用于判断两个值是否相等。

 

标量(scalar)使用标量

在定义好变量后,就可以使用这些变量。这里需要说明的是pl/sql块为变量赋值不同于其它的编程语言,需要在等号前加冒号(:=)

案例:以输入员工号,显示雇员姓名、工资、个人所得税(税率为0.03)为例。说明变量的使用,看看如何编写?

create or replace procedure emp_info(in_empno number) is

v_ename varchar2(32);

v_sal number;

v_tax number;

c_tax_rate number(3,2):=0.03;

begin

    select ename,sal,sal*c_tax_rate into v_ename,v_sal,v_tax from emp where empno=in_empno;

    dbms_output.put_line('姓名:'||v_ename||'    工资:'||v_sal||'    个人所得税:'||v_tax);

    exception

    when no_data_found then

    dbms_output.put_line('你的输入有误!');

end;

/

 

标量(scalar)--使用%type类型

对于上面的pl/sql块有一个问题:

就是如果员工的姓名超过了5字符的话,就会有错误,为了降低pl/sql程序的维护工作量,可以使用%type属性定义变量,这样它会按照数据库列来确定你定义的变量的类型和长度。

 

我们看看这个怎么使用:

%type类型使用的基本语法:

标识符名 表名.列名%type;

 

create or replace procedure emp_info(in_empno number) is

v_ename emp.ename%type;--为了让v_ename的类型更加灵活,我们使用%type,这样就会自适应

v_sal emp.sal%type;

v_tax number;

c_tax_rate number(3,2):=0.03;

begin

    select ename,sal,sal*c_tax_rate into v_ename,v_sal,v_tax from emp where empno=in_empno;

    dbms_output.put_line('姓名:'||v_ename||'    工资:'||v_sal||'    个人所得税:'||v_tax);

    exception

    when no_data_found then

    dbms_output.put_line('你的输入有误!');

end;

/

 

复合变量(composite)--介绍

用于存放多个值的变量。常用的包括:1、pl/sql记录;2、pl/sql表

 

复合类型--pl/sql记录

类似与高级语言中的结构体,需要注意的是,当引用pl/sql记录成员时,必须要加记录变量作为前缀(记录变量.记录成员)如下:

复合变量定义基本语法:

type 自定义的pl/sql记录名 is record(

变量名 变量类型,

变量名 变量类型

);

 

//使用自定义的pl/sql记录

复合变量基本使用语法:

变量名 自定义的pl/sql记录名;

 

请编写一个过程,该过程可以接收一个用户编号,并显示该用户的名字,薪水,工作岗位(注意:要求用pl/sql记录实现)

create or replace procedure inEmpno(in_empno number) is

--定义一个记录数据类型

type my_emp_record is record(

v_ename emp.ename%type,

v_sal emp.sal%type,

v_job emp.job%type

);

--定义一个变量,该变量的类型是my_emp_record

v_emp_record my_emp_record;

begin

select ename,sal,job into v_emp_record from emp where empno=in_empno;

dbms_output.put_line('名字:'||v_emp_record.v_ename||'  工资:'||v_emp_record.v_sal||'  职位:'||v_emp_record.v_job);

exception

when no_data_found then

dbms_output.put_line('你的输入有误!');

end;

/

 

复合类型--pl/sql表(了解即可)

相当于高级语言中的数组,但是需要注意的是在高级语言中数组的下标不能为负数,而pl/sql是可以为负数的,并且表元素的下标没有限制。(可以理解为是oracle下的数组)实例如下:

 

复合类型pl/sql表的基本语法:

type 自定义的pl/sql表名 is table of 对应表.列名%type

index by binary_integer;

//使用自定义的pl/sql表

变量名 自定义的pl/sql表名;

declare

type sp_table_type is table of emp.ename%type

index by binary_integer;

sp_table sp_table_type;--定义一个变量:sp_table类型

begin

select ename into sp_table(-1) from emp where empno=7788;

dbms_output.put_line('员工名:'||sp_table(-1));

end;

说明:

sp_table_type 是pl/sql表类型

emp.ename%type 指定了表的元素的类型和长度

sp_table 为pl/sql表变量

sp_table(0) 则表示下标为0的元素

参照变量--介绍(重点,必须掌握)

参照变量是指用于存放数值指针的变量,通过使用参照变量,可以使得应用程序共享相同对象,从而降低占用的空间。在编写pl/sql程序时,可以使用游标变量(ref cursor)和对象类型变量(ref obj_type)两种参照变量类型。

 

游标变量

通过游标可以取得返回结果集(这个结果集,往往是select语句的结果)的任何一行数据,从而提供共享的效率。

 

参照变量--游标(ref cursor)使用

定义游标基本语法:

type 自定义游标名 is ref cursor;

变量名 自定义游标名;

 

打开游标基本语法:

open 游标变量 for select 语句;

 

取出当前游标指向的行基本语法:

fetch 游标变量 into 其它变量;

 

判断游标是否指向记录最后基本语法:

游标变量%notfound

 

参照变量--游标(ref cursor)变量

使用游标时,当定义游标时不需要指定相应的select语句,但是当使用游标时,(open时)需要指定select语句,这样一个游标就写一个select语句结合了。

实例如下:

1、请使用pl/sql编写一个过程,可以输入部门号,并显示该部门所有员工姓名和他的工资。

create or replace procedure test_cursor(in_deptno in number) is

    --定义一个记录数据类型

    type my_emp_record is record(v_ename emp.ename%type,v_sal emp.sal%type);

    --定义一个变量,该变量的类型是my_emp_record

    v_emp_record my_emp_record;

    --先定义一个游标变量类型

    type my_emp_cursor is ref cursor;

    --定义一个游标变量

    v_emp_cursor my_emp_cursor;

begin

    --打开游标,执行语句

    open v_emp_cursor for select ename,sal from emp where deptno=in_deptno;

    --取出游标指向的每行数据,使用循环语句取出

    loop

        fetch v_emp_cursor into v_emp_record;--会引起游标向下走

        --判断当前游标是否到达最后

        exit when v_emp_cursor%notfound;--判断游标是否为空,为空时退出循环

        dbms_output.put_line('姓名:'||v_emp_record.v_ename||'    工资:'||v_emp_record.v_sal);

    end loop;

    --关闭游标[游标使用完后,一定要关闭,避免资源浪费]

    close v_emp_cursor;

end;

/

 

2、在1基础上,如果某个员工的工资低于200元,就增加100元。

create or replace procedure test_cursor(in_deptno in number) is

    --创建一个记录数据类型

    type my_emp_record is record(v_ename emp.ename%type,

    v_sal emp.sal%type,

    v_empno emp.empno%type

    );

    v_emp_record my_emp_record;

    --创建游标

    type my_emp_cursor is ref cursor;

    v_emp_cursor my_emp_cursor;

begin

    open v_emp_cursor for select ename,sal,empno from emp where deptno=in_deptno;

    loop

        fetch v_emp_cursor into v_emp_record;

        exit when v_emp_cursor%notfound;

        --判断工资低于200的人,加100块

        if v_emp_record.v_sal<200 then

            v_emp_record.v_sal:=v_emp_record.v_sal+100;

            update emp set sal=v_emp_record.v_sal where empno=v_emp_record.v_empno;

        end if;

        dbms_output.put_line('姓名:'||v_emp_record.v_ename||'    工资:'||v_emp_record.v_sal);

    end loop;

    close v_emp_cursor;

end;

/

 

 

pl/sql练习题:

使用pl/sql块编程实现,注意必需的异常处理。

1、输入一个员工号,输出该员工的姓名、薪金和大概的服务年限(按年月日显示)

declare

    v_ename emp.ename%type;

    v_sal emp.sal%type;

    v_year varchar2(20);

begin

    select ename,sal,to_char(to_date('00010101','yyyymmdd')+(sysdate-hiredate)-366-31,'yy"年"mm"个月"dd"天"') into v_ename,v_sal,v_year from emp where empno=&empno;

    dbms_output.put_line('姓名:'||v_ename||'  薪金:'||v_sal||'  服务年限:'||v_year);

exception

    when no_data_found then

    dbms_output.put_line('您输入的员工编号不存在!');

end;

/

 

2、接收一个员工号,输出该员工所在部门的名称

declare

    v_ename emp.ename%type;

    v_dname dept.dname%type;

begin

    select e.ename,d.dname into v_ename,v_dname from emp e,dept d where e.deptno=d.deptno and empno=&empno;

    dbms_output.put_line('姓名:'||v_ename||'  所在部门:'||v_dname);

exception

    when no_data_found then

    dbms_output.put_line('您输入的员工编号不存在!');

end;

/

 

3、接收一个部门号,如果该员工职位是MANAGER,并且在DALLAS工作那么就给他薪金加15%;如果该员工职位是CLERK,并且在NEW YORK工作,那么就给他薪金扣除5%,其他情况不作处理。

declare

    v_deptno number;

    v_deptno1 dept.deptno%type;

    v_deptno2 dept.deptno%type;

begin

    v_deptno:=&deptno;

    select deptno into v_deptno1 from dept where loc='DALLAS';

    select deptno into v_deptno2 from dept where loc='NEW YORK';

    if v_deptno=v_deptno1 then

        update emp set sal=sal*1.15 where job='MANAGER' and deptno=v_deptno1;

        dbms_output.put_line('已将job为MANAGER且在DALLAS工作的员工薪资提高了15%');

    elsif v_deptno=v_deptno2 then

        update emp set sal=sal*0.95 where job='CLERK' and deptno=v_deptno2;

        dbms_output.put_line('已将job为CLERK且在NEW YORK工作的员工薪资降低了5%');

    else

        dbms_output.put_line('数据未做修改或输入的部门号不存在!');

    end if;

end;

/

 

4、接收一个员工号,输出这个员工所在部门的平均工资

declare

    v_deptno emp.deptno%type;

    v_avgsal number(7,2);

begin

    select deptno into v_deptno from emp where empno=&empno;

    select avg(sal+nvl(comm,0)) into v_avgsal from emp where deptno=v_deptno;

    dbms_output.put_line(v_deptno||'部门的平均工资为:'||v_avgsal);

exception

    when no_data_found then

    dbms_output.put_line('您输入的员工编号不存在!');

end;

/

 

5、以交互的方式给部门插入一条记录,如果出现主键冲突的异常,请显示“部门号已被占用”的字样。

declare

    v_deptno dept.deptno%type;

    v_dname dept.dname%type;

    v_loc dept.loc%type;

begin

    v_deptno:=&deptno;

    v_dname:=&dname;

    v_loc:=&loc;

    insert into dept (deptno,dname,loc) values(v_deptno,v_dname,v_loc);

    dbms_output.put_line('部门编号:'||v_deptno||'部门名称:'||v_dname||'所在地:'||v_loc||'已成功添加!');

exception

    when dup_val_on_index then

    dbms_output.put_line('部门号已被占用!');

end;

/

 

过程函数练习题:

1、建立一个存储过程用来接收一个员工号,返回他的工资和他所在的部门的平均工资并作为传出参数传出。

create or replace procedure inempno_outsaldname(in_empno in number,out_sal out number,out_avgsal out number) is

v_deptno emp.deptno%type;

begin

    select sal,deptno into out_sal,v_deptno from emp where empno=in_empno;

    dbms_output.put_line('部门号:'||v_deptno);

    select avg(sal) into out_avgsal from emp where deptno=v_deptno;

    dbms_output.put_line('平均工资:'||out_avgsal);

end;

/

 

create or replace procedure in_out_emp(in_empno number) is

v_sal number;

v_avgsal number(7,2);

begin

    inempno_outsaldname(in_empno,v_sal,v_avgsal);

    dbms_output.put_line('员工号:'||in_empno||'工资为:'||v_sal||'平均工资为:'||v_avgsal);

exception

    when no_data_found then

    dbms_output.put_line('您输入的员工编号不存在!');

end;

/

 

2、建立一个存储过程用来接收一个部门号,找出其中的两位最老的员工的员工号,并打印。

create or replace procedure indeptno_outhiredate(in_deptno number) is

    type my_emp_cursor is ref cursor;

    v_emp_cursor my_emp_cursor;

    v_i number;

   type my_emp_record is record(v_empno emp.empno%type,v_ename emp.ename%type,v_hiredate emp.hiredate%type);

    v_emp_record my_emp_record;

begin

    v_i:=0;

   open v_emp_cursor for select empno,ename,hiredate from emp where deptno=in_deptno order by hiredate;

    loop

        fetch v_emp_cursor into v_emp_record;

        v_i:=v_i+1;

        if(v_i=3)then

            exit;

        end if;

        dbms_output.put_line('员工编号:'||v_emp_record.v_empno||'姓名:'||v_emp_record.v_ename||'入职日期:'||to_char(v_emp_record.v_hiredate,'yyyy-mm-dd'));

    end loop;

    close v_emp_cursor;

exception

    when no_data_found then

    dbms_output.put_line('您输入的部门编号不存在!');

end;

/

 

3、编写一个过程用来传入一个员工号,在emp表中删除一个员工,当该员工是该部门的最后一个员工时就在dept表中删除该员工所在的部门。

create or replace procedure inempno_deldept(in_empno number) is

    v_count number;

    v_empno emp.empno%type;

    v_deptno emp.deptno%type;

begin

    v_empno:=in_empno;

    select deptno into v_deptno from emp where empno=v_empno;

    delete from emp where empno=v_empno;

    dbms_output.put_line('工号:'||v_empno||'成功删除!');

    select count(*) into v_count from emp where deptno=v_deptno;

    dbms_output.put_line(v_deptno||'部门还有'||v_count||'雇员!');

    if v_count=0 then

        delete from dept where deptno=v_deptno;

        dbms_output.put_line(v_deptno||'删除部门成功!');

    end if;

exception

    when no_data_found then

    dbms_output.put_line('您输入的员工编号不存在!');

end;

/

 

 

 

 

pl/sql的进阶

Procedural Language/SQL叫做过程化SQL编程语言,是oracle对SQL语句的扩展。在普通SQL语句的使用上增加了编程语言的特点,所以pl/sql就是把数据操作和查询语言组织在pl/sql代码的过程性单元中,通过逻辑判断,循环等操作实现复杂的功能或者计算的程序语言。

 

pl/sql进阶--控制结构

在任何计算机语言(c,java,c#,c++)都有各种控制语句(条件语句,循环语句,顺序控制结构..)在pl/sql中也存在这样的控制结构。

 

条件分支语句

pl/sql中提供了三种条件分支语句if--then,if--then--else,if--then--elsif--elsif--else这里我们可以和java语句进行一个比较。

 

简单的条件判断if--then

基本语法:

if 条件表达式 then

    执行语句...;

end if;

 

编写一个过程,可以输入一个雇员名,如果该雇员的工资低于2000,就给该雇员工资增加10%。

create or replace procedure inempno_upsal(in_ename varchar2) is

v_sal emp.sal%type;

begin

    select sal into v_sal from emp where ename=in_ename;

    if v_sal<2000 then

        update emp set sal=sal*1.1 where ename=in_ename;

    end if;

exception

    when no_data_found then

    dbms_output.put_line('您输入的姓名不存在!');

end;

/

 

二重条件分支if--then--else

基本语法:

if 条件表达式 then

    执行语句;

else

    执行语句;

end if;

 

编写一个过程,可以输入一个雇员名,如果该雇员的补助不是0就在原来的基础上增加100;如果补助为0就把补助设为200;

create or replace procedure inname_upcomm(in_ename varchar2) is

v_comm emp.comm%type;

begin

    select nvl(comm,0) into v_comm from emp where ename=in_ename;

    if v_comm<>0 then

        update emp set comm=comm+100 where ename=in_ename;

    else

        update emp set comm=comm+200 where ename=in_ename;

    end if;

exception

    when no_data_found then

    dbms_output.put_line('您输入的姓名不存在!');

end;

/

 

多重条件分支if--then--elsif--else

基本语法:

if 条件表达式 then

    执行语句;

elsif 条件表达式 then

    执行语句;

else

    执行语句;

end if;

 

编写一个过程,可以输入一个雇员编号,如果该雇员的职位是PRESIDENT就给他的工资增加1000,如果该雇员职位是MANAGER就给他的工资增加500,其它职位的雇员工资增加200。

create or replace procedure inempno_upsal(in_empno number) is

v_job emp.job%type;

begin

    select job into v_job from emp where empno=in_empno;

    if v_job='PRESIDENT' then

        update emp set sal=sal+1000 where empno=in_empno;

    elsif v_job='MANAGER' then

        update emp set sal=sal+500 where empno=in_empno;

    else

        update emp set sal=sal+200 where empno=in_empno;

    end if;

exception

    when no_data_found then

    dbms_output.put_line('您输入的编号有误!');

end;

/

 

循环结构--loop

是pl/sql中最简单的循环语句,这种循环语句以loop开头,以end loop结尾,这种循环至少会被执行一次。

基本语法:

loop

    执行语句;

exit when 条件表达式;

end loop;

 

案例:现在有一张表users,表结构如下:

用户ID

用户名

 

 

请编写一个过程,可以输入用户名和添加用户的个数n;循环添加n个用户到users表中,用户编号从1开始增加,直到n

create table users(userId number primary key,userName varchar2(32));

create or replace procedure inname_adduser(in_username varchar2,in_n number) is

    v_i number:=0;

begin

    loop   

        exit when in_n<=0;

        v_i:=v_i+1;

        insert into users values(v_i,in_username);

        exit when v_i=in_n;

    end loop;

end;

/

 

循环语句--while循环

基本循环至少要执行循环体一次,而对于while循环来说,只有条件为true时,才会执行循环体语句,while循环以while..loop开始,以end loop结束。

基本语法:

while 条件表达式 loop

    执行语句;

end loop;

 

案例:现在有一张表users,表结构如下:

用户ID

用户名

 

 

请编写一个过程,可输入用户名,并循环添加10个用户到users表中,用户编号从11开始增加。

create table users(userId number primary key,userName varchar2(32));

create or replace procedure inname_addusers(in_name varchar2) is

v_i number:=0;

v_j number:=10;

begin

    while v_i<10 loop

        v_j=v_j+1;

        insert into users values(v_j,in_name);

    end loop;

end;

/

 

看下面题判断是否正确

下面的过程是否正确,如果不正确,应该怎么改?

create or replace procedure sp_pro6(spName varchar2) is

v_test varchar2(40);

v_test:='aaa';--赋初值需在定义时就直接赋值,或在定义之后在begin中进行赋值。

begin

    dbms_output.put_line(v_test);

end;

 

create or replace procedure sp_pro6(spName varchar2) is

v_test varchar2(40):='aaa';

begin

    spName:='你好';--spName为传入变量,不能重复赋值。

    dbms_output.put_line(v_test||spName);

end;

 

说明:

1、在is--begin之间只能定义变量类型同时初始化赋值,或定义变量类型后在begin内进行赋值,不能在定义变量类型之后再对变量赋值。

2、传入的参数变量不能在存储过程中再次赋值。

 

循环语句--for循环

基本for循环的基本结构如下:

begin

    for i in reverse 1..10 loop

        insert into users values(i,'顺平');

    end loop;

end;

 

基本语法:

for 变量 in reverse 开始值..结束值 loop

    执行语句;

end loop;

我们可以看到控制变量i,在隐含中就在不停的增加

注意:推荐使用loop循环结构,不推荐使用for循环。

 

顺序控制语句--goto,null

1、goto语句

goto语句用于跳转到特定标号去执行语句,注意由于使用goto语句会增加程序的复杂性,并使得应用程序可读性变差,所以在做一般应用开发时,建议大家不要使用goto语句。基本语法如下:goto lable,其中lable是已定义好的标号名。

 

基本语法:goto 标号;

标号定义:<<标号>>

 

例:

declare

i number:=1;

begin

<<start_loop>>

    loop

        dbms_output.put_line('输出i='||i);

        if i=12 then

            goto end_loop;

        end if;

        i:=i+1;

        if i=10 then

            goto start_loop;

        end if;

    end loop;

<<end_loop>>

    dbms_output.put_line('循环结束');

end;

--输出1至12 循环结束。

 

2、null

null语句不会执行任何操作,并且会直接将控制传递到下一条语句。使用null语句的主要好处是可以提高pl/sql的可读性。

例:

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;

 

pl/sql进阶--编写分页过程

介绍

分页是任何一个网站(bbs、网上商城、blog)都会使用到的技术,因此学习pl/sql编程开发一定要掌握该技术。

 

无返回值的存储过程

古人云:欲速则不达,为了让大家比较容易接受分页过程编写,还是从简单到复杂,循序渐进的给大家讲解。首先是掌握最简单的存储过程,无返回值的存储过程:

案例:现在有一张表book,表结构如下:

字段名      字段类型

id          number(5)

name        varchar2(100)

pubHouse    varchar2(100)

请编写一个过程,可以向book表添加书,要求通过java程序调用该过程。

提示查看jdk,看看CallableStatement是怎么调存储过程的!

建book表

create table book(id number(5) primary key,name varchar2(100) not null,pubHouse varchar2(100));

create or replace procedure inBook(in_id number,in_name varchar2,in_pubHouse varchar2)

begin

    insert into book values(in_id,in_name,in_pubHouse);

exception

    when dup_val_on_index then

    dbms_output.put_line('错误:序号不能为空或不能重复!');

end;

 

java调用无返回值的存储过程代码:

        //当我们需要去调用过程的时候传SQL语句

        String sql="{call 存储过程名称(?,?)}";//调用格式{call 用户区.包名.过程名(参数?)}

        String paras[]={"过程中的参数1","过程中的参数2"};

        SQLHelper.executeProcedure(sql, paras);

 

public class SQLHelper {

    //定义三个变量

    private static Connection ct=null;

    private static PreparedStatement ps=null;

    private static ResultSet rs=null;

    private static CallableStatement cs=null;

    //连接数据库的用户名,密码,url,驱动

    //说明:在实际开发中,我们往往把这些变量写到一个外部文件中

    //当程序启动时,我们读入这些配置信息。java.util.Properites

    private static String username;

    private static String password;

    private static String driver;

    private static String url;

    //使用静态块加载驱动(驱动只需要加载一次)

    static{

        //使用Properties类,来读取配置文件

        Properties pp=new Properties();

        FileInputStream fis=null;

        try {

            fis=new FileInputStream("dbinfo.properties");

            //让pp与dbinfo.properties文件关联起来

            pp.load(fis);

            //获取dbinfo.properties文件内信息

            username=pp.getProperty("username");

            password=pp.getProperty("password");

            driver=pp.getProperty("driver");

            url=pp.getProperty("url");

           

            //获得驱动

            Class.forName(driver);

        } catch (Exception e) {

            e.printStackTrace();

        }finally{

            try {

                if(fis!=null){

                    fis.close();

                }

            } catch (Exception e) {

                e.printStackTrace();

            }

            fis=null;

        }

    }

    //调用存储过程的方法

    public static void executeProcedure(String sql,String [] parameters){

        try {

            ct=DriverManager.getConnection(url,username,password);

            cs=ct.prepareCall(sql);

            if(parameters!=null){

                for(int i=0;i<parameters.length;i++){

                    cs.setString(i+1, parameters[i]);

                    System.out.println(parameters[i]);

                }

            }

            //执行

            cs.execute();

            ct.commit();

        } catch (Exception e) {

            e.printStackTrace();

            throw new RuntimeException(e.getMessage());

        }finally{

            close(rs, cs, ct);

        }

    }

    //把关闭资源写成函数

    public static void close(ResultSet rs,Statement ps,Connection ct){

        //关闭资源

        if(rs!=null){

            try {

                rs.close();

            } catch (SQLException e) {

                e.printStackTrace();

            }

            rs=null;

        }

        if(ps!=null){

            try {

                ps.close();

            } catch (SQLException e) {

                e.printStackTrace();

            }

            ps=null;

        }

        if(ct!=null){

            try {

                ct.close();

            } catch (SQLException e) {

                e.printStackTrace();

            }

            ct=null;

        }

    }

}

 

dbinfo.properties 连接Oracle数据库配置文件

username=scott

password=tiger

driver=oracle.jdbc.driver.OracleDriver

url=jdbc\:oracle\:thin\:@127.0.0.1\:1521\:orcl

 

有返回值的存储过程(非列表,只有一个返回值)

再看如何处理有返回值的存储过程:

 

建立有返回值的存储过程基本语法:

create or replace procedure 过程名(参数名 in 类型,..,参数名 out 类型,..) is

    定义变量..;

begin

    执行语句..;

exception

    when 错误提示 then

    处理或提示语句;

end;

 

案例:编写一个过程,可以输入雇员的编号,返回该雇员的姓名。

create or replace procedure inEmpno_outEname(in_v_empno in number,out_v_ename out varchar2) is

begin

    select ename into out_v_ename from emp where empno=in_v_empno;

exception

    when no_data_found then

    dbms_output.put_line('您输入的雇员编号不存在!');

end;

 

在java中去调用该过程,并接受返回的用户名。

[TestProcedureOutValues.java]源码示例:

package com.test;

 

import java.sql.CallableStatement;

import java.sql.Connection;

import java.sql.DriverManager;

import java.sql.ResultSet;

 

public class TestProcedureOutValues {

    //调用oracle存储过程并获得存储过程的返回值。

    public static void main(String[] args) {

        //定义需要的变量

        Connection ct=null;

        CallableStatement cs=null;

        ResultSet rs=null;

        try {

            //加载驱动

            Class.forName("oracle.jdbc.driver.OracleDriver");

            //得到连接

            ct=DriverManager.getConnection("jdbc:oracle:thin:@127.0.0.1:1521:orcl","scott","tiger");

            //创建CallableStatement接口

            cs=ct.prepareCall("{call inEmpno_outEname(?,?)}");//第一个?输入值,第二个?是获得值。

            //给第一个?赋值

            cs.setString(1, "7839");

            //给第二个?注册(因为它是输出值)

            cs.registerOutParameter(2, oracle.jdbc.OracleTypes.VARCHAR);

            //执行

            cs.execute();

            //取出输出的值

            String ename=cs.getString(2);

            System.out.println("用户的名字是:"+ename);

        } catch (Exception e) {

            e.printStackTrace();

        }finally{

            //关闭资源

            try {

                if(cs!=null){

                    cs.close();

                }

                if(rs!=null){

                    rs.close();

                }

                if(ct!=null){

                    ct.close();

                }

            } catch (Exception e) {

                e.printStackTrace();

            }

            cs=null;

            rs=null;

            ct=null;

        }

    }

}

说明:

1、对于过程的输入值,使用set方法,对于输出值使用registerOutParameter来注册接收返回值。问号的顺序要对应,同时考虑类型。

2、取出过程返回值的方法是CallableStatement提供的get方法(输出参数的位置);同时要考虑输出的参数类型。

 

java调用关键代码:

CallableStatement cs=null;

cs=ct.prepareCall("{call 过程名(?,?)}");

cs.registerOutParameter(输出参数的在第几个问号,oracle.jdbc.OracleTypes.类型);//Types.类型是输出参数的类型。

cs.get类型(输出参数在问号的位置);//不用的类型要用不同的get方法接收。

 

 

案例扩展:编写一个过程,可以输入雇员的编号,返回该雇员的姓名、工资和岗位。

create or replace procedure inEmpno_outAllinfo(in_v_empno in number,out_v_ename out varchar2,out_v_sal out number,out_v_job out varchar2) is

begin

    select ename,sal,job into out_v_ename,out_v_sal,out_v_job from emp where empno=in_v_empno;

exception

    when no_data_found then

    dbms_output.put_line('您输入的雇员编号不存在!');

end;

 

在java中去调用该过程,并接受返回的用户名、工资、职位。

[TestProcedureOutValues.java]返回多个值的调用存储过程源代码。

package com.test;

 

import java.sql.CallableStatement;

import java.sql.Connection;

import java.sql.DriverManager;

import java.sql.ResultSet;

 

public class TestProcedureOutValues {

    //调用oracle存储过程并获得存储过程的返回值。

    public static void main(String[] args) {

        //定义需要的变量

        Connection ct=null;

        CallableStatement cs=null;

        ResultSet rs=null;

        try {

            //加载驱动

            Class.forName("oracle.jdbc.driver.OracleDriver");

            //得到连接

            ct=DriverManager.getConnection("jdbc:oracle:thin:@127.0.0.1:1521:orcl","scott","tiger");

            //创建CallableStatement接口

            cs=ct.prepareCall("{call inEmpno_outAllinfo(?,?,?,?)}");//第一个?输入值,第二个?是获得值。

            //给第一个?赋值

            cs.setString(1, "7839");

            //给第二-四个?注册(因为它是输出值)

            cs.registerOutParameter(2, oracle.jdbc.OracleTypes.VARCHAR);

            cs.registerOutParameter(3, oracle.jdbc.OracleTypes.NUMBER);

            cs.registerOutParameter(4, oracle.jdbc.OracleTypes.VARCHAR);

            //执行

            cs.execute();

            //取出输出的值

            String ename=cs.getString(2);

            Float sal=cs.getFloat(3);

            String job=cs.getString(4);

            System.out.println("姓名:"+ename+"  工资:"+sal+"  职位:"+job);

        } catch (Exception e) {

            e.printStackTrace();

        }finally{

            //关闭资源

            try {

                if(cs!=null){

                    cs.close();

                }

                if(rs!=null){

                    rs.close();

                }

                if(ct!=null){

                    ct.close();

                }

            } catch (Exception e) {

                e.printStackTrace();

            }

            cs=null;

            rs=null;

            ct=null;

        }

    }

}

 

有返回值的存储过程(列表[结果集])

案例:编写一个过程,输入部门号,返回该部门所有雇员信息。

对该题分析如下:

由于oracle存储过程没有返回值,它的所有返回值都是通过out参数来替代的,列表同样也不例外,但由于是集合,所以不能用一般的参数,必须要用package了,步骤如下:

1、建一个包。

2、建立存储过程。

3、下面如何在java程序中调用。

 

创建包同时定义一个游标类型

create or replace package empPackage is

--定义一个游标数据类型

    type my_emp_cursor is ref cursor;

end;

 

创建存储过程

create or replace procedure indeptno_outAllInfo(v_in_deptno in number,v_out_result out empPackage.my_emp_cursor) is

begin

    open v_out_result for select * from emp where deptno=v_in_deptno;

--close v_out_result;--此处不能关闭游标,需要在程序中关闭游标。

end;

 

编写java程序获得存储过程返回的结果集。

[TestProcedureOutAllValues.java]源代码

package com.test;

 

import java.sql.CallableStatement;

import java.sql.Connection;

import java.sql.DriverManager;

import java.sql.ResultSet;

 

public class TestProcedureOutAllValues {

    //调用oracle存储过程并获得存储过程的返回结果集。

    public static void main(String[] args) {

        //定义需要的变量

        Connection ct=null;

        CallableStatement cs=null;

        ResultSet rs=null;

        try {

            //加载驱动

            Class.forName("oracle.jdbc.driver.OracleDriver");

            //得到连接

            ct=DriverManager.getConnection("jdbc:oracle:thin:@127.0.0.1:1521:orcl","scott","tiger");

            //创建CallableStatement接口

            cs=ct.prepareCall("{call indeptno_outAllInfo(?,?)}");

            //给?赋值

            cs.setInt(1, 20);

            //给第二个?注册

            cs.registerOutParameter(2, oracle.jdbc.OracleTypes.CURSOR);

            //执行

            cs.execute();

            /*这里是关键所在,java没有接收结果集的get方法,所以只能用getObject来接收结果集,接收到后需要使用ResultSet强转才可以。*/

            rs=(ResultSet)cs.getObject(2);

            //循环取出

            while(rs.next()){

                System.out.println(rs.getString("ename")+" "+rs.getString("sal"));

            }

        } catch (Exception e) {

            e.printStackTrace();

        }finally{

            //关闭资源

            try {

                if(cs!=null){

                    cs.close();

                }

                if(rs!=null){

                    rs.close();

                }

                if(ct!=null){

                    ct.close();

                }

            } catch (Exception e) {

                e.printStackTrace();

            }

            cs=null;

            rs=null;

            ct=null;

        }

    }

}

 

编写分页过程

有了上面的基础,相信大家可以完成分页存储过程了。

要求:请大家编写一个存储过程,要求可以输入表名、每页显示记录数、当前页,排序字段(deptno降序)。返回总记录数,总页数和返回的结果集。

 

把一个字符串,当做sql语句执行,并把查询得到的结果赋给某个变量,语法如下:

execute immediate v_sql into myrows;

基本语法:

execute immediate 变量(sql拼接语句) into 输出变量名;

 

温馨提示:

如果大家忘了oracle中如何分页,请参考第三天的内容

提示:为了讲的清楚明白,这里使用循序渐进的方法,逐步增加传入的参数来讲解。

 

先简化再复杂

通过输入表名、每页显示记录数、当前页,返回结果集。

1、创建包同时创建游标

create or replace package pagingPackage is

    type paging_cursor is ref cursor;

end;

 

2、创建分页存储过程

create or replace procedure paging_cursor(v_in_table in varchar2,v_in_pagesize in number,v_in_pagenow in number,v_out_result out pagingPackage.paging_cursor) is

--定义需要的变量

v_sql varchar2(4000);

v_start number;

v_end number;

begin

--执行代码

--计算v_start和v_end是多少

v_start:=v_in_pagesize*(v_in_pagenow-1)+1;

v_end:=v_in_pagesize*v_in_pagenow;

v_sql:='select t2.* from (select t1.*,rownum rn from (select * from '||v_in_table||') t1 where rownum<='||v_end||') t2 where rn>='||v_start;

--打开游标,让游标指向结果集

open v_out_result for v_sql;

end;

 

java调用分页存储过程

[TestProcedurePaging.java]源代码

package com.test;

import java.sql.CallableStatement;

import java.sql.Connection;

import java.sql.DriverManager;

import java.sql.ResultSet;

 

public class TestProcedurePaging {

    //调用oracle分页存储过程并获得存储过程的返回结果集。

    public static void main(String[] args) {

        //定义需要的变量

        Connection ct=null;

        CallableStatement cs=null;

        ResultSet rs=null;

        try {

            //加载驱动

            Class.forName("oracle.jdbc.driver.OracleDriver");

            //得到连接

            ct=DriverManager.getConnection("jdbc:oracle:thin:@127.0.0.1:1521:orcl","scott","tiger");

            //创建CallableStatement接口

            cs=ct.prepareCall("{call paging_cursor(?,?,?,?)}");

            //给in?赋值

            cs.setString(1,"emp");//传表名

            cs.setInt(2, 6);//传入pagesize,每页显示多少条记录

            cs.setInt(3, 1);//传入pagenow,显示第几页。

            //给out?注册

            cs.registerOutParameter(4, oracle.jdbc.OracleTypes.CURSOR);

            //执行

            cs.execute();

            /*这里是关键所在,java没有接收结果集的get方法,所以只能用getObject来接收结果集,接收到后需要使用ResultSet强转才可以。*/

            rs=(ResultSet)cs.getObject(4);

            //循环取出

            while(rs.next()){

                System.out.println(rs.getString("ename")+" "+rs.getString("sal"));

            }

        } catch (Exception e) {

            e.printStackTrace();

        }finally{

            //关闭资源

            try {

                if(cs!=null){

                    cs.close();

                }

                if(rs!=null){

                    rs.close();

                }

                if(ct!=null){

                    ct.close();

                }

            } catch (Exception e) {

                e.printStackTrace();

            }

            cs=null;

            rs=null;

            ct=null;

        }

    }

}

 

要求:请大家编写一个存储过程,要求可以输入表名、每页显示记录数、当前页,排序字段(deptno降序)。返回总记录数,总页数和返回的结果集。

1、创建包同时创建游标

create or replace package pagingPackage is

    type paging_cursor is ref cursor;

end;

 

2、创建分页存储过程

create or replace procedure paging_cursor(v_in_table in varchar2,v_in_pagesize in number,v_in_pagenow in number,v_out_result out pagingPackage.paging_cursor,v_out_rows out number,v_out_pagecount out number) is

--定义需要的变量

v_sql varchar2(4000);

v_sql_select varchar2(4000);

v_start number;

v_end number;

begin

--执行代码

--计算v_start和v_end是多少

v_start:=v_in_pagesize*(v_in_pagenow-1)+1;

v_end:=v_in_pagesize*v_in_pagenow;

v_sql:='select t2.* from (select t1.*,rownum rn from (select * from '||v_in_table||') t1 where rownum<='||v_end||') t2 where rn>='||v_start;

--打开游标,让游标指向结果集

open v_out_result for v_sql;

--查询共有多少条记录

v_sql_select:='select count(*) from '||v_in_table;

execute immediate v_sql_select into v_out_rows;

--统计多少页记录

if mod(v_out_rows,v_in_pagesize)=0 then

        v_out_pagecount:=v_out_rows/v_in_pagesize;

else

        v_out_pagecount:=v_out_rows/v_in_pagesize+1;

end if;

end;

 

java调用分页存储过程(完整)源代码

[TestProcedurePaging.java]源代码

package com.test;

import java.sql.CallableStatement;

import java.sql.Connection;

import java.sql.DriverManager;

import java.sql.ResultSet;

 

public class TestProcedurePaging {

    //调用oracle分页存储过程并获得存储过程的返回结果集。

    public static void main(String[] args) {

        //定义需要的变量

        Connection ct=null;

        CallableStatement cs=null;

        ResultSet rs=null;

        try {

            //加载驱动

            Class.forName("oracle.jdbc.driver.OracleDriver");

            //得到连接

            ct=DriverManager.getConnection("jdbc:oracle:thin:@127.0.0.1:1521:orcl","scott","tiger");

            //创建CallableStatement接口

            cs=ct.prepareCall("{call paging_cursor(?,?,?,?,?,?)}");

            //给in?赋值

            cs.setString(1,"emp");//传表名

            cs.setInt(2, 6);//传入pagesize,每页显示多少条记录

            cs.setInt(3, 1);//传入pagenow,显示第几页。

            //给out?注册

            cs.registerOutParameter(4, oracle.jdbc.OracleTypes.CURSOR);

            cs.registerOutParameter(5, oracle.jdbc.OracleTypes.INTEGER);

            cs.registerOutParameter(6, oracle.jdbc.OracleTypes.INTEGER);

            //执行

            cs.execute();

            /*这里是关键所在,java没有接收结果集的get方法,所以只能用getObject来接收结果集,接收到后需要使用ResultSet强转才可以。*/

            rs=(ResultSet)cs.getObject(4);

            //循环取出

            while(rs.next()){

                System.out.println(rs.getString("ename")+" "+rs.getString("sal"));

            }

            //取出总记录数

            int rowCount=cs.getInt(5);

            //取出总页数

            int pageCount=cs.getInt(6);

            System.out.println("共有记录:"+rowCount+"条!   "+"共有记录:"+pageCount+"页!");

        } catch (Exception e) {

            e.printStackTrace();

        }finally{

            //关闭资源

            try {

                if(cs!=null){

                    cs.close();

                }

                if(rs!=null){

                    rs.close();

                }

                if(ct!=null){

                    ct.close();

                }

            } catch (Exception e) {

                e.printStackTrace();

            }

            cs=null;

            rs=null;

            ct=null;

        }

    }

}

posted @ 2017-10-27 10:56  罗小川的博客  阅读(689)  评论(0编辑  收藏  举报