一文詳解Oracle存儲過程
簡介
Oracle 存儲過程是 Oracle 數(shù)據(jù)庫中的一種數(shù)據(jù)處理對象,它可以在數(shù)據(jù)庫中定義一組預定義的 SQL 語句,用于完成特定的數(shù)據(jù)庫操作。存儲過程可以被授權的用戶調用,并且可以執(zhí)行多個語句,這些語句可以被視為一個單獨的操作,也可以被視為一系列的操作。
使用存儲過程可以大大提高數(shù)據(jù)庫的性能和安全性。存儲過程可以減少網(wǎng)絡流量和請求,同時也可以減少與應用程序之間的接口調用,從而提高了數(shù)據(jù)庫的性能和可靠性。
1、基本語法
1.1 新建測試窗口
- New Test Window

1.2 程序結構
- 在Java編程中是區(qū)分大小寫,這里不用區(qū)分大小寫。
- DECLARE部分聲明變量或游標(結果集類型變量),程序沒有變量聲明的可以省略或刪除。
- PLSQL可分為三個部分: 變量聲明部分,執(zhí)行部分,異常處理部分。
-- Created on 2023/5/10 by 肖 declare --聲明變量 游標 begin -- 執(zhí)行語句 -- 異常處理 end;
1.3打印輸出
- Dbms_Output 為oracle內置程序包,類似Java中的System.out,而put_line() 是調用的方法,相當于println()方法。
- 需要注意的是: put_line('hello mr.xiao'); 中 一定是 ' ' 號,否則會報錯的。
begin
--打印 hello mr.xiao
Dbms_Output.put_line('hello mr.xiao');
end;執(zhí)行結果
- 如果你不能打印輸出,需要開啟 set serveroutput on 因為 默認情況下,輸出選項是關閉狀態(tài)。

1.4 變量
變量分兩大類如:
- 普通數(shù)據(jù)類型(char,varchar2, date, number, boolean, long)
- 特殊變量類型(引用型變量、記錄型變量)
聲明變量的方式如:
- 變量名 變量類型(變量長度) 例如: v_name varchar2(30);
1.4.1 普通變量
變量賦值的方式有兩種如:
- 直接賦值語句 := 比如: v_name := '你才是臭弟弟'
- 語句賦值,使用select …into … 賦值:(語法 select 值 into 變量)
-- 打印個人信息,包括: 姓名、薪水、地址
DECLARE
-- 姓名
V_NAME VARCHAR2(30) := '你才是臭弟弟'; -- 聲明變量直接賦值
--薪水
V_SAL NUMBER;
--地址
V_ADDR VARCHAR2(200);
BEGIN
--在程序中直接賦值
V_SAL := 1800; --工資每月1800 每天笑哈哈
--語句賦值
SELECT 'CSDN你才是臭弟弟' INTO V_ADDR FROM DUAL; --不會有人不知道DUAL吧,DUAL 是一個用于描述 Oracle 數(shù)據(jù)庫中的虛擬表的關鍵字
--打印變量 注意 || 是拼接
DBMS_OUTPUT.PUT_LINE('姓名:' || V_NAME || ',薪水:' || V_SAL || ',地址:' ||V_ADDR);
END;執(zhí)行結果:

1.4.2 引用型變量
- 變量的類型和長度取決于表中字段的類型和長度
- 通過 表名.列名%TYPE 指定變量的類型和長度,例如: v_name emp.ename%TYPE
-- 查詢emp表中1001號員工的個人信息,打印姓名和薪水
DECLARE
-- 姓名
V_NAME EMP.ENAME%TYPE; -- 聲明變量直接賦值
--薪水
V_SAL EMP.ESALARY%TYPE;
BEGIN
--查詢表中的姓名和薪水并賦值給變量
--注意查詢的字段和賦值的變量的順序、個數(shù)、類型要一致
SELECT ENAME, ESALARY INTO V_NAME, V_SAL FROM EMP WHERE EMPLOYEEID = 1001;
--打印變量
DBMS_OUTPUT.PUT_LINE('姓名:' || V_NAME || ',薪水:' || V_SAL);
END;執(zhí)行結果:

