Oracle (07)遊標物件.智慧迴圈(FOR) 遍歷遊標.NULL值的比較.異常處理.儲存過程.如何指定引數的模式.function 函式.包 package.觸發器 trigger
遊標物件 熟練
遊標就是查詢結果的容器中游動的標記 !
使用的步驟與語法:
-
宣告一個遊標, 並繫結一個select語句
在宣告區中定義: cursor 遊標變數名稱 is 查詢語句;
-
開啟遊標
在執行區操作:
open 遊標變數; -
控制遊標向下移動, 提取一行資料
fetch 遊標變數 into record型別變數;如果結果集中存在的資料 只有一列, 可以賦值給基本資料型別(number varchar2…)
-
關閉遊標 釋放資源
因為資料庫的連線控制代碼只有1024個 ,所以在連線完畢後, 需要釋放資源 !
案例
定義一個遊標, 結果集中包含了 s_emp表格中的id,salary,last_name欄位, 然後提取遊標的前3行資料, 並將其列印到控制檯:
set serveroutput on;
declare
–宣告遊標 , 並關聯查詢語句
cursor myec is select id,salary,last_name from s_emp;
–定義一個與遊標中屬性一致的 record型別變數
ecr myec%rowtype;
begin
--開啟遊標 open myec; --迴圈取出前三條 for i in 1..3 loop --隨著每次迴圈, 遊標向下移動, 並取出行內資料 fetch myec into ecr; --輸出列印這些列的內容 dbms_output.put_line('員工編號:'||ecr.id||',員工姓名:'||ecr.last_name||',員工月薪:'||ecr.salary); end loop; close myec;
end;
通過多表查詢 ,關聯遊標, 並進行列印輸出
查詢 s_dept 和 s_region表格, 並取出s_dept(id,name),s_region(name)
set serveroutput on;
declare
cursor s_dr_cursor is select d.id did,d.name dname,r.name rname from s_dept d,s_region r where d.region_id=r.id;
mydr s_dr_cursor%rowtype;
begin
open s_dr_cursor;
fetch s_dr_cursor into mydr; dbms_output.put_line('部門編號:'||mydr.did||',部門名稱:'||mydr.dname||',地區名稱:'||mydr.rname); close s_dr_cursor;
end;
遊標常用屬性: 熟練
-
遊標%rowtype : 得到與遊標屬性一致的 record型別 !
-
遊標%found : 判斷上次遊標是否取到了資料 , 取到資料則返回true , 否則返回false;
-
遊標%notfound:判斷上次遊標是否取到了資料 , 取到資料則返回false , 否則返回true;
遊標%found or 遊標%notfound 在使用時限制:
- 遊標必須是開啟的 ,
- 並且在呼叫此屬性時, 遊標必須是經過下移的! 否則獲取結果為null
練習:
通過迴圈遍歷上題:
通過多表查詢 ,關聯遊標, 並進行列印輸出
查詢 s_dept 和 s_region表格, 並取出s_dept(id,name),s_region(name)
set serveroutput on;
declare
cursor s_dr_cursor is select d.id did,d.name dname,r.name rname from s_dept d,s_region r where d.region_id=r.id;
mydr s_dr_cursor%rowtype;
begin
open s_dr_cursor;
loop
fetch s_dr_cursor into mydr;
--如果上次遊標下移, 沒有獲取到資料, 則退出迴圈
exit when s_dr_cursor%notfound;
dbms_output.put_line('部門編號:'||mydr.did||',部門名稱:'||mydr.dname||',地區名稱:'||mydr.rname);
end loop;
close s_dr_cursor;
end;
練習
將上述案例, 改造為while迴圈
-
什麼是迴圈條件
-
什麼是退出迴圈條件
set serveroutput on;
declare
cursor s_dr_cursor is select d.id did,d.name dname,r.name rname from s_dept d,s_region r where d.region_id=r.id;
mydr s_dr_cursor%rowtype;
begin
open s_dr_cursor;
fetch s_dr_cursor into mydr;
while s_dr_cursor%found loop
dbms_output.put_line(‘部門編號:’||mydr.did||’,部門名稱:’||mydr.dname||’,地區名稱:’||mydr.rname);
fetch s_dr_cursor into mydr;
end loop;
close s_dr_cursor;
end;
遊標其他屬性
-
遊標變數%isopen: 判斷遊標是否開啟, 開啟則返回true , 否則返回false
-
遊標變數%rowcount: 獲取當前遊標所在資料行的 偏移量 !
set serveroutput on;
declare
cursor s_dr_cursor is select d.id did,d.name dname,r.name rname from s_dept d,s_region r where d.region_id=r.id;
mydr s_dr_cursor%rowtype;
begin
open s_dr_cursor;
fetch s_dr_cursor into mydr;
while s_dr_cursor%found loop
dbms_output.put_line(‘部門編號:’||mydr.did||’,部門名稱:’||mydr.dname||’,地區名稱:’||mydr.rname);
fetch s_dr_cursor into mydr;
dbms_output.put_line('當前遊標開啟狀態:'||s_dr_cursor%isopen||',當前遊標偏移量:'||s_dr_cursor%rowcount);
end loop;
close s_dr_cursor;
end;
智慧迴圈(FOR) 遍歷遊標 ***
for迴圈遍歷遊標時:
- 自動定義record變數
- 自動開啟遊標
- 自動關閉遊標
- 每次迴圈自動提取一行資料 給到record變數
案例:
set serveroutput on;
declare
cursor s_dr_cursor is select d.id did,d.name dname,r.name rname from s_dept d,s_region r where d.region_id=r.id;
begin
for mydr in s_dr_cursor loop
dbms_output.put_line(‘部門編號:’||mydr.did||’,部門名稱:’||mydr.dname||’,地區名稱:’||mydr.rname);
end loop;
end;
引數遊標
在遊標開啟時, 可以傳遞引數到查詢語句中
步驟格式:
1. 定義遊標時, 在遊標變數名稱後,新增形式引數列表;
cursor 遊標變數名稱(形式引數列表) is 查詢語句;
列表中的形式引數, 可以在select語句中使用;
2. 在開啟遊標時, 在遊標變數名稱後, 輸入實際引數列表!
open 遊標變數名稱(實際引數列表);
案例:
提示使用者輸入要查詢的員工編號. 根據使用者輸入的編號查詢s_emp表格, 並輸出id,last_name,salary資訊
set serveroutput on;
declare
cursor emp_cursor(empid number) is select id,last_name,salary from s_emp where id=empid;
empid number;
myemp emp_cursor%rowtype;
begin
empid := &請輸入要查詢的員工編號;
open emp_cursor(empid);
fetch emp_cursor into myemp;
dbms_output.put_line(‘資訊查詢完畢’);
if emp_cursor%found then
dbms_output.put_line(‘員工編號:’||myemp.id||’,員工姓名:’||myemp.last_name||’,員工月薪:’||myemp.salary);
else
dbms_output.put_line(‘您指定的員工編號不存在!’);
end if;
end;
參考遊標
參考遊標在宣告遊標時, 不用指定查詢語句, 可以在遊標的開啟時 繫結一個語句 !
也就是說, 可以在宣告區不指定查詢語句 來建立遊標, 等到執行區程式碼執行時, 再去繫結查詢語句 進行操作!
語法格式:
-
宣告遊標型別
type 遊標名稱 is ref cursor; -
宣告遊標變數
變數名 遊標型別名稱;
-
開啟遊標 並繫結查詢語句
open 遊標變數 for ‘查詢語句字串’;
案例:
提示使用者操作, 根據使用者的選擇, 執行不同的SQL語句
提示使用者輸入要查詢的資訊 為 部門|員工, 然後再根據使用者的提示開啟遊標繫結不同的查詢語句!
set serveroutput on;
declare
–定義參考遊標型別
type mycursor is ref cursor;
– 根據上面的型別, 定義一個變數
mc mycursor;
– 字串型別的變數, 用來後期指定查詢語句
select_sql varchar2(500);
– 接收使用者輸入的變數, 用來判斷使用者選擇的操作
user_type number;
– 接收使用者輸入的查詢id
user_id number;
–用來接收查詢的員工薪資
usersalary number;
–用來接收查詢的部門名稱
user_deptname varchar2(500);
begin
dbms_output.put_line(‘歡迎進入公司管理系統:’);
dbms_output.put_line(‘請輸入您要查詢的資訊:’);
dbms_output.put_line(‘1.查詢員工資訊’);
dbms_output.put_line(‘2.查詢部門資訊’);
user_type := &請輸入1員工查詢2部門查詢;
if user_type =1 then
--使用者要查詢的是員工資訊
user_id := &請輸入要查詢的員工id;
select_sql := 'select salary from s_emp where id='||user_id;
--開啟遊標, 並繫結sql語句
open mc for select_sql;
fetch mc into usersalary;
dbms_output.put_line('您查詢的員工月薪為:'||usersalary);
close mc;
elsif user_type=2 then
--使用者要查詢的時部門資訊
user_id := &請輸入要查詢的部門id;
select_sql := 'select name from s_dept where id='||user_id;
--開啟遊標 並繫結查詢語句
open mc for select_sql;
fetch mc into user_deptname;
dbms_output.put_line('查詢的部門名稱為:'||user_deptname);
close mc;
else
--使用者輸入錯誤
dbms_output.put_line('您輸入的選項有誤');
end if;
end;
NULL值的比較***
在PL/SQL中, null值參與比較運算, 不會出現結果!
如果使用null參與if運算, 永遠進不去任何的if段 , 必進else!
面試題: 觀察如下程式碼 ,分析列印的結果為哪句話, 並說出原因!
A. 12345程式執行結束 B. 3程式執行結束
C. 程式執行結束 D. 5程式執行結束 ✔
set serveroutput on;
declare
x number := 10;
y number;
begin
if x>y then
dbms_output.put_line(‘1’);
elsif x=y then
dbms_output.put_line(‘2’);
elsif x!=y then
dbms_output.put_line(‘3’);
elsif x<y then
dbms_output.put_line(‘4’);
else
dbms_output.put_line(‘5’);
end if;
dbms_output.put_line(‘程式執行結束’);
end;
/
異常處理
-
非執行時異常 (受檢異常)
-
執行時異常 (非受檢異常)
PL/SQL中的執行時異常
set serveroutput on;
declare
m_last_name s_emp.last_name%type;
begin
select last_name into m_last_name from s_emp where id=-100;
dbms_output.put_line(m_last_name);
end;
上述程式碼 出現瞭如下錯誤:
錯誤報告:
ORA-01403: 未找到任何資料
ORA-06512: 在 line 4
01403. 00000 - “no_data_found”
異常處理格式 ***
begin
…
exception
–當begin中出現執行時異常後, exception塊自動執行
when 異常型別1 then
–異常型別匹配時的處理塊
when 異常型別2 then
...
when 異常型別n then
--異常型別匹配時的處理塊
when others then
--當上面所有的異常型別都不匹配時, 或 上面不存在任何的異常型別時 ,由這裡處理異常!
end;
使用上述的異常處理格式, 處理程式碼的異常
set serveroutput on;
declare
m_last_name s_emp.last_name%type;
begin
select last_name into m_last_name from s_emp where id=-100;
dbms_output.put_line(m_last_name);
exception
when others then
dbms_output.put_line(‘程式出現了異常. 哈哈哈哈哈’);
end;
針對異常型別 進行異常處理
set serveroutput on;
declare
m_last_name s_emp.last_name%type;
begin
select last_name into m_last_name from s_emp where id=-100;
dbms_output.put_line(m_last_name);
exception
when no_data_found then
dbms_output.put_line('程式資料查詢失敗. 哈哈哈哈哈');
when others then
dbms_output.put_line('程式出現了其他異常. 哈哈哈哈哈');
end;
儲存過程 *****
把一組邏輯相關的SQL語句 或 PL/SQL語句 組織到一起的一個程式碼結構, 我們稱之為過程 !
定義上 與 Java中的方法很像, 但是不是!
儲存過程不存在返回值 !
建立儲存過程的語法格式:
create or replace procedure 過程名稱(形參列表)
is
/*宣告區*/
begin
/*程式碼執行區*/
end;
呼叫儲存過程的語法:
在sql> 中通過兩種方式呼叫:
1. call 過程名稱(實參列表);
2. exec 過程名稱(實參列表);
在匿名過程塊中:
3.
begin
過程名稱(實參列表);
end;
刪除儲存過程
drop procedure 儲存過程名稱;
練習
編寫一個儲存過程 , 用來比較兩個引數的大小, 並輸出較大的一個
(建議大家在手生時, 編寫儲存過程時, 先編寫匿名塊, 再改造 )
set serveroutput on;
declare
x number := 1000;
y number := 2000;
begin
if x>y then
dbms_output.put_line(x);
elsif x<y then
dbms_output.put_line(y);
else
dbms_output.put_line(‘兩個數字沒有誰大誰小’);
end if;
end;
將上述的程式碼 改造為儲存過程, 並嘗試呼叫:
create or replace procedure max15(x number,y number)
is
begin
if x>y then
dbms_output.put_line(x);
elsif x<y then
dbms_output.put_line(y);
else
dbms_output.put_line(‘兩個數字沒有誰大誰小’);
end if;
end;
set serveroutput on;
call max15(150,160);
exec max15(150,160);
儲存過程形式引數 指定預設值 ***
案例:
drop procedure max15;
create or replace procedure max15(x number:=100,y number:=200)
is
begin
if x>y then
dbms_output.put_line(x);
elsif x<y then
dbms_output.put_line(y);
else
dbms_output.put_line(‘兩個數字沒有誰大誰小’);
end if;
end;
set serveroutput on;
call max15(150,160);
在匿名塊中呼叫儲存過程 ***
set serveroutput on;
begin
max15(1,10);
end;
查詢儲存過程結構
格式: desc 過程名稱;
案例: desc max15;
查詢的結果如下:
SQL> desc max15;
PROCEDURE max15
引數名稱 型別 輸入/輸出 預設值?
X NUMBER IN DEFAULT
Y NUMBER IN DEFAULT
-
輸入/輸出:
描述的是引數的操作模式:
取值:
- in: 只讀,預設模式, 表示引數在過程中只讀
- out:只寫, 引數在過程中不可用來讀取使用 , 只能用來賦值!
- in out: 可讀可寫, 在過程中 既可以讀取資料, 又可以給引數賦值!如果引數的模式為out 或者為in out , 過程在呼叫時, 傳遞的引數必須時變數.
-
預設值? :
表示引數在過程中, 是否存在預設值, default表示存在預設值!
如何指定引數的模式 ***
在定義儲存過程時, 編寫形式引數列表時 可以指定引數的使用模式
格式:
... 過程名(引數名 模式 引數型別); 模式可選: in/out/in out
案例:
編寫一個儲存過程, 擁有三個number型別的引數, 比較第一個引數和第二個引數 , 將最大的值 儲存在第三個引數中.
create or replace procedure max15_2(x number,y number,z out number)
is
begin
if x>y then
z := x;
elsif y>x then
z := y;
else
z :=x;
end if;
end;
在匿名塊中呼叫:
set serveroutput on;
declare
max1 number;
begin
max15_2(100,200,max1);
dbms_output.put_line('max的值為:'||max1);
end;
根據形式引數的名稱, 傳遞值
語法格式:
在呼叫時,
儲存過程名稱(引數名稱=>值);
create or replace procedure max15(x number:=100,y number:=200)
is
begin
if x>y then
dbms_output.put_line(x);
elsif x<y then
dbms_output.put_line(y);
else
dbms_output.put_line(‘兩個數字沒有誰大誰小’);
end if;
end;
set serveroutput on;
declare
begin
max15(y=>50);
end;
function 函式 *
函式的概念 基本上可以理解為Java中的方法 .
與儲存過程概念也很相似, 都是把一組邏輯相關的SQL語句 / PLSQL語句 組織到一起的程式碼結構!
函式與儲存過程的區別:
1. 名稱不同 , 函式的關鍵字是function , 儲存過程的關鍵字procedure
2. 函式可以通過return返回資料, 而儲存過程沒有返回值!
3. 呼叫方式不同 ,
- 儲存過程在呼叫時, 可以是pl/sql中的一部分, 也可以在sql>中直接call
- 函式在呼叫時, 必須組成表示式, 才可以使用!
宣告函式的語法格式:
create or replace function 函式名(形參列表) return 返回值型別
is
/*宣告區*/
begin
return 值;
end;
檢視函式結構:
desc 函式名;
刪除函式
drop function 函式名;
案例:
建立一個函式, 兩個number型別引數 ,返回比較引數的最大值 .
create or replace function mymax15(x numnber,y number) return number
is
begin
if x>y then
return x;
end if;
return y;
end;
回顧之前呼叫單行函式 和 組函式
select length(‘12345’) from dual;
呼叫剛編寫的函式:
select mymax15(100,300) from dual;
練習:
編寫一個函式 , 傳入兩個值, 返回其中的最大值, 並將兩個引數的和 儲存到第二個引數中, 在程式碼中不允許使用第三方變數:
create or replace function maxsum15(x number,y in out number) return number
is
begin
if x<y then
y := x+y;
return y-x;
end if;
y := x+y;
return x;
end;
在匿名塊中呼叫 與java很像:
set serveroutput on;
declare
–用來接收返回值
var_max number;
var_x number := 300;
var_y number := 1000;
begin
var_max := maxsum15(var_x,var_y);
dbms_output.put_line(‘兩個數字的和為:’||var_y||’,最大值為:’||var_max);
end;
包 package
把相關的 函式, 過程, 型別 等等放到一起的一個邏輯結構 !
dbms_output: 系統輸出包
函式:
- put_line(文字); 輸出到控制檯
dbms_random: 隨機數包
函式:
-
value(引數1,引數2)
引數1. 隨機數字產生的最小範圍 number型別 in模式
引數2. 隨機數字產生的最大範圍 number型別 in模式返回值: number型別的浮點型隨機數
案例:
獲取一個0-10000的隨機數字
select dbms_random.value(0,10000) from dual;
隨機獲取一個0-10000的隨機數 , 並取整
select trunc(dbms_random.value(0,10000)) from dual;
dbms_job 定時任務包
dbms_lob: 與定時任務包很像, 容易混淆, 是用來讀取和寫入大文字和二進位制的包!
常用函式:
-
submit(引數1,引數2,引數3,引數4);
- 用來提交定時任務到資料庫管理系統
- 引數1.job(out模式的binary_integer型別) , 是用來提交定時任務時 接收任務編號的!
- 引數2.what(varchar2):要呼叫的儲存過程的名稱; 例如:‘過程名();’
- 引數3.next_date(date): 第一次執行任務的時間
-
- 引數4.interval(varchar2) 呼叫的間隔時間
- 引數4, 要傳遞的是一個字串, 但是字串中必須是date型別 , 例如: ‘sysdate+1’;
- 間隔時間的計算方式 , 引數4-引數3
-
run(任務編號 binary_integer型別): 執行資料庫管理系統中已提交的某定時任務!
-
remove(任務編號 binary_integer型別);移除資料庫管理系統中已提交的某定時任務! 無論任務是否在執行都會被移除!
定時任務案例
定時向dongfei表格中, 插入一行資料 10秒插入一次!
-
建立一個表格
drop table dongfei cascade constraints;
create table dongfei(
id number constraint dongfei_id_pk primary key,
name varchar2(20)
length number
); -
定義一個序列, 用來插入id欄位
drop sequence dongfei_id_seq;
create sequence dongfei_id_seq;- 序列中如何獲取當前的值
序列.currval - 序列中如何獲取下一個值:
序列.nextval
- 序列中如何獲取當前的值
-
定義一個儲存過程, 插入dongfei一行資料
drop procedure xdongfei;
create or replace procedure xdongfei
is
begin
insert into dongfei values(dongfei_id_seq.nextval,‘董飛鈦合金版’,2);
insert into dongfei values(dongfei_id_seq.nextval,‘董飛回旋專版’||dongfei_id_seq.currval,5);
end; -
在匿名塊中 建立定時任務 並提交, 然後啟動
set serveroutput on;
declare
job_id binary_integer;
begin
dbms_job.submit(job_id,‘xdongfei();’,sysdate,‘sysdate+(1/(1440*6))’);
dbms_job.run(job_id);
dbms_output.put_line(‘本次任務的id:’||job_id);
end; -
刪除定時任務
begin
dbms_job.remove(1);
end;
觸發器 trigger
概念:
在對資料庫表格 進行 dml操作時 , 我們可以對其新增觸發器 !
新增觸發器後, 當指定的dml操作發生時, 觸發器會自動執行指定的一段過程 !
行級觸發器:
針對每一行資料, 觸發一次邏輯 !
語句級觸發器:
每個dml操作語句 ,無論 修改 了多少條資料 , 都只觸發一次!
建立觸發器的語法格式:
create or replace trigger 名稱 before|after insert|update|delete on 表名 [for each row]
declare
begin
觸發器執行時的程式碼塊
end;
[for each row]: 在建立語句上新增, 表示將語句級觸發器 修改為 行級觸發器
案例:
偵聽 dongfei 表格的插入操作
create or replace trigger dongfei_insert_trigger after insert on dongfei
declare
begin
dbms_output.put_line('有人在插入dongfei表格');
end;
插入資料:
set serveroutput on;
insert into dongfei values(dongfei_id_seq.nextval,'董飛上天版',20);
在觸發器中 獲取原資料 和 新資料
在觸發器的begin 到 end之間, 可以使用兩個已存在的物件, 操作新資料和舊資料 !
:old 舊資料 和 :new 新資料
看作自定義物件 , 通過:new.列名, 即可獲取新資料中 某一列的值!
DML操作分為三種:
1. insert語句 : 只存在新資料, 沒有舊資料!
2. update語句 : 存在新資料和舊資料!
3. delete語句 : 只有舊資料 , 沒有新資料
編寫一個偵聽修改的觸發器, (行級)
create or replace trigger dongfei_update_trigger after update on dongfei for each row
declare
begin
dbms_output.put_line(‘有人在修改董飛:舊董飛的名字:’||:old.name||’,新的董飛名稱:’||:new.name);
end;
set serveroutput on;
update dongfei set name=’***’;
作業
-
編寫一個儲存過程, 存在兩個number型別形式引數,
比較大小, 並將較大的資料, 儲存到第二個引數的位置 !
-
編寫一個儲存過程, 存在兩個number型別形式引數,
比較大小, 並將較大的資料, 儲存到第二個引數的位置 !
求和, 將和儲存到第一個引數的位置!