PL/SQL編程語言(2)

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');
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章