推薦大家使用引用型變量區(qū)別:
- 普通型變量: V_NAME VARCHAR2(30); 你怎么知道一定VARCHAR2類型,你又怎么知道長度一定是30呢,假設V_NAME VARCHAR2(1); 就對應不上SELECT ENAME, ESALARY INTO V_NAME, V_SAL FROM EMP 這條語句中的 ENAME 的長度了,也就接收不到值了,就會報錯。如果要使用普通變量前提是 ,了解查詢表中對應字段的 類型 及 長度, 才能基于他們來確定類型長度 這比較繁瑣。
- 引用型變量: 聲明一個變量 不再定義類型長度,而是基于接收表字段的類型及長度 來定義。如:V_NAME EMP.ENAME%TYPE;
總結:
使用普通變量定義方式,需要知道表中列的類型,而使用引用類型,不需要考慮列的類型,使用%TYPE是非常好的編程風格,因為引用型變量更加靈活。
1.4.3 記錄型變量
- 記錄型變量 接受表中的一整行記錄,相當于Java中的一個對象
- 語法: 變量名稱 表名%ROWTYPE, 例如:v_emp emp%rowtype;
-- 查詢emp表中1001號員工的個人信息,打印姓名和薪水
DECLARE
-- 記錄型變量接受一行
V_EMP EMP%ROWTYPE;
BEGIN
--記錄型變量默認接受表中的一行數(shù)據(jù),不能指定字段。
SELECT * INTO V_EMP FROM EMP WHERE EMPLOYEEID = 1001;
--打印變量,通過變量名.屬性的方式獲取變量中的值
DBMS_OUTPUT.PUT_LINE('姓名:' || V_EMP.ENAME || ',薪水:' || V_EMP.ESALARY);
END;總結:
- 如果有一張表,有50個字段,那么你程序如果要使用這50字段話,如果你使用引用型變量一個個聲明,會特別繁瑣,記錄型變量可以方便的解決這個問題。
注意錯誤的使用案例如下:
- 記錄型變量只能存儲一個完整的行數(shù)據(jù)

我把 * 換成 單個字段執(zhí)行報錯,因為上面的變量定義的是一行,而現(xiàn)在只給一個是不行的。
- 返回的行太多了,記錄型變量也接收不了

現(xiàn)在這條sql 為什么報錯,因為現(xiàn)在是全表查詢 返回的行數(shù)超出了 一個變量只能接收一行,這就和JAVA 類似了,應該用集合去裝才可以 ,裝進集合在取出來 是不是就跟JAVA 中循環(huán)取值差不多。
1.5 流程控制
1.5.1 條件分支
- IF條件判斷~語法
BEGIN
IF 條件 THEN
執(zhí)行語句
END IF;
END;- IF...ELSE 條件判斷~語法
BEGIN
IF 條件 THEN
執(zhí)行語句
ELSE
執(zhí)行語句
END IF;
END;- IF...ELSIF...ELSE條件判斷~語法,注意關鍵字:ELSIF。
BEGIN
IF 條件1 THEN 執(zhí)行1
ELSIF 條件2 THEN 執(zhí)行2
ELSE 執(zhí)行3
END IF;
END;案例:
--判斷emp表中記錄是否超過20條,10-20之間,或者10條以下
DECLARE
--聲明變量接受emp表中的記錄數(shù)
V_COUNT NUMBER;
BEGIN
--查詢emp表中的記錄數(shù)賦值給變量
SELECT COUNT(1) INTO V_COUNT FROM EMP;
--判斷打印
IF V_COUNT > 20 THEN
DBMS_OUTPUT.PUT_LINE('EMP表中的記錄數(shù)超過了20條為:' || V_COUNT || '條。');
ELSIF V_COUNT >= 10 THEN
DBMS_OUTPUT.PUT_LINE('EMP表中的記錄數(shù)在10~20條之間為:' || V_COUNT || '條。');
ELSE
DBMS_OUTPUT.PUT_LINE('EMP表中的記錄數(shù)在10條以下為:' || V_COUNT || '條。');
END IF;
END;執(zhí)行結果:

1.5.2 循環(huán)
- Loop 語法
BEGIN LOOP EXIT WHEN 退出循環(huán)條件 END LOOP; END;
Loop語法 案例:
--循環(huán)打印 1-5
DECLARE
--聲明循環(huán)變量并賦初值
V_NUM NUMBER := 1;
BEGIN
LOOP
EXIT WHEN V_NUM > 5;
DBMS_OUTPUT.PUT_LINE(V_NUM);
--循環(huán)變量自增
V_NUM := V_NUM + 1;
END LOOP;
END;- While 語法
while(判斷循環(huán)的條件) loop 循環(huán)的語句; END loop;
While語法 案例:
DECLARE
--聲明循環(huán)變量
V_NUM NUMBER;
BEGIN
-- 必須給一個初始值
V_NUM := 1;
WHILE(V_NUM < 10) LOOP
DBMS_OUTPUT.put_line('值為: ' || V_NUM);
V_NUM := V_NUM + 1;
END LOOP;
END;
--此循環(huán)會先判斷再執(zhí)行語句- FOR循環(huán) 語法
FOR 變量名 in 變量的初始值..結束值 lOOP 循環(huán)語句; END loop;
FOR循環(huán)語法 案例:
--for循環(huán)打印 1-10
DECLARE
--聲明循環(huán)變量并賦初值
V_NUM NUMBER ;
BEGIN
--此語句會自動將1到10賦值給V_NUM
FOR V_NUM in 1..10 loop
DBMS_OUTPUT.put_line('值為: ' || V_NUM);
END LOOP;
END;
2、游標
2.1 游標說明
- 用于臨時存儲一個查詢返回的多行數(shù)據(jù),通過遍歷游標,可以逐行訪問處理該結果集的數(shù)據(jù)。
- 游標的使用方式:聲明→打開→讀取→關閉 2.2 語法
游標聲明:
CURSOR 游標名[(參數(shù)列表)] IS 查詢語句;
游標的打開:
OPEN 游標名;
游標的取值:
FETCH 游標名 INTO 變量列表;
游標的關閉:
CLOSE 游標名;
注意: 游標名自身是可以帶參數(shù)的,如果有參數(shù)、參數(shù)會帶入到查詢語句中進行查詢,游標本質 就是 一個 is 查詢語句,也就是說查詢結果被放置到游標中。
2.3 游標屬性
游標的屬性
| 屬性 | 說明 |
| %FOUND | 變量最后從游標中獲取記錄的時候,在結果集中找到了記錄。 |
| %NOTFOUND | 變量最后從游標中獲取記錄的時候,在結果集中沒有找到記錄。 |
| %ROWCOUNT | 當前時刻已經(jīng)從游標中獲取的記錄數(shù)量。 |
| %ISOPEN | 是否打開。 |
| %ROW | 游標指向的行數(shù)。 |
| %COLUMN | 游標指向的列數(shù)。 |
| %ATTEMPTS | 嘗試獲取記錄的次數(shù)。 |
| %ERROR | 發(fā)生錯誤的次數(shù)。 |
| %FETCH_STATUS FETCH | 語句的執(zhí)行狀態(tài),包括成功、失敗和出錯標志。 |
| %SIZE | 當前游標指向的記錄大小。 |
| %LINE_NUMBER | 當前行號。 |
| %ERROR_STRING | 錯誤信息字符串。 |
| %PROCID | 當前執(zhí)行的SQL語句的ID |
2.4 無參數(shù)游標
- 使用游標查詢emp表中所有員工的姓名和工資,Loop循環(huán)依次打印結果集。
--使用游標查詢emp表中所有員工的姓名和工資,依次打印結果集。
DECLARE
--聲明游標
CURSOR C_EMP IS
SELECT ENAME, ESALARY FROM EMP;
--聲明變量用來接受游標中的元素
V_ENAME EMP.ENAME%TYPE;
V_SAL EMP.ESALARY%TYPE;
BEGIN
--打開游標
OPEN C_EMP;
--遍歷游標中的值
LOOP
--通過FETCH語句獲取游標中的值并賦值給變量
FETCH C_EMP
INTO V_ENAME, V_SAL;
--通過%NOTFOUND判斷是否有值,有值打印,沒有則退出循環(huán)
EXIT WHEN C_EMP%NOTFOUND;
DBMS_OUTPUT.PUT_LINE('姓名:' || V_ENAME || ',薪水:' || V_SAL);
END LOOP;
--關閉游標
CLOSE C_EMP;
END;執(zhí)行結果:

2.5 帶參數(shù)的游標
- 使用游標查詢并打印某部門的員工的姓名和薪資,部門編號為運行時手動輸入。
--使用游標查詢并打印某部門的員工的姓名和薪資,部門編號為運行時手動輸入。
DECLARE
--聲明游標傳遞參數(shù)
CURSOR C_EMP(V_EMPLOID EMP.EMPLOYEEID%TYPE) IS
SELECT ENAME, ESALARY FROM EMP WHERE EMPLOYEEID = V_EMPLOID;
--聲明變量用來接受游標中的元素
V_ENAME EMP.ENAME%TYPE;
V_SAL EMP.ESALARY%TYPE;
BEGIN
--打開游標并傳遞參數(shù)
OPEN C_EMP(1001);
--遍歷游標中的值
LOOP
--通過FETCH語句獲取游標中的值并賦值給變量
FETCH C_EMP
INTO V_ENAME, V_SAL;
--通過%NOTFOUND判斷是否有值,有值打印,沒有則退出循環(huán)
EXIT WHEN C_EMP%NOTFOUND;
DBMS_OUTPUT.PUT_LINE('姓名:' || V_ENAME || ',薪水:' || V_SAL);
END LOOP;
--關閉游標
CLOSE C_EMP;
END;執(zhí)行結果:

注意:%NOTFOUND屬性默認值為FLASE,所以在循環(huán)中要注意判斷條件的位置.如果先判斷在FETCH會導致最后一條記錄的值被打印兩次(多循環(huán)一次默認);
錯誤反例演示:

反例執(zhí)行結果:

原因: %NOTFOUND 默認值是 false,false意味著游標里面默認是有值,到底有值還是沒值 需要fetch 好之后才知道有沒有值,%NOTFOUND 默認做了一個有值的假設 , 看下面代碼:
LOOP
--通過%NOTFOUND判斷是否有值,有值打印,沒有則退出循環(huán)
EXIT WHEN C_EMP%NOTFOUND;
--通過FETCH語句獲取游標中的值并賦值給變量
FETCH C_EMP
INTO V_ENAME, V_SAL;
DBMS_OUTPUT.PUT_LINE('姓名:' || V_ENAME || ',薪水:' || V_SAL);
END LOOP;
EXIT WHEN C_EMP%NOTFOUND; 判斷有值打印,出去之后 又帶著有值的進入到循環(huán)中 EXIT WHEN C_EMP%NOTFOUND; 判斷沒值打印, 所以這次打印的是上次值的。注意存放的位置。
3、存儲過程
3.1 概念
- 之前編寫的PLSQL程序可以進行表的操作、判斷、循環(huán)等邏輯處理的工作,但無法重復調用??梢岳斫鉃榇a編寫在了JAVA的main方法中,JAVA可以通過封裝對象和方法來解決復用問題
- PLSQL是將一個個PLSQL的業(yè)務處理過程存儲起來進行復用,這些被存儲起來的PLSQL程序稱之為存儲過程
3.2 語法
參數(shù)的類型分為:
- 不帶參數(shù)的。
- 帶輸入?yún)?shù)的。
- 帶輸入輸出參數(shù)(返回值)的。
CREATE OR REPLACE PROCEDURE 過程名稱[(參數(shù)列表)] IS BEGIN END 過程名稱;
3.3 無參存儲
3.3.1 創(chuàng)建存儲
- 第一種方式: New → Program Window → Procedure

- 第二種方式: New → SQL Window

