Oracle存儲過程和自定義函數(shù)詳解
概述
PL/SQL中的過程和函數(shù)(通常稱為子程序)是PL/SQL塊的一種特殊的類型,這種類型的子程序可以以編譯的形式存放在數(shù)據(jù)庫中,并為后續(xù)的程序塊調用。
相同點: 完成特定功能的程序
不同點:是否用return語句返回值。
舉個例子:
create or replace procedure PrintStudents(p_staffName in xgj_test.username%type) as cursor c_testData is select t.sal, t.comm from xgj_test t where t.username = p_staffName; begin for v_info in c_testData loop DBMS_OUTPUT.PUT_LINE(v_info.sal || ' ' || v_info.comm); end loop; end PrintStudents;
一旦創(chuàng)建了改程序并將其存儲在數(shù)據(jù)庫中,就可以使用如下的方式調用該過程
begin
PrintStudents('Computer Science');
PrintStudents('Match');
end;
/
或者
exec PrintStudents('Computer Science');
exec PrintStudents('Match');
在命令窗口中:

在pl/sql工具的sql窗口中: 
存儲過程的創(chuàng)建和調用
基本語法
create [ or replace] procedure procedure_name
[( argument [ {IN | OUT | IN OUT }] type,
......
argument [ {IN | OUT | IN OUT }] type ) ] { IS | AS}
procedure_body
無參的存儲過程
/**
無參數(shù)的存過
打印hello world
調用存儲過程:
1. exec sayhelloworld();
2 begin
sayhelloworld();
end;
/
*/
create or replace procedure sayhelloworld
as
--說明部分
begin
dbms_output.put_line('hello world');
end sayhelloworld;
調用過程:
SQL> set serveroutput on ; SQL> exec sayhelloworld(); hello world PL/SQL procedure successfully completed SQL> begin 2 sayhelloworld(); 3 sayhelloworld(); 4 end; 5 / hello world hello world PL/SQL procedure successfully completed
帶參數(shù)的存儲過程
/**
創(chuàng)建一個帶參數(shù)的存儲過程
給指定的員工增加工資,并打印增長前后的工資
*/
create or replace procedure addSalary(staffName in xgj_test.username%type )
as
--定義一個變量保存調整之前的薪水
oldSalary xgj_test.sal%type;
begin
--查詢員工漲之前的薪水
select t.sal into oldSalary from xgj_test t where t.username=staffName;
--調整薪水
update xgj_test t set t.sal = sal+1000 where t.username=staffName ;
--輸出
dbms_output.put_line('調整之前的薪水:'|| oldSalary || ' ,調整之后的薪水:' || (oldSalary + 1000));
end addSalary;
可以看到,update語句之后并沒有commit的操作。
一般來講為了保證事務的一致性,由調用者來提交比較合適,當然了是需要區(qū)分具體的業(yè)務需求的~
begin
addSalary('xiao');
addSalary('gong');
commit ;
end ;
/
存儲函數(shù)
基本語法
create [ or replace] function function_name
[( argument [ {IN | OUT | IN OUT }] type,
......
argument [ {IN | OUT | IN OUT }] type ) ]
RETURN { IS | AS}
function_body
其中 return子句是必須存在的,一個函數(shù)如果沒有執(zhí)行return就結束將發(fā)生錯誤,這一點和存過有說不同。
存儲函數(shù)
準備的數(shù)據(jù)如下:

/** 查詢員工的年薪 (月工資*12 + 獎金) */ create or replace function querySalaryInCome(staffName in varchar2) return number as --定義變量保存員工的工資和獎金 pSalary xgj_test.sal%type; pComm xgj_test.comm%type; begin --查詢員工的工資和獎金 select t.sal, t.comm into pSalary, pComm from xgj_test t where t.username = staffName; --直接返回年薪 return pSalary * 12 + pComm; end querySalaryInCome;

