1.存儲(chǔ)過程簡述
存儲(chǔ)過程:它是大型數(shù)據(jù)庫中常用的、一組為了完成特定功能的SQL語句集谆刨。
存儲(chǔ)過程在Oracl中是procedure。
2.存儲(chǔ)過程優(yōu)點(diǎn)
2.1.性能高(效率高)
存儲(chǔ)過程相較于普通的SQL語句來說,它的性能非常的好生兆,這是因?yàn)榇鎯?chǔ)過程在第一次編譯之后膝宁,是存儲(chǔ)在數(shù)據(jù)庫的员淫,用的時(shí)候直接使用存儲(chǔ)過程名就可以(第一次之后不需要再編譯),普通的SQL語句每次執(zhí)行都需要先編譯再執(zhí)行拴事。
2.2.低流量
存儲(chǔ)過程在編譯好之后直接存放在數(shù)據(jù)庫刃宵,因此不需要再傳輸大量字符串類型的SQL語句徘公。
2.3.高復(fù)用
存儲(chǔ)過程在寫好之后关面,需要使用這個(gè)特定功能的都可以調(diào)用。
2.4.易維護(hù)
存儲(chǔ)過程在編寫好之后捂齐,如果需要修改需求缩抡,也很容易缝其。
2.5.高安全
完成某個(gè)特定功能的存儲(chǔ)過程一般只有特定身份的人才能使用,所以具有身份限制
3.存儲(chǔ)過程結(jié)構(gòu)
3.1.存儲(chǔ)過程的基本結(jié)構(gòu)
基本結(jié)構(gòu)包含三大部分:聲明過程榴都,執(zhí)行部分嘴高,存儲(chǔ)過程異常(多用于增強(qiáng)代碼的容錯(cuò)性和健壯性)。
3.2.無參存儲(chǔ)過程
CREATE OR REPLACE PROCEDURE 存儲(chǔ)過程名 IS/AS? ? ?//IS和AS選擇哪個(gè)均可春瞬,無區(qū)別
? ? ? ? 變量1 DATE;
? ? ? ? 變量2 NUMBER;
BEGIN
? ? ? ? //需要執(zhí)行的SQL語句
? ? ? ? EXCEPTION;? ? //存儲(chǔ)過程異常
END
3.3.有參存儲(chǔ)過程
CREATE OR REPLACE PROCEDURE 存儲(chǔ)過程名(param student.id%TYPE) AS/IS
name student.name%TYPE;
age number :=20;
BEGIN
? ? ? ? //需要執(zhí)行的SQL語句
? ? ? ? EXCEPTION;? ? //存儲(chǔ)過程異常
END
3.4.進(jìn)行賦值的有參存儲(chǔ)過程
CREATE OR REPLACE PROCEDURE 存儲(chǔ)過程名(sno in? varchar,? ? //in代表的傳入?yún)?shù)
? ? ? ? sname out varchar,? ? ? ? ?//out代表的是返回值
? ? ? ? sage number) AS? ? ? ? ? ? //沒有指定是in或out的時(shí)候宽气,默認(rèn)是in
? ? ? ? total NUMBER := 0;??
BEGIN
? ? ? ? SELECT COUNT(1) INTO total FROM student s WHERE s.age = sage;
? ? ? ? dbms_output.put_line("符合該區(qū)間的學(xué)生有:"|| total || "人");
? ? ? ? EXCEPTION
? ? ? ? ? ? WHEN two_money_throws THEN
? ? ? ? ? ? DBMS_OUTPUT.PUT_LINE("返回值大于1行萄涯!");
END
3.5.存儲(chǔ)過程語法
運(yùn)算符:
SELECT ... INTO ...賦值
SELECT INTO語句可以將select到的結(jié)果賦值給一個(gè)或多個(gè)變量涝影。
CREATE OR REPLACE PROCEDURE 存儲(chǔ)過程名 IS
sname VARCHAR2;? ? //學(xué)生姓名
sage NUMBER;? ? //學(xué)生年齡
saddress VARCHAR2;? ? //學(xué)生籍貫
BEGIN?
? ? select s_address into saddress from student where s_grade = 100;
? ? select s_name,s_age into sname,sage from student where s_grade = 100;
END
IF...END IF/IF...ELSE...END IF? ??選擇語句
IF...END IF
IF sex=1 THEN
? ? dbms_output.put_line("男");
END IF
IF...ELSE IF...END IF
IF sex=1 THEN
? ? dbms_output.put_line("男");
ELSE
? ? dbms_output.put_line("女");
END IF
基本循環(huán)/while循環(huán)/for循環(huán)
基本循環(huán)
LOOP?
? ? IF 表達(dá)式 THEN
? ? ? ? ? 需要執(zhí)行的操作
? ? END IF
END LOOP
while循環(huán)
WHILE 表達(dá)式 LOOP
? ? 需要執(zhí)行的操作
END LOOP
for循環(huán)
FOR x IN 20..30 LOOP
? ? 需要執(zhí)行的操作
END LOOP
游標(biāo)
游標(biāo)是SQL的一個(gè)工作區(qū)燃逻,由系統(tǒng)或者用戶以變量的形式來定義的臂痕。
游標(biāo)的類型:顯式游標(biāo) / 隱式游標(biāo)刻蟹。
游標(biāo)的作用:它用來臨時(shí)存儲(chǔ)從數(shù)據(jù)庫讀取出來的數(shù)據(jù)塊。
游標(biāo)的好處:游標(biāo)可以把讀取出來的數(shù)據(jù)臨時(shí)存放在計(jì)算機(jī)內(nèi)存中片效,使用的時(shí)候不需要頻繁的和磁盤進(jìn)行數(shù)據(jù)交換淀衣,提高了效率和速度召调。
游標(biāo)的特點(diǎn):正常的DML操作(增刪改)和只從數(shù)據(jù)庫中讀取一行數(shù)據(jù)的查操作唠叛,系統(tǒng)會(huì)使用一個(gè)隱式游標(biāo),但是對(duì)于讀取多行數(shù)據(jù)册舞,就需要定義一個(gè)顯式游標(biāo)障般,并通過與游標(biāo)有關(guān)的語句進(jìn)行處理(所以,顯式游標(biāo)通常對(duì)應(yīng)一條返回多行多列的查詢語句)藐石。? ? 一旦打開游標(biāo)于微,語句結(jié)果就會(huì)傳到游標(biāo)變量中,最后應(yīng)用程序在從游標(biāo)變量中分解出需要的數(shù)據(jù)進(jìn)行處理。
隱式游標(biāo):隱式游標(biāo)對(duì)應(yīng)的是DML操作和讀取單行數(shù)據(jù)的查詢操作勺三,可以通過名字SQL的方式去訪問需曾,但是隱式游標(biāo)只能訪問上一個(gè)執(zhí)行的DML和單行讀取的查詢操作呆万,所以在剛執(zhí)行完操作之后,需要馬上使用SQL游標(biāo)名進(jìn)行訪問屬性牡彻。
隱式游標(biāo)的四個(gè)屬性:
? ? ? ? 隱式游標(biāo)屬性? ? ? ? ? ? ? ? ? ? ????返回值? ? ? ? ? ? 意義
1.? ? SQL%ROWCOUNT? ? ? ? ? ? ? ? 整型? ? ? ? ? ? ? ?表示DML執(zhí)行成功影響的數(shù)據(jù)行數(shù)
2.? ? SQL%FOUND? ? ? ? ? ? ? ? ? ? ? ? 布爾值? ? ? ? ? ? 值為TRUE表示DML或查詢操作成功
3.? ? SQL%NOTFOUND? ? ? ? ? ? ? ? ?布爾值? ? ? ? ? ? 和SQL%FOUND相反
4.? ? SQL%ISOPEN? ? ? ? ? ? ? ? ? ? ? ? 布爾值? ? ? ? ? ? DML執(zhí)行時(shí)為TRUE庄吼,執(zhí)行結(jié)束為FALSE
例子:
SET SERVEROUTPUT ON? ? ? ? ? ? ? ? ? ? //設(shè)置環(huán)境變量严就,否則無法正常輸出返回信息
? ? BEGIN
? ? ? ? ? ?UPDATE?emp?SET?sal=sal+100?WHERE?empno=1234;??
? ? ? ? ? ? IF SQL%FOUND THEN
? ? ? ? ? ? ? ? DBMS_OUTPUT.PUT_LINE("修改成功总寻!");
? ? ? ? ? ? ? ? COMMIT;
????????????ELSE
? ? ? ? ? ? ? ? DBMS_OUTPUT.PUT_LINE("修改失敗梢为!");
? ? ? ? ? ? END IF;
? ? END;
顯式游標(biāo):
? ? 聲明:CURSOR 游標(biāo)名[(參1 類型 [參2 類型...])]
? ? ? ? ? ? ? ? IS SELECT語句;
? ? ????注意:參數(shù)為可選的渐行,但是在使用游標(biāo)的時(shí)候,必須傳入實(shí)際參數(shù)铸董;SELECT語句除了INTO子句不可寫之外祟印,其他的都可以;在SELECT語句中粟害,可以使用在游標(biāo)之前定義的變量旁理。
? ? 打開游標(biāo):OPEN 游標(biāo)名[(參1 類型 [參2 類型...])];
? ? ? ? 注意:打開游標(biāo)的時(shí)候我磁,SELECT的查詢結(jié)果就會(huì)被傳到游標(biāo)中孽文。
? ? 提取數(shù)據(jù):FETCH 游標(biāo)名 INTO 變量名1 [變量名2];
? ? ? ? ? ? ? ? ? ? ?或:FETCH 游標(biāo)名 INTO 記錄變量驻襟;
? ? ? ? 注意:游標(biāo)中有一個(gè)指針指向游標(biāo)數(shù)據(jù)區(qū),但是指針一次只能指向一行數(shù)據(jù)芋哭,返回多行數(shù)據(jù)需要重復(fù)執(zhí)行沉衣,可以搭配使用循環(huán)。循環(huán)控制可以通過訪問游標(biāo)屬性來實(shí)現(xiàn)减牺。? ? ? ? 第一種方式:變量名是用于從游標(biāo)中接收數(shù)據(jù)的變量豌习,需要事先定義。參數(shù)的數(shù)量和類型需要和select語句中的一致拔疚。第二種方式:一次將一行數(shù)據(jù)記錄到變量中肥隆,需要通過%ROWTYPE事先定義記錄變量。CLOSE
? ? 關(guān)閉游標(biāo):CLOSE 游標(biāo)名稚失;
? ? ? ? 注意:顯式游標(biāo)必須顯示關(guān)閉栋艳,一旦關(guān)閉就會(huì)釋放游標(biāo)的資源,想要使用必須再次打開句各。
例子:
//采用第一種方法:
SET SERVEROUTPUT ON? ? //設(shè)置環(huán)境變量
? ? DECLARE? ? ? ? ? ? //定義變量(局部變量吸占,作用類似BEGIN)
? ? ? ? name VARCHAR2(10);
? ? ? ? job? ? VARCHAR2(10);
? ? ? ? CURSOR emp_cursor IS? ? ? ? //聲明顯式游標(biāo)emp_cursor?
? ? ? ? SELECT name,job FROM emp WHERE enpno = 100;? ? //執(zhí)行查詢語句
? ? ? ? BEGIN
? ? ? ? ? ? OPEN emp_cursor;? ? ? ? //打開游標(biāo),select語句執(zhí)行結(jié)果返回到游標(biāo)數(shù)據(jù)區(qū)
? ? ? ? ? ? FETCH emp_cursor?INTO?name,job ;? ? ? ?//讀取游標(biāo)中的數(shù)據(jù)
? ? ? ? ? ? DBMS_OUTPUT.PUT_LINE(name || "," || job);? ? //輸出獲取的數(shù)據(jù)
? ? ? ? ? ? CLOSE?emp_cursor;? ? //關(guān)閉游標(biāo)
? ? ? ? END;
//采用第二種方法:
SET SERVEROUTPUT ON? ? ? ? //設(shè)置環(huán)境變量
? ? DECLARE????
? ? ? ? CURSOR?emp_cursor IS? ? ? ? //定義游標(biāo)
? ? ? ? ? ? SELECT name,job,sal FROM emp WHERE empno = 7788;
? ? ? ? ? ? emp_record emp_cursor %ROWTYPE;? ? //定義變量emp_record的類型
? ? ?BEGIN? ? ? ?
? ? ? ? OPEN?emp_cursor ;? ? ? ? //打開游標(biāo)
? ? ? ? FETCH?emp_cursor? INTO? emp_record;? ? ? ?//將查詢到的數(shù)據(jù)傳給變量emp_record
? ? ? ? DBMS_OUTPUT.PUT_LINE(emp_record.name || "," || emp_record.age || "," ||? ? ? ? ? ? ? ? ? ? ? ? emp_record.sal);
? ? ? ? CLOSE?emp_cursor;? ? ? ? //關(guān)閉游標(biāo)
//游標(biāo)搭配循環(huán)
SET?SERVEROUTPUT?ON??
????????DECLARE? ? ? ? ??
? ???????????V_ename?VARCHAR2(10);? ? ? ? ? ?
? ???????????V_sal?NUMBER(5);? ? ? ? ? ?
? ???????????CURSOR?emp_cursor?IS? ? ? ?
? ???????????SELECT?ename,sal?FROM?emp?ORDER?BY?sal?DESC;? ? ? ? ? ?
? ??????????BEGIN? ? ? ? ? ?
? ??????????????OPEN?emp_cursor;? ? ? ? ? ? ? ??
? ??????????????FOR?I?IN?1..3?LOOP? ? ? ? ? ? ??
? ? ? ? ? ? ? ? ?????FETCH?emp_cursor?INTO?v_ename,v_sal;? ? ? ? ? ?
? ???????????????????DBMS_OUTPUT.PUT_LINE(v_ename||','||v_sal);? ? ? ? ? ? ?
? ? ? ? ? ? ? ? ? END?LOOP;? ? ? ? ? ? ? ? ?
? ? ? ? ? ? ? ? ? CLOSE?emp_cursor;? ? ? ? ? ? ?
? ? ? ? ? ? ? ?END;
4.存儲(chǔ)過程高級(jí)
存儲(chǔ)過程的執(zhí)行過程:存儲(chǔ)過程在編譯之后凿宾,每遇到一條語句都會(huì)判斷是pl/sql還是sql矾屯,然后給對(duì)應(yīng)的引擎去處理。
存儲(chǔ)過程存在的問題:當(dāng)遇到大數(shù)據(jù)量的處理時(shí)初厚,執(zhí)行焦點(diǎn)就會(huì)不斷地切換件蚕。過多的上下文切換會(huì)(pl/sql和sql引擎的來回切換成為上下文切換)造成沉重的負(fù)擔(dān),導(dǎo)致效率的下降产禾。
解決問題的方法:Oracl8i之后PL/SQL引入兩個(gè)新數(shù)據(jù)操縱語句——FORALL和BUIK COLLECT骤坐。這兩個(gè)語句減少了上下文切換(一次切換多次執(zhí)行)。
4.1.BUIK COLLECT
BUIK COLLECT的特點(diǎn)是批量檢索下愈,然后將檢索結(jié)果綁定到一個(gè)集合變量中纽绍,和游標(biāo)cursor一條條的檢索是不同的。BUIK COLLECT可以在SELECT INTO势似、FETCH INTO拌夏、RETURNING INTO中使用。
//SELSECT INTO? ? ? ? ? ? ?查詢出來的結(jié)果集合賦值給另一個(gè)集合變量?
語法:SELECT 查詢字段 BUIK COLLECT INTO 存值集合變量 FROM 表名 WHERE 條件;
例:SELECT s_name BUIK COLLECT INTO arr_name FROM student WHERE s_age>10;
//FETCH INTO? ? ? ? ? ? 將集合中的一部分?jǐn)?shù)據(jù)賦值給另一個(gè)集合
語法:FETCH 數(shù)據(jù)集合 BUIK COLLECT INTO 數(shù)據(jù)集合 [ LIMIT 每次獲取的行數(shù) ];
例:
//聲明一個(gè)游標(biāo) 履因,并存放年齡大于10的學(xué)生編號(hào)
CURSOR cur_no IS SELECT s_no FROM student WHERE s_age>10;?
//聲明一個(gè)數(shù)組障簿,類型和游標(biāo)的每個(gè)元素一樣
TYPE arr_no IS VARRY(10) OF?cur_no%ROWTYPE;
//聲明一個(gè)類型為arr_no類型的變量
no arr_no;
BEGIN
? ? FETCH cur_no BUIK COLLECT INTO no LIMIT 100;? ? //每次獲取100條數(shù)據(jù)給該變量
? ? FORALL i IN 1..no.count SAVE EXCEPTIONS?
? ??????????UPDATE student SET s_grade=s_grade-1 WHERE no(i);?
END
//RETURNING? ? ? ? 將進(jìn)行DML操作影響到的數(shù)據(jù)行的列值保存進(jìn)指定的PL/SQL變量中
語法:DML語句 RETURNING 表字段1[,表字段2... ] BUIK COLLECT INTO 字段同類型集合;
例:
TYPE name_collect IS TABLE student.s_name%TYPE;
names name_collect;
BEGIN?
? ? UPDATE student SET s_grade=s_grade-1 WHERE s_age<10
? ? RETURNING? s_name BUIK COLLECT INTO names;
END
注意:
1.不能對(duì)鍵為字符串類型的關(guān)聯(lián)數(shù)組使用BUIK COLLECT子句
2.只能在服務(wù)器端的程序中使用BUIK COLLECT子句,在客戶端使用會(huì)報(bào)錯(cuò)(不支持)
3.BUIK COLLECT INTO子句的目標(biāo)對(duì)象必須是集合類型
4.RETURNING中不能使用復(fù)合目標(biāo)(對(duì)象類型)
5.如果有一個(gè)或多個(gè)隱式數(shù)據(jù)類型轉(zhuǎn)換栅迄,復(fù)合對(duì)象不能在BUIK COLLECT INTO中作為目標(biāo)對(duì)象使用
4.2.FORALL
FORALL主要的作用就是增強(qiáng)DML的操作性站故,簡化代碼。
語法:FORALL 下標(biāo) IN 范圍 [ SAVE EXCEPIONS ] DML語句;
例:FORALL i IN 5..10 DELETE FROM student WHERE s_grade=i;?
在執(zhí)行DML語句的時(shí)候西篓,會(huì)可能遇到異常愈腾,可能會(huì)導(dǎo)致事件的回滾。如果在FORALL的后面沒有加上SAVE EXCEPTIONS語句岂津,DML語句會(huì)在執(zhí)行到一半的時(shí)候停下來虱黄。如果加上了SAVE EXCEPTIONS語句,那么DML語句會(huì)繼續(xù)向下執(zhí)行吮成,異常信息則會(huì)記錄在SQL%BULK_EXCEPTIONS游標(biāo)屬性中橱乱,這個(gè)游標(biāo)屬性是一個(gè)信息記錄集合,每條記錄里面有兩個(gè)字段粱甫,分別是發(fā)生異常的FORALL語句的迭代編號(hào)和錯(cuò)誤代碼泳叠,例:(1,03400)。SQL%BULK_EXCEPTIONS這個(gè)集合保存著最近一次可能發(fā)生異常的信息茶宵,而異常個(gè)數(shù)則由它的COUNT屬性表示危纫,即:SQL%BULK_EXCEPTIONS.COUNT
4.3.INDICES OF
INDICES OF是用于處理稀疏數(shù)組或包含間隙的數(shù)組的。因?yàn)镺racl數(shù)據(jù)庫在10g之前有一個(gè)限制:在IN范圍中节预,會(huì)從第一行到最后一行依次讀取數(shù)據(jù)庫的內(nèi)容叶摄,如果遇到了一個(gè)未定義的行或者是被刪除的行属韧, 那么就會(huì)引發(fā)ORA-22160的異常(ORA-22160: element at index [N] does not exist).
語法:FORALL i INDICES OF 集合 [ SAVE EXCEPTIONS ] sql語句;
例:FORALL i INDICES OF arr_stu
? ? ? ? ? ? INSERT INTO student VALUES(arr_stu(i).name,arr_stu(i).age,arr_stu(i).grade) ;
4.4.VALUES OF?
VALUES OF可以指定FORALL語句中循環(huán)計(jì)數(shù)器的值來自于指定集合中元素的值安拟。VALUES OF適用于綁定數(shù)組為稀疏數(shù)組的情況(也可以不是稀疏數(shù)組)。但是如果VALUES OF使用的集合是聯(lián)合數(shù)組宵喂,則必須使用PLS_INTEGER和BINARY_INTEGER進(jìn)項(xiàng)索引糠赦,即VALUES OF所使用的元素必須是PLS_INTEGER和BINARY_INTEGER。
注意:當(dāng)VALUES OF子句引用的集合為空的時(shí)候锅棕,F(xiàn)ORALL語句會(huì)報(bào)錯(cuò)拙泽。
語法:FORALL i IN VALUES OF 集合 [ SAVE EXCEPTIONS ] sql語句;
5.聯(lián)合數(shù)組和嵌套表
5.1.聯(lián)合數(shù)組
聯(lián)合數(shù)組類似于一張簡單的SQL表,可以按照主鍵檢索數(shù)據(jù)裸燎,且數(shù)據(jù)元素個(gè)數(shù)無限制顾瞻。
存儲(chǔ)的數(shù)據(jù)是沒有順序的,當(dāng)使用變量來檢索數(shù)據(jù)的時(shí)候德绿,每個(gè)數(shù)據(jù)會(huì)分配一個(gè)從1開始的下標(biāo)荷荤。???
下標(biāo)可以為負(fù),且下標(biāo)的數(shù)據(jù)類型支持BINARY_INTEGER,PLS_INTEGER,VARCHAR2移稳。
不能作為表列的數(shù)據(jù)類型使用蕴纳,只能作為PL/SQL復(fù)合數(shù)據(jù)類型使用
存放的數(shù)據(jù)是臨時(shí)數(shù)據(jù),所以不支持insert,select into等SQL語句个粱,等同于sql server中的表變量
語法:
//element_type為聯(lián)合數(shù)組元數(shù)據(jù)指定數(shù)據(jù)類型(先使用TYPE聲明表結(jié)構(gòu)
TYPE type_name IS TABLE OF element_type [ NOT NULL ]
INDEX BY key_type;? ? ? ? //元素下標(biāo)的使用類型
table_name TYPE_NAME;? ? ? ? //使用聲明的TYPE類型來聲明實(shí)際數(shù)組名
5.2.嵌套表
元素下標(biāo)從1開始古毛,長度可以動(dòng)態(tài)增長,沒有限制都许。
嵌套表的數(shù)組元素可以是稀疏數(shù)組稻薇,它的語法和聯(lián)合語法相似嫂冻,但是沒有index by子句
嵌套表必須先初始化才能引用其中元素,若初始化值為空颖低,則后面需要使用extend來擴(kuò)展其大小
嵌套表初始化的時(shí)候是密集的絮吵,但是允許有空隙,所以支持使用內(nèi)置過程delete來從嵌套表刪除元素
嵌套表類型可以作為表列的數(shù)據(jù)類型來使用忱屑。
語法:TYPE type_name IS TABLE OF element_type [ NOT NULL ]
? ? ? ? ? ? table_name TYPE_NAME;
參考文章:https://blog.csdn.net/weixin_41968788/article/details/83659164