- 創(chuàng)建存儲過程語法
--通過調用存儲過程打印hello 臭弟弟
CREATE OR REPLACE PROCEDURE P_XIAO IS
--聲明變量
BEGIN
DBMS_OUTPUT.PUT_LINE('hello 臭弟弟');
END P_XIAO ;1、is和as都可以用。
2、存儲過程中沒有declare關鍵字,declare用在語句塊中。也就是說匿名程序才需要, 存儲過程沒有可以直接帶上方--聲明變量。
- 注意點擊△執(zhí)行后 會進行存儲 ,Procedures 中會以P_XIAO 這個名稱進行存儲。

- 通過PLSQL工具查看創(chuàng)建好的存儲過程

3.3.2 調用存儲過程
- 通過PLSQL程序調用 New → Text Window
begin --輸入調用存儲過程的名稱 P_XIAO; end;
查看結果:

3.4 帶輸入?yún)?shù)的存儲過程 IN
說明:
- 帶參數(shù)的存儲過程跟我們在Java中的方法就可以對應上,比如查詢并打印某個員工姓名薪水 ,在調用存儲過程的時候自己指定傳參, 比如我傳一個員工編號,基于傳的編號 將結果返回。
- 實現(xiàn)查詢并打印某個員工(如:編號1001)的姓名和薪水, 調用存儲過程的時候傳入員工編號,自動控制臺打印。
3.4.1 創(chuàng)建帶參數(shù)存儲過程
- 第一種方式: 重新編輯存儲過程 右擊 → Edit(這是基于之前創(chuàng)建的,還可以進行編輯)

- 需要注意的是如果有OR REPLACE當存儲過程名字被更改時,如果PLSQL中存在此存儲過程名稱會被刪除替換創(chuàng)建當前的,果不存在則創(chuàng)建一個新的存儲過程。

執(zhí)行結果:

- 如果沒有OR REPLACE語句 PLSQL也不存相同的名字在則會新創(chuàng)建。如果存在則會報錯。

- 第二種方式: New → SQL Window

- 查詢并打印某個員工(如:編號1001)的姓名和薪水, 要調用存儲過程的時候傳入員工編號,自動控制臺打印。
- 注意:參數(shù)要與定義的參數(shù)的順序和類型一致
--查詢并打印某個員工(如:編號1001)的姓名和薪水, 要調用存儲過程的時候傳入員工編號,自動控制臺打印。
CREATE OR REPLACE PROCEDURE P_xiao_jian(IN_EMPLOYEEID IN EMP.EMPLOYEEID%TYPE) as
--聲明變量接受查詢結果
V_ENAME EMP.ENAME%TYPE;
V_SAL EMP.ESALARY%TYPE;
BEGIN
--根據(jù)用戶傳遞的員工號查詢姓名和薪水
--注意:參數(shù)要與定義的參數(shù)的順序和類型一致 如: ENAME INTO V_ENAME
SELECT ENAME, ESALARY INTO V_ENAME, V_SAL FROM EMP WHERE EMPLOYEEID = IN_EMPLOYEEID;
--打印結果
DBMS_OUTPUT.PUT_LINE('姓名:' || V_ENAME || ',薪水:' || V_SAL);
END P_xiao_jian;3.4.2 調用帶參數(shù)存儲過程
- New → Text window 直接賦值
-- Created on 2023/5/13 by 肖 declare -- Local variables here i integer; begin -- Test statements here P_XIAO_JIAN(1001);--直接賦值 做一個值的傳遞 end;
- 也可以 聲明變量 賦值
-- Created on 2023/5/13 by 肖 declare -- Local variables here V_PARAM number :=1001; --聲明變量 賦值 begin -- Test statements here P_XIAO_JIAN(V_PARAM); end;
查看執(zhí)行結果:

3.5 帶輸入輸出參數(shù)的存儲過程 OUT
說明:
- 帶輸入輸出的這種存儲過程通常是給第三方程序調用的,就比如Java或其他編程語言,也就是說把這個存儲過程的 計算結果進行返回不是在數(shù)據(jù)庫打印打印就完事了
- 舉例說明比如輸入員工編號查詢某個員工信息,要求將薪水作為返回值輸出,給調用的程序使用。這個調用的程序可以是PLSQ自身程序,也可以是第三方比如Java 程序。
3.5.1 創(chuàng)建帶輸入輸出參數(shù)的存儲過程
- 參數(shù)傳遞方式分三類: IN,OUT,IN OUT
- IN 表示輸入?yún)?shù)
- OUT 表示輸出參數(shù)
- IN OUT 即可作輸入?yún)?shù),也可作輸出參數(shù)。
--輸入員工號查詢某個員工(如:編號1001)信息,要求將薪水作為返回值輸出,給調用的程序使用。 CREATE OR REPLACE PROCEDURE P_XIAO_JIAN(IN_EMPLOYEEID IN EMP.EMPLOYEEID%TYPE,OUT_ESALARY OUT EMP.ESALARY%TYPE) as BEGIN --查詢 ESALARY into 給 OUT_ESALARY 輸出變量 SELECT ESALARY INTO OUT_ESALARY FROM EMP WHERE EMPLOYEEID = IN_EMPLOYEEID; END P_XIAO_JIAN;
3.5.2 調用帶輸入輸出參數(shù)存儲過程
DECLARE
--聲明一個變量接受存儲過程的輸出參數(shù)
V_ESALARY EMP.ESALARY%TYPE;
BEGIN
P_XIAO_JIAN(1001, V_ESALARY); --注意參數(shù)的順序
DBMS_OUTPUT.PUT_LINE('工資:'||V_ESALARY);
END; 執(zhí)行結果:

3.6 帶輸入輸出參數(shù)的存儲過程 IN OUT
- IN OUT 即可作輸入?yún)?shù),也可作輸出參數(shù)。
3.6.1 創(chuàng)建帶輸入輸出參數(shù)的存儲過程
--輸入員工號查詢某個員工(如:編號1001)信息,要求將薪水作為返回值輸出,給調用的程序使用。
CREATE OR REPLACE PROCEDURE P_xiao_jian(IN_EMPLOYEEID IN EMP.EMPLOYEEID%TYPE,OUT_ESALARY OUT EMP.ESALARY%TYPE,IN_OUT_PARAM in out number) as
BEGIN
--查詢 ESALARY into 給 OUT_ESALARY 輸出變量
SELECT ESALARY INTO OUT_ESALARY FROM EMP WHERE EMPLOYEEID = IN_EMPLOYEEID;
--打印被傳入的值
dbms_output.put_line('我是被傳入的值'||IN_OUT_PARAM);
--IN_OUT_PARAM賦值默認值為10
IN_OUT_PARAM:=10;
END P_xiao_jian;
3.6.2 調用帶輸入輸出參數(shù)存儲過程
-- Created on 2023/5/16 by 肖
declare
-- Local variables here
V_ESALARY EMP.ESALARY%TYPE;
V_IN_OUT_PARAM number:=6;--傳入的值
begin
-- Test statements here
P_xiao_jian(1001,V_ESALARY,V_IN_OUT_PARAM);
DBMS_OUTPUT.PUT_LINE('薪水:'||V_ESALARY||'原始默認值: '||V_IN_OUT_PARAM);
end;
執(zhí)行結果:

以上就是一文詳解Oracle存儲過程的詳細內容,更多關于Oracle存儲過程的資料請關注腳本之家其它相關文章!
相關文章
Win7 64環(huán)境下Oracle10g 64位版本安裝教程
這篇文章主要為大家詳細介紹了Win7 64環(huán)境下Oracle10g 64位版本安裝教程,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-03-03
oracle 會話 死鎖 執(zhí)行sql 執(zhí)行job的方法
這篇文章主要介紹了oracle 會話 死鎖 執(zhí)行sql 執(zhí)行job的方法,需要的朋友可以參考下2015-12-12
Oracle如何批量將表中字段名全轉換為大寫(利用簡單存儲過程)
這篇文章主要給大家介紹了關于Oracle如何批量將表中字段名全轉換為大寫的相關資料,主要利用的就是一個簡單的存儲過程,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2020-11-11
解決Oracle?DISTINCT?報錯?inconsistent?datatypes:?expected?
這篇文章主要介紹了Oracle DISTINCT報錯inconsistent datatypes:expected-got CLOB(數(shù)據(jù)類型不一致:?應為-,但卻獲得?CLOB),本文給大家分享三種解決方案,需要的朋友可以參考下2023-07-07

