1.遊標Cursor
遊標,用於操作結果集,類似jdbc的ResultSet
1.1 語法
CURSOR 遊標名 [(參數名,數據類型),(參數名,數據類型),…] is select 語句;
1.2 使用步驟
- 打開遊標執行查詢:open 遊標名;
- 取一行遊標值:fetch 遊標名 into 變量名;
- 關閉遊標:close 遊標名;
- 遊標的結束方式:exit when 遊標名%notfound
1.3 例子
-- 輸出所有員工的姓名
declare
cursor vrows is
select * from emp;
vrow emp%rowtype;
begin
open vrows;
loop
fetch vrows
into vrow;
exit when vrows%notfound;
dbms_output.put_line(vrow.ename);
end loop;
close vrows;
end;
-- 輸出指定部門的員工姓名
declare
-- 聲明遊標
cursor vrows(vdeptno number) is select * from emp where deptno = vdeptno;
-- 記錄型變量
vrow emp%rowtype;
begin
-- 1.打開遊標
open vrows(20);
-- 提取數據
loop
fetch vrows into vrow;
exit when vrows%notfound;
dbms_output.put_line(vrow.ename);
end loop;
-- 關閉遊標
close vrows;
end;
-- 擴展: 使用for循環遍歷遊標
declare
-- 聲明遊標
cursor vrows is select * from emp;
begin
for vrow in vrows loop
dbms_output.put_line('姓名:'||vrow.ename||' 工資:'||vrow.sal);
end loop;
end;
1.4 系統引用遊標
變量名 sys_refcursor;
open 變量名 for select 語句;
-- 輸出所有員工的姓名和工資
declare
-- 聲明一個遊標
vrows sys_refcursor;
-- 聲明一個變量
vrow emp%rowtype;
begin
-- 1.打開遊標
open vrows for select * from emp;
-- 提取數據
loop
fetch vrows into vrow;
exit when vrows%notfound;
dbms_output.put_line('姓名:'||vrow.ename||' 工資:'||vrow.sal);
end loop;
-- 關閉遊標
close vrows;
end;
2. 異常
2.1 常見系統異常
- zero_divide : 除零異常
- value_error : 類型轉換異常
- no_data_found: 空指針異常
- too_many_rows : 找到了多行記錄,但是賦值給了一行
- others: 捕獲所有異常
2.2 語法
-- 語法:
declare
-- 聲明部分
begin
-- 編寫業務
exception
--處理異常
when 異常1 then
--處理異常
when 異常2 then
--處理異常
when others then 捕獲所有異常
--處理異常
end;
declare
i number;
vrow emp%rowtype;
begin
-- i := 5/0;
-- i := 'adsfadfwerwer';
-- select * into vrow from emp where empno=1234567;
select * into vrow from emp;
exception
when too_many_rows then
dbms_output.put_line('發生了找到了多行記錄,但是賦值給了一行的例外');
when no_data_found then
dbms_output.put_line('發生了沒有找到的例外');
when value_error then
dbms_output.put_line('發生了類型轉換例外');
when zero_divide then
dbms_output.put_line('發生了除零例外');
when others then
dbms_output.put_line('發生了未知的例外');
end;
2.3 自定義異常
聲明:
例外名稱 exception;
拋出例外:
raise 例外名稱
-- 自定義例外
declare
-- 聲明例外
no_emp_found exception;
begin
-- 拋出例外
raise no_emp_found;
exception
when no_emp_found then
dbms_output.put_line('發生了no_emp_found的例外');
when others then
dbms_output.put_line('發生了未知的例外');
end;
-- 查詢指定編號的員工信息,如果沒有找到則拋出自定義的例外
-- 如果沒有找到員工,拋出的是自定義例外
-- 錯誤的演示
declare
-- 聲明例外
no_emp_found exception;
-- 聲明記錄型變量
vrow emp%rowtype;
begin
select * into vrow from emp where empno=9999; -- 已經拋出了例外 no_data_found
if vrow.ename is null then
raise no_emp_found;
end if;
exception
when no_emp_found then
dbms_output.put_line('沒有找到員工,拋出了自定義的例外');
when others then
dbms_output.put_line('發生了未知的');
end;
-- 如果沒有找到員工,拋出的是自定義例外
-- 遊標 : 如果沒有找到數據 %notfound
declare
-- 聲明一個遊標
cursor vrows is select * from emp where empno=1234567;
-- 自定義例外
no_emp_found exception;
-- 聲明一個記錄型變量
vrow emp%rowtype;
begin
-- 1,打開遊標
open vrows;
-- 將遊標向下移動
fetch vrows into vrow;
if vrows%notfound then
raise no_emp_found;
end if;
-- 關閉遊標
close vrows;
exception
when no_emp_found then
dbms_output.put_line('沒有找到員工');
when others then
dbms_output.put_line('未知的例外');
end;
3. 存儲過程
存儲過程 : 實際上是將一段已經編譯好了的PLSQL代碼片斷,封裝在數據庫中,方便調用。
作用:
1. 方便代碼重用
2. 提高SQL的執行的效率
語法:
create [or replace] procedure 存儲過程名稱(參數1 in|out 參數類型,參數2 in|out 參數類型)
is|as
-- 聲明部分
begin
-- 業務邏輯部分
end;
in : 輸入參數
out : 輸出參數
-- 給指定員工漲薪,並打印漲薪前和漲薪後的工資
-- 員工編號 , 輸入參數
-- 漲多少
create or replace procedure proc_updatesal(vempno in number,vcount in number)
is
vsal number;
begin
-- 查詢漲薪前的工資
select sal into vsal from emp where empno=vempno;
-- 打印工資
dbms_output.put_line('漲薪前:'||vsal);
-- 漲薪
update emp set sal = vsal+vcount where empno=vempno;
-- 打印漲薪後
dbms_output.put_line('漲薪後:'||(vsal+vcount));
-- 提交事務
commit;
end;
-- 調用方式1
call proc_updatesal(7369,10);
-- 調用方式2
declare
begin
proc_updatesal(7369,-100);
end;
--存儲過程: 獲取指定員工的年薪
-- 參數 : 員工編號 in number
-- 參數: 年薪 out number
create or replace procedure proc_getyearsal(vempno in number,vyearsal out number)
is
begin
select sal*12+nvl(comm,0) into vyearsal from emp where empno=vempno;
end;
-- 調用
declare
yearsal number;
begin
proc_getyearsal(7369,yearsal);
dbms_output.put_line('年薪:'||yearsal);
end;
4. 函數
存儲函數:
實際上是將一段PLSQL代碼片斷,封裝在數據庫,方便別人去調用
作用:
1.提高代碼複用性
2.提高執行效率
語法:
create [or replace] function 函數名稱(參數1 in|out 參數類型) return 返回類型
is|as
begin
end;
區別:
1.函數有返回值,過程沒有
2.過程能實現的,函數能實現
3.函數能實現的,過程也能實現
4.函數可以直接在SQL語句中使用
5.函數和過程本質上沒有區別
-- 獲取指定員工的年薪
create or replace function func_getyearsal(vempno number) return number
is
--聲明一個變量
vyearsal number;
begin
select sal*12+nvl(comm,0) into vyearsal from emp where empno=vempno;
return vyearsal;
end;
-- 調用函數
declare
yearsal number;
begin
yearsal := func_getyearsal(7369);
dbms_output.put_line('年薪:'||yearsal);
end;
-- 在SQL語句中直接調用
select emp.*,func_getyearsal(emp.empno) "年薪" from emp;
select * from emp;
5. 觸發器
觸發器:
當我們在SQL中執行了insert | update | delete這些操作的時候,可以去觸發一段PLSQL代碼片斷執行
語法:
create [or replace] trigger 觸發器名稱
before | after
insert | update | delete
on 表名
[for each row] //行級觸發器,寫這個語句
declare
-- 聲明
begin
-- 業務
end;
作用: 表的內容發生變化
1. 當數據發生變化的時候, 需要觸發一段業務邏輯的執行
2. 數據校驗
觸發器的分類:
語句級觸發器:
一條語句,不管影響多少行記錄,都只觸發一次
行級觸發器:
一條語句,影響了多少行記錄,就觸發多少次
:new 新的記錄
:old 舊的記錄
-- 當向emp表插入數據之前,就輸出一句hello trigger
create or replace trigger tri_test1
before
insert on emp
declare
begin
dbms_output.put_line('hello trigger');
end;
insert into emp(empno,ename) values(9527,'huaan');
update emp set sal=10 where empno=9527;
-- 校驗指定週六不能插入員工
--before insert
create or replace trigger tri_check
before
insert on emp
declare
vday varchar2(20);
begin
-- 查詢今天周幾
select trim(to_char(sysdate,'day')) into vday from dual;
-- 條件判斷
if vday='saturday' then
raise_application_error(-20000,'老闆週六不在,不能插入數據!');
end if;
end;
create or replace trigger tri_test2
before
update on emp
declare
begin
dbms_output.put_line('語句級觸發器');
end;
create or replace trigger tri_test3
before
update on emp
for each row
declare
begin
-- :new sal+10 :old sal
dbms_output.put_line('行級觸發器:'||:new.sal||' 舊的記錄:'||:old.sal);
end;
update emp set sal=sal-10;
/*
模擬mysql中的ID自動增長 auto_increment
insert into 表名 values(null,zhangsan);
觸發器 : before insert
序列 : 1,2,3,4,5,6,7,8...
*/
create table ppp(
pid number primary key,
pname varchar2(20)
);
insert into ppp values(null,'zs');
-- 創建一個序列
create sequence seq_ppp_index;
-- 觸發器
create or replace trigger tri_ppp
before
insert on ppp
for each row
declare
begin
if :new.pid is null then
select seq_ppp_index.nextval into :new.pid from dual;
end if;
end;
insert into ppp values(null,'zs');