存在一個問題,當獎金為空的時候,算出來的年收入竟然是空的。
因為 如果一個表達式中有空值,那么這個表達式的結果即為空值。
所以我們需要對空值進行處理, 使用nvl函數(shù)即可。
最后修改后的function為
create or replace function querySalaryInCome(staffName in varchar2) return number as --定義變量保存員工的工資和獎金 pSalary xgj_test.sal%type; pComm xgj_test.comm%type; begin --查詢員工的工資和獎金 select t.sal, t.comm into pSalary, pComm from xgj_test t where t.username = staffName; --直接返回年薪 return pSalary * 12 + nvl(pComm,0); end querySalaryInCome;
out參數(shù)
一般來講,存儲過程和存儲函數(shù)的區(qū)別在于存儲函數(shù)可以有一個返回值,而存儲過程沒有返回值。
- 存儲過程和存儲函數(shù)都可以有out參數(shù)
- 存儲過程和存儲函數(shù)都可以有多個out參數(shù)
- 存儲過程可以通過out參數(shù)實現(xiàn)返回值
那我們如何選擇存儲過程和存儲函數(shù)呢?
原則:
如果只有一個返回值,用存儲函數(shù),否則(即沒有返回值或者有多個返回值)使用存儲過程。
/**
根據(jù)員工姓名,查詢員工的全部信息
*/
create or replace procedure QueryStaffInfo(staffName in xgj_test.username%type,
pSal out number,
pComm out xgj_test.comm%type,
pJob out xgj_test.job%type)
is
begin
--查詢該員工的薪資,獎金和職位
select t.sal,t.comm,t.job into pSal,pComm,pJob from xgj_test t where t.username=staffName;
end QueryStaffInfo;

先拋出兩個思考問題:
- 查詢員工的所有信息–> out參數(shù)太多怎么辦?
- 查詢某個部門中所有員工的信息–> out中返回集合?
后面會講到如何解決? 總不能一個個的寫out吧~
在應用中訪問存儲過程和存儲函數(shù)
概述
我們使用Java程序連接Oracle數(shù)據(jù)庫。
使用jar: ojdbc14.jar
關于oracle官方提供的幾個jar的區(qū)別
- classes12.jar (1,600,090 bytes) - for use with JDK 1.2 and JDK 1.3
- classes12_g.jar (2,044,594 bytes) - same as classes12.jar, except that classes were compiled with “javac -g” and contain some tracing information.
- classes12dms.jar (1,607,745 bytes) - same as classes12.jar, except that it contains additional code`to support Oracle Dynamic Monitoring Service.
- classes12dms_g.jar (2,052,968 bytes) - same as classes12dms.jar except that classes were compiled with “javac -g” and contain some tracing information.
- ojdbc14.jar (1,545,954 bytes) - classes for use with JDK 1.4 and 1.5
- ojdbc14_g.jar (1,938,906 bytes) - same as ojdbc14.jar, except that classes were compiled with “javac -g” and contain some tracing information.
- ojdbc14dms.jar (1,553,561 bytes) - same as ojdbc14.jar, except that it contains additional code`to support Oracle Dynamic Monitoring Service.
- ojdbc14dms_g.jar (1,947,136 bytes) - same as ojdbc14dms.jar, except that classes were compiled with “javac -g” and contain some tracing information.
工程目錄如下:

簡單的寫下獲取數(shù)據(jù)庫連接的工具類
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class DBUtils {
// 設定數(shù)據(jù)庫驅動,數(shù)據(jù)庫連接地址端口名稱,用戶名,密碼
private static final String driver = "oracle.jdbc.driver.OracleDriver";
private static final String url = "jdbc:oracle:thin:@ip:xxxx";
private static final String username = "xxxx";
private static final String password = "xxxx";
/**
* 注冊數(shù)據(jù)庫驅動
*/
static {
try {
Class.forName(driver);
} catch (ClassNotFoundException e) {
throw new ExceptionInInitializerError(e.getMessage());
}
}
/**
* 獲取數(shù)據(jù)庫連接
*/
public static Connection getConnection() {
try {
Connection connection = DriverManager.getConnection(url, username, password);
// 成功,返回connection
return connection;
} catch (SQLException e) {
e.printStackTrace();
}
// 獲取失敗,返回null
return null;
}
/**
* 釋放連接
*/
public static void cleanup(Connection conn, Statement st, ResultSet rs) {
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
} finally {
rs = null;
}
}
if (st != null) {
try {
st.close();
} catch (SQLException e) {
e.printStackTrace();
} finally {
st = null;
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
} finally {
conn = null;
}
}
}
}
在應用程序中訪問存儲過程
根據(jù)官方提供的API,我們可以看到:
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.SQLException;
import org.junit.Test;
import com.turing.oracle.dbutil.DBUtils;
import oracle.jdbc.OracleTypes;
public class TestProcedure {
@Test
public void callProcedure(){
// {call <procedure-name>[(<arg1>,<arg2>, ...)]}
Connection conn = null ;
CallableStatement callableStatement = null ;
/**
*
根據(jù)員工姓名,查詢員工的全部信息
create or replace procedure QueryStaffInfo(staffName in xgj_test.username%type,
pSal out number,
pComm out xgj_test.comm%type,
pJob out xgj_test.job%type)
is
begin
--查詢該員工的薪資,獎金和職位
select t.sal,t.comm,t.job into pSal,pComm,pJob from xgj_test t where t.username=staffName;
end QueryStaffInfo;
*/
// 我們可以看到該存過 4個參數(shù) 1個入?yún)?3個出參
String sql = "{call QueryStaffInfo(?,?,?,?)}";
try {
// 獲取連接
conn = DBUtils.getConnection();
// 通過連接獲取到CallableStatement
callableStatement = conn.prepareCall(sql);
// 對于in 參數(shù),需要賦值
callableStatement.setString(1, "xiao");
// 對于out 參數(shù),需要聲明
callableStatement.registerOutParameter(2, OracleTypes.NUMBER); // 第二個 ?
callableStatement.registerOutParameter(3, OracleTypes.NUMBER);// 第三個 ?
callableStatement.registerOutParameter(4, OracleTypes.VARCHAR);// 第四個 ?
// 執(zhí)行調用
callableStatement.execute();
// 取出結果
int salary = callableStatement.getInt(2);
int comm = callableStatement.getInt(3);
String job = callableStatement.getString(3);
System.out.println(salary + "\t" + comm + "\t" + job);
} catch (SQLException e) {
e.printStackTrace();
}finally {
DBUtils.cleanup(conn, callableStatement, null);
}
}
}
在應用程序中訪問存儲函數(shù)
根據(jù)官方提供的API,我們可以看到:
import java.sql.CallableStatement;
import java.sql.Connection;
import org.junit.Test;
import com.turing.oracle.dbutil.DBUtils;
import oracle.jdbc.OracleTypes;
public class TestFuction {
@Test
public void callFuction(){
//{?= call <procedure-name>[(<arg1>,<arg2>, ...)]}
Connection conn = null;
CallableStatement call = null;
/**
* create or replace function querySalaryInCome(staffName in varchar2)
return number as
--定義變量保存員工的工資和獎金
pSalary xgj_test.sal%type;
pComm xgj_test.comm%type;
begin
--查詢員工的工資和獎金
select t.sal, t.comm
into pSalary, pComm
from xgj_test t
where t.username = staffName;
--直接返回年薪
return pSalary * 12 + nvl(pComm,0);
end querySalaryInCome;
*/
String sql = "{?=call querySalaryInCome(?)}";
try {
// 獲取連接
conn = DBUtils.getConnection();
// 通過conn獲取CallableStatement
call = conn.prepareCall(sql);
// out 參數(shù),需要聲明
call.registerOutParameter(1, OracleTypes.NUMBER);
// in 參數(shù),需要賦值
call.setString(2, "gong");
// 執(zhí)行
call.execute();
// 取出返回值 第一個?的值
double income = call.getDouble(1);
System.out.println("該員工的年收入:" + income);
} catch (Exception e) {
e.printStackTrace();
}finally {
DBUtils.cleanup(conn, call, null);
}
}
}
在out參數(shù)中訪問光標
在out參數(shù)中使用光標
我們之前拋出的兩個思考問題:
- 查詢員工的所有信息–> out參數(shù)太多怎么辦?
- 查詢某個部門中所有員工的信息–> out中返回集合?
我們可以通過返回Cursor的方式來實現(xiàn)。
在out參數(shù)中使用光標 的步驟:
- 申明包結構
- 包頭
- 包體
包頭:
create or replace package MyPackage is
-- Author : ADMINISTRATOR
-- Created : 2016-6-4 18:10:42
-- Purpose :
-- 使用type關鍵字 is ref cursor說明是cursor類型
type staffCursor is ref cursor;
procedure queryStaffJob(pJob in xgj_test.job%type,
jobStaffList out staffCursor);
end MyPackage;
創(chuàng)建完包頭之后,創(chuàng)建包體,包體需要實現(xiàn)包頭中聲明的所有方法。
包體
create or replace package body MyPackage is
procedure queryStaffJob(pJob in xgj_test.job%type,
jobStaffList out staffCursor)
as
begin
open jobStaffList for select * from xgj_test t where t.job=pJob;
end queryStaffJob;
end MyPackage;
事實上,通過plsql工具創(chuàng)建包頭,編譯后,包體的框架就會自動的生成了。
在應用程序中訪問包下的存儲過程
在應用程序中訪問包下的存儲過程
在應用程序中訪問包下的存儲過程 ,需要帶包名
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.ResultSet;
import org.junit.Test;
import com.turing.oracle.dbutil.DBUtils;
import oracle.jdbc.OracleTypes;
import oracle.jdbc.driver.OracleCallableStatement;
public class TestCursor {
@Test
public void testCursor(){
/**
*
* create or replace package MyPackage is
type staffCursor is ref cursor;
procedure queryStaffJob(pJob in xgj_test.job%type,
jobStaffList out staffCursor);
end MyPackage;
*/
String sql = "{call MyPackage.queryStaffJob(?,?)}" ;
Connection conn = null;
CallableStatement call = null ;
ResultSet rs = null;
try {
// 獲取數(shù)據(jù)庫連接
conn = DBUtils.getConnection();
// 通過conn創(chuàng)建CallableStatemet
call = conn.prepareCall(sql);
// in 參數(shù) 需要賦值
call.setString(1, "Staff");
// out 參數(shù)需要聲明
call.registerOutParameter(2, OracleTypes.CURSOR);
// 執(zhí)行調用
call.execute();
// 獲取返回值
rs = ((OracleCallableStatement)call).getCursor(2);
while(rs.next()){
// 取出值
String username = rs.getString("username");
double sal = rs.getDouble("sal");
double comm = rs.getDouble("comm");
System.out.println("username:" + username + "\t sal:" + sal + "\t comm:" + comm);
}
} catch (Exception e) {
e.printStackTrace();
}finally {
DBUtils.cleanup(conn, call, rs);
}
}
}
原文鏈接:http://blog.csdn.net/yangshangwei/article/details/51581952
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。
相關文章
Oracle ORA 07445 evaopn2()+128錯誤問題的解決方案
這篇文章主要介紹了Oracle ORA 07445 evaopn2()+128錯誤問題的解決方案,非常不錯,具有參考借鑒價值,需要的朋友可以參考下2016-09-09
如何把Oracle?數(shù)據(jù)庫從?RAC?集群遷移到單機環(huán)境
這篇文章主要介紹了把?Oracle?數(shù)據(jù)庫從?RAC?集群遷移到單機環(huán)境,內容包括系統(tǒng)環(huán)境搭建,源數(shù)據(jù)庫的操作及目標數(shù)據(jù)庫的操作,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2022-08-08
centos 6.5下安裝oracle 11gR2與Oracle自動啟動的配置
CentOS 下安裝 Oracle 是一件比較麻煩的事情,下面這篇文章主要介紹了在 centos 6.5下安裝oracle 11gR2的前的配置步驟,以及安裝完成后,如何設置為隨系統(tǒng)自動啟動。配置完成后,啟動圖形化安裝,沒有什么可說的,本文就沒有一一截圖。需要的朋友可以參考借鑒。2017-01-01
oracle插入字符串數(shù)據(jù)時字符串中有''單引號問題
這篇文章主要介紹了oracle插入字符串數(shù)據(jù)時字符串中有'單引號問題的相關資料,需要的朋友可以參考下2017-04-04
Oracle收購TimesTen 提高數(shù)據(jù)庫軟件性能
Oracle收購TimesTen 提高數(shù)據(jù)庫軟件性能...2007-03-03

