用了两年Oracle还没写过存储过程,真是十分惭愧,从今天开始学习Oracle存储过程,完全零起点,争取每日一篇学习笔记,可能开始认识的不全面甚至有错误,但坚持下来一定会有收获。
1. 建立一个存储过程
create or replace PROCEDURE firstPro
IS
BEGIN
DBMS_OUTPUT.PUT_LINE('Hello World!');
END;
其中IS关键字替换为AS关键字结果不会出现任何变化,大多认为他们是等同的,但也有一种说法解释为:一般PACKAGE 或者单独的FUNCTION, PROCEDURE 都用AS,PACKAGE 中的FUNCTION, PROCEDURE 用IS。
DBMS_OUTPUT.PUT_LINE('Hello World!'); 是一个输出语句。
2. 执行存储过程
Oracle返回结果需要使用包,那么存储过程似乎只能在数据库中执行或被其他调用,编程语言似乎并不能直接调用存储过程返回数据,是否能执行他有待研究。那么首先在数库中执行上面的存储过程。
BEGIN
FirstPro();//注意有括号
END;
运行后输出Hello World。
3. 下面写一个稍复杂的存储过程,他定义了变量,进行了运算,输出一个count操作所用的时间。
CREATE OR REPLACE procedure testtime
is
n_start number;
n_end number;
samplenum number;
use_time number;
begin
n_start:=dbms_utility.get_time;
select count(*) into samplenum from emp;
n_end:=dbms_utility.get_time;
use_time:= n_end - n_start;
dbms_output.put_line('This statement cost '|| use_time ||' miliseconds');
end;
4. 下面试验下怎么能给存储过程赋值
CREATE OR REPLACE procedure test(num in number) is
begin
dbms_output.put_line('The input numer is:' || num);
end ;
今天的就到这,明天将调用这个存储过程,并试验一写对表的操作。
1. 首先把昨天带参的存储过程执行一下
declare
n number;
begin
n:=1;
test(num=>n);
end;
注;在调用存储过程时,=>前面的变量为存储过程的形参且必须于存储过程中定义的一致,而=>后的参数为实际参数。当然也不可以不定义变量保存实参,可写成如下形式:
Begin
test(num=>1);
end;
这样我们就能更清楚得看到给存储过程赋值的格式了。后面打算用存储过程操作一些表,按照增删改查的顺序依次建立存储过程。
2. 插入
CREATE OR REPLACE
procedure proc_test_Insert_Single(e_no in number,e_name in varchar ,s in varchar,d in varchar)
as
begin
insert into emp (emp_id,emp_name,salary,birthday) values (e_no,e_name,s,d);
end;
调用:
DECLARE
i NUMBER;
n varchar(5);
s varchar(11);
d varchar(10);
BEGIN
i:=10;
n := 'text11';
s:='';
d:='1998-02-02';
PROc_TEST_Insert_single(e_no => i,e_name=>n,s=>s,d=>d);
END;
注:调用存储过程声明varchar时,必须限定长度,即斜体的部分不能少。同时如果给变量赋值时大于限定的长度了,则会提示ORA-06502: PL/SQL: 数字或值错误 : 字符串缓冲区太小。 3. 更新
create or replace procedure proc_test_update_Single(e_no in number,s in varchar)
as
begin
UPDATE emp set salary =s where emp_id=e_no;
end;
调用:
DECLARE
n NUMBER;
s varchar(11);
BEGIN
n := 2;
s:=3998;
PROc_TEST_UPdate_single(e_no => n,s=>s);
END;
4. 号外,今天在开发过程中正好有个数据库更新操作可用存储过程实现,顺便练习一下,需求是将一个表中的ID字段,查出来更新到另一个表中,两个表通过b_bs和b_kgh关联。存储过程如下:
create or replace procedure update_yygzdbid
as
bs varchar(20);
kgh varchar(20);
bid number;
cursor c_db is select b_id,b_bs,b_kgh from pmdcdb;
begin
for temp in c_db loop
update yygz_db set b_id= temp.b_id where g_bs=temp.b_bs and g_bh=temp.b_kgh;
end loop;
end;
运行这个存储过程:
Begin
update_yygzdbid();
end;
说明:
(1).在没有参数的存储过程定义时存储过程的名称不需要括号,写成update_yygzdbid()是错误的,
(2). cursor c_db是定义一个游标,获得查询语句的结果集,
(3). For temp in c_bd loop
Begin
End;
End loop
是循环游标,其形式类似于C#中的foreach, 获得字段:temp.b_id。
5. 查询
最后我们做一个查询的存储过程,能够返回一个值,注意不是结果集,结果集是明天的目标。
CREATE OR REPLACE
procedure proc_test_Select_Single(t in varchar,r out varchar )
as
begin
select salary into r from emp where emp_name=t;
end;
这个存储过程使用了2个参数,并分别出现了IN和OUT,in代表输入,out用于输出,从下面的语句也可以看到salary写入到变量r中了,这个r我们可以在调用存储过程后得到。
这时编译后会出现一个Warning(1,48): PLW-07203: 使用 NOCOPY 编译器提示可能对参数 'R' 有所帮助,那么nocopy是什么呢,nocopy主要是针对in|out record/index-by table/varray/varchar2提高效率使用的, 对于number使用nocopy与否基本没有影响.所以在'enable:performance'情况下不会对number提示warning.
我们把第一行改为:procedure proc_test_Select_Single(t in varchar,r out nocopy varchar )
现在即使对in的varchar没有使用nocopy也不会提示警告,
DECLARE
T varchar2(4);
R VARCHAR2(4);
BEGIN
T := 'zz';
PROC_TEST_SELECT_SINGLE(T => T,R => R );
DBMS_OUTPUT.PUT_LINE('R = ' || R);
END;
运行后即可在输出中看到结果了。
三、
1. 今天我们首先写一个涨工资的存储过程,给每个低于5k工资的人涨点钱。
CREATE OR REPLACE PROCEDURE p_test(forRaise in number)
as
begin
for v_emp in (select * from emp) loop
if(v_emp.salary<'') then
update emp set salary =(v_emp.salary+forRaise) where emp_id=v_emp.emp_id;
end if;
end loop;
end;
调用:
DECLARE
FORRAISE NUMBER;
BEGIN
FORRAISE :=1;
P_TEST(FORRAISE => FORRAISE);
END;
这里要注意两个地方:
(1) 循环中begin和end不是必须的
(2) 这里增加了if语句,其格式比较简单就不细说了。
(3) 这里没有定义游标,在游标的位置直接用select语句代替了。
2. 这里顺便介绍下另外一种循环,while循环,实现同样的功能
CREATE OR REPLACE PROCEDURE p_test(forRaise in number)
as
cursor c is select * from emp;
v_row emp%rowtype;
begin
open c;
fetch c into v_row;
while c%found Loop
if(v_row.salary<'') then
update emp set salary =(v_row.salary+forRaise) where emp_id=v_row.emp_id;
end if;
fetch c into v_row;
end loop;
close c;
end;
说明:
(1) 这里需要定义一个游标,还要定义一个emp%rowtype类型的变量,%前面是表名,后面表示这个表的一行,
(2) 在使用游标前还要显示的打开游标,并将其赋值到row中,使用后关闭游标。
(3) C%found表示只有row中有值的时候才会进行循环。
(4) 经过对比发现于while循环相比,for循环更像是C#中的foreach,使用起来方便很多。
(5) 另从9i开始提供的动态游标类型sys_refcursor,以前的版本必须要先创建一个ref cursor的类型,现在多个
3. 现在我们使用程序调用下涨工资的存储过程,这个存储过程是没有返回值的。
OracleConnection conn = new OracleConnection(); //创建一个新连接
conn.ConnectionString = "Data Source='ds';user id='id ';password='pwd';"; OracleCommand cmd = new OracleCommand("P_TEST", conn);
cmd.CommandType = CommandType.StoredProcedure;
OracleParameter p1 = new OracleParameter("forRaise", OracleType.UInt32);
p1.Value = 1;
p1.Direction = System.Data.ParameterDirection.Input;
cmd.Parameters.Add(p1);
conn.Open();
int r=cmd.ExecuteNonQuery();
conn.Close();
这样我们就可以给员工涨工资了,说明:
(1) 虽然给多个人涨了公司,但r的值是1,他只调用了1个存储过程,或者说受影响的只是1个。
(2) 参数P1的名字必须和存储过程中的一样否则会提示:调用 'P_TEST' 时参数个数或类型错误。
4. 现在我们试着从存储过程中得到点结果吧,我先看看我给几个人涨了工资,我每个月一共要多付多少钱了。
改动存储过程:
CREATE OR REPLACE PROCEDURE p_test(forRaise in number,res out number)
is
begin
res:=0;
for v_emp in (select * from emp) loop
if(v_emp.salary<'') then
update emp set salary =(v_emp.salary+forRaise) where emp_id=v_emp.emp_id;
res:=res+1;
end if;
end loop;
end;
增加了一个out 的number型,记录改动的次数。然后相应的调整C#程序,获得这个改动的次数。
OracleCommand cmd = new OracleCommand("P_TEST", conn);
cmd.CommandType = CommandType.StoredProcedure;
OracleParameter p1 = new OracleParameter("forRaise", OracleType.UInt32);
p1.Value = 4;
p1.Direction = System.Data.ParameterDirection.Input;
OracleParameter p2 = new OracleParameter("res", OracleType.UInt32);
p2.Value = 10;
p2.Direction = System.Data.ParameterDirection.Output;
cmd.Parameters.Add(p1);
cmd.Parameters.Add(p2);
conn.Open();
int r=cmd.ExecuteNonQuery();
conn.Close();
MessageBox.Show(“你已经给:”+p2.Value.ToString()+“人涨了工资”);
好了,今天就到这,下次返回数据集。
Oracle使用存储过程返回结果集必须使用包,包包括包头和包体两部分,包头是定义部分包体是具体的实现
包头:
CREATE OR REPLACE
PACKAGE pkg_test_select_mul
AS
TYPE myrctype IS REF CURSOR;
PROCEDURE proc(s number, res OUT myrctype);
END pkg_test_select_mul;
这里定义了个一个游标和一个存储过程。
包体:
CREATE OR REPLACE
PACKAGE BODY "PKG_TEST_SELECT_MUL" AS
PROCEDURE proc(s in number,res OUT myrctype)
IS
BEGIN
OPEN res FOR Select emp_id,emp_Name, salary,birthday From emp where salary> s;
END proc;
END PKG_TEST_SELECT_MUL;
这里实现里包头中定义的存储过程,实现了查询工资超过一定数额的人的信息,而游标则不用重新定义了,且存储过程中的参数名必须和定义中的一致。下面我们看一下C#的调用部分。
OracleConnection conn = new OracleConnection(); //创建一个新连接
conn.ConnectionString = "Data Source='" + "MyTest" + "';user id='" + "azkaser" + "';password='" + "sti" + "';"; //写连接串
OracleCommand cmd = new OracleCommand("PKG_TEST_SELECT_MUL.proc", conn);
cmd.CommandType = CommandType.StoredProcedure;
OracleParameter p1 = new OracleParameter("s", OracleType.Number);
p1.Value = 4000;
p1.Direction = ParameterDirection.Input;
OracleParameter p2 = new OracleParameter("res", OracleType.Cursor);
p2.Direction = ParameterDirection.Output; cmd.Parameters.Add(p1);
cmd.Parameters.Add(p2);
conn.Open();
OracleDataReader myReader = cmd.ExecuteReader();
while (myReader.Read())
{
MessageBox.Show(myReader.GetString(1));
}
conn.Close();
程序将得到的结果存放在OracleDataReader的对象中。
存储过程
  1 CREATE OR REPLACE PROCEDURE 存储过程名   2 IS   3 BEGIN   4 NULL;   5 END; 行1:   CREATE OR REPLACE PROCEDURE 是一个SQL语句通知Oracle数据库去创建一个叫做skeleton存储过程, 如果存在就覆盖它; 行2:   IS关键词表明后面将跟随一个PL/SQL体。 行3:   BEGIN关键词表明PL/SQL体的开始。 行4:   NULL PL/SQL语句表明什么事都不做,这句不能删去,因为PL/SQL体中至少需要有一句; 行5:   END关键词表明PL/SQL体的结束 存储过程创建语法:
create or replace procedure 存储过程名(param1 in type,param2 out type) as 变量1 类型(值范围); --vs_msg VARCHAR2(4000); 变量2 类型(值范围); Begin Select count(*) into 变量1 from 表A where列名=param1; If (判断条件) then Select 列名 into 变量2 from 表A where列名=param1; Dbms_output。Put_line(‘打印信息’); Elsif (判断条件) then Dbms_output。Put_line(‘打印信息’); Else Raise 异常名(NO_DATA_FOUND); End if; Exception When others then Rollback; End; 注意事项: 1, 存储过程参数不带取值范围,in表示传入,out表示输出 类型可以使用任意Oracle中的合法类型。 2, 变量带取值范围,后面接分号 3, 在判断语句前最好先用count(*)函数判断是否存在该条操作记录 4, 用select 。。。into。。。给变量赋值 5, 在代码中抛异常用 raise+异常名 CREATE OR REPLACE PROCEDURE存储过程名
( --定义参数
is_ym IN CHAR(6) , the_count OUT NUMBER,
)
AS
--定义变量
vs_msg VARCHAR2(4000); --错误信息变量
vs_ym_beg CHAR(6); --起始月份
vs_ym_end CHAR(6); --终止月份
vs_ym_sn_beg CHAR(6); --同期起始月份
vs_ym_sn_end CHAR(6); --同期终止月份 --定义游标(简单的说就是一个可以遍历的结果集)

 
 简要记录存储过程语法与Java程序的调用方式

  一 存储过程

    首先,我们建立一个简单的表进行存储过程的测试

create table
xuesheng(id integer, xing_ming varchar2(25), yu_wen number, shu_xue number); insert into xuesheng values(1,'zhangsan',80,90)
insert into xuesheng values(2,'lisi',85,87)
1)无返回值的存储过程 create or replace procedure xs_proc_no is
begin
insert into xuesheng values (3, 'wangwu', 90, 90);
commit;
end xs_proc_no;
2)有单个数据值返回的存储过程 复制代码
create or replace procedure xs_proc(temp_name in varchar2,
temp_num out number) is
num_1 number;
num_2 number;
begin
select yu_wen, shu_xue
into num_1, num_2
from xuesheng
where xing_ming = temp_name;
--dbms_output.put_line(num_1 + num_2);
temp_num := num_1 + num_2;
end;
复制代码
其中,以上两种与sql server基本类似,而对于返回数据集时,上述方法则不能满足我们的要求。在Oracle中,一般使用ref cursor来返回数据集。示例代码如下: 3)有返回值的存储过程(列表返回) 首先,建立我们自己的包。并定义包中的一个自定义ref cursor create or replace package mypackage as
type my_cursor is ref cursor;
end mypackage;
在定义了ref cursor后,可以书写我们的程序代码 create or replace procedure xs_proc_list(shuxue in number,
p_cursor out mypackage.my_cursor) is
begin
open p_cursor for
select * from xuesheng where shu_xue > shuxue;
end xs_proc_list;
二、程序调用 在本节中,我们使用java语言调用存储过程。其中,关键是使用CallableStatement这个对象,代码如下: String oracleDriverName = "oracle.jdbc.driver.OracleDriver"; // 以下使用的Test就是Oracle里的表空间
String oracleUrlToConnect = "jdbc:oracle:thin:@127.0.0.1:1521:orcl";
Connection myConnection = null;
try {
Class.forName(oracleDriverName);
} catch (ClassNotFoundException ex) {
ex.printStackTrace();
}
try {
myConnection = DriverManager.getConnection(oracleUrlToConnect,
"xxxx", "xxxx");//此处为数据库用户名与密码 } catch (Exception ex) {
ex.printStackTrace();
}
try { CallableStatement proc=null;
proc=myConnection.prepareCall("{call xs_proc(?,?)}");
proc.setString(1, "zhangsan");
proc.registerOutParameter(2, Types.NUMERIC);
proc.execute();
String teststring=proc.getString(2);
System.out.println(teststring); } catch (Exception ex) {
ex.printStackTrace();
}
对于列表返回值的存储过程,在上述代码中做简单修改。如下 复制代码
CallableStatement proc=null;
proc=myConnection.prepareCall("{call getdcsj(?,?,?,?,?)}");
proc.setString(1, strDate);
proc.setString(2, jzbh);
proc.registerOutParameter(3, Types.NUMERIC);
proc.registerOutParameter(4, OracleTypes.CURSOR);
proc.registerOutParameter(5, OracleTypes.CURSOR);
proc.execute();
ResultSet rs=null;
int total_number=proc.getInt(3);
rs=(ResultSet)proc.getObject(4);
复制代码
上述存储过程修改完毕。另外,一个复杂的工程项目中的例子:查询一段数据中间隔不超过十分钟且连续超过100条的数据。即上述代码所调用的getdcsj存储过程

转载地址:http://www.cnblogs.com/liliu/archive/2011/06/22/2087546.html

转载地址:http://blog.sina.com.cn/s/blog_82faefb00100tn6o.html

oracle中的存储过程例子的更多相关文章

  1. Oracle中执行存储过程call和exec区别

    Oracle中执行存储过程call和exec区别 在sqlplus中这两种方法都可以使用: exec pro_name(参数1..); call pro_name(参数1..); 区别: 1. 但是e ...

  2. oracle 中的存储过程

      oracle 中的存储过程 --oracle 中的存储过程, --不带任何参数的 CREATE OR REPLACE PROCEDURE PRO_TEST AS -- AS 和is 没有任何区别 ...

  3. 用sql语句导出oracle中的存储过程和函数

    用sql语句导出oracle中的存储过程和函数: SET echo off ; SET heading off ; SET feedback off ; SPOOL 'C:/PRC.SQL' repl ...

  4. oracle中print_table存储过程实例介绍

    oracle中pro_print_table存储过程实例介绍 存储过程(Stored Procedure),就是一组用于完成特定数据库功能的SQL语句集,该SQL语句集经过编译后存储在数据库系统中.这 ...

  5. Oracle中的存储过程简单例子

    --创建表create table TESTTABLE(  id1  VARCHAR2(12),  name VARCHAR2(32))select t.id1,t.name from TESTTAB ...

  6. Mybatis调用Oracle中的存储过程和function

    一.Mybatis调用存储过程 1 在数据库中创建以下的存储过程create or replace procedure pro_hello(p_user_name in varchar2,p_resu ...

  7. oracle中的存储过程(实例一)

    引子 这是测试环境存在了很久的问题.由于基础配置信息(如:代理人信息)不像生产环境有专人维护,常常会有数据过期,导致无法使用的情况. 而很多配置数据是在外围系统维护(如代理人信息,在销管系统)以往的解 ...

  8. oracle中print_table存储过程介绍

    一直以来,觉得MySQL中使用\G参数改变输出结果集的显示方式非常好用,尤其是在命令行界面.但是ORACLE数据库没有这个功能,今天在搜索到Tom大师的一篇博文时,发现大师用一个存储过程print_t ...

  9. oracle中PLSQL存储过程中如何使用逗号分隔的集合(逗号分隔字符串转换为一个集合)

    原文: https://blogs.oracle.com/aramamoo/entry/how_to_split_comma_separated_string_and_pass_to_in_claus ...

随机推荐

  1. 2.1.4 扫描器X-Scan查本机隐患

    X-Scan是由安全焦点开发的一个功能强大的扫描工具.它采用多线程方式对指定IP地址段(或单机)进行安全漏洞检测,支持插件功能. 1.用X-Scan查看本机IP地址 利用X-Scan扫描器来查看本机的 ...

  2. 【原创】一些常用的Vi命令,可帮助脱离鼠标

    使用Vi编写代码时,如果想脱离鼠标,需要使用一些命令快捷键,下面罗列了一些常用的并且容易记住的: 1. 命令模式下,移动光标或跳转 0到行首 ^到行首第一个非空字符 $到行尾非空字符 fx向后移动光标 ...

  3. css+div网页设计(三)--与多种技术的混合应用

    本篇博客将介绍css与多种技术的混合应用,javascript可以为我们的页面更加方便的交互,xml使数据存储跟方便,ajax的异步更新可以加快我们网页的载入速度. 1.css与javascript ...

  4. 100天成就卓越领导力:新晋领导者的First100训练法

    <100天成就卓越领导力:新晋领导者的First100训练法> 基本信息 原书名:Your Frist 100 days: How to Make Maximum Impact in Yo ...

  5. 10 个顶级 JavaScript 动画框架推荐

    使用JavaScript可以做出一些引人注目的动画效果,但通常不太容易实现.本文为你整理了10个非常优秀的JavaScript动画框架,使用它们你可以轻松实现动画效果.1. RaphaëlRaphaë ...

  6. 启动 Eclipse 弹出“Failed to load the JNI shared library jvm.dll”错误的解决方法!&&在eclipse.ini中为eclipse指定jdk启动

    参考:http://blog.csdn.net/zyz511919766/article/details/7442633 http://blog.sina.com.cn/s/blog_028f0c1c ...

  7. 发布后500访问错误 —— dll引用错误

    System.Net.Http相关dll

  8. Every student in every school should have the opportunity to learn to code

    “I think everybody in this country should learn how to program a computerbecause it teaches you how ...

  9. windows下,python+scrapy环境搭建

    •安装lxml(官网给出的地址http://www.lfd.uci.edu/~gohlke/pythonlibs/#lxml,下载whl文件安装) •安装zope.interface https:// ...

  10. (js有关图片加载问题)dom加载完和onload事件

    引用旺旺的话...哈哈哈DOMContentLoaded事件表示页面的DOM结构绘制完成了,这时候外部资源(带src属性的)还没有加载完.而onload事件是等外部资源都加载完了就触发的.$.read ...