oracle數據庫(六)_PLSQL

格式

[DECLARE]
     				--declaration statements  ①
BEGIN
     				--executable statements  ②
	[EXCEPTION]		--exception statements  ③
END;

類型

引用類型

%type引用特定類型
%rowtype應用行對象類型

複合類型
  • 自定義行對象
-- 自定義行對象
declare
   type tp_row is record(
        empno number(24),
        ename varchar2(24)
   );
   my_row tp_row;
   
begin
   -- 使用行對象
   select empno,ename into my_row from emp where rownum = 1;
   dbms_output.put_line(my_row.empno
          ||','
          ||my_row.ename);
end;
  • 列類型(一維表序列)
-- 自定義列類型(一維表序列)
declare
   type tp_col is table of varchar2(36); -- index by binary_integer;
   my_col tp_col;
 
begin 
   -- 迭代列類型
   select ename bulk collect into my_col from emp;
   -- for idx in my_col.first..my_col.last
   for idx in 1..my_col.count loop
       dbms_output.put_line(my_col(idx));
   end loop;
end;
  • 表類型(多維表類型)
-- 自定義表類型(多維表類型)
declare
   type tp_row is record(
        empno number(24),
        ename varchar2(24)
   );
   type tp_frame is table of tp_row; -- index by binary_integer
   
   my_row tp_row;
   my_frame tp_frame;
   
begin
   -- 使用表類型
   select empno,ename bulk collect into my_frame from emp;
   for idx in my_frame.first..my_frame.last loop
       dbms_output.put_line(my_frame(idx).empno||','||my_frame(idx).ename);
   end loop;
end;
數組
DECLARE
   type namesarray IS VARRAY(6) OF VARCHAR2(10);
   type grades IS VARRAY(6) OF number(24);
   names namesarray;
   marks grades;
   total integer;
BEGIN
   names := namesarray('Kavita', 'Pritam', 'Ayan', 'Rishav', 'Aziz');
   marks:= grades(98, 97, 78, 87, 92);
   total := names.count;
   dbms_output.put_line('Total '|| total || ' Students');
   
   FOR i in 1 .. total LOOP
      dbms_output.put_line('Student: ' || names(i) || '
      Marks: ' || marks(i));
   END LOOP;
END;
序列屬性

arr.count數組數量
arr.first第一個下下標值
arr.last最後的下標值
arr.delete清空數組
arr.delete(num)刪除下標數據,table類型纔可以用
arr.extend下標長度加一
arr(num):=val賦值,table可以直接賦值,arr需要先extend再賦值

hello world

-- hello world
declare
      ss varchar2(24);
      df number default 3;        -- 默認值
      PI CONSTANT NUMBER := 3.14; -- 常量
begin
      ss := 'hello world';
      dbms_output.put_line(ss);
end;

--
declare
      emprow emp%rowtype;
begin
      select * into emprow from emp where empno = 7934;
      dbms_output.put_line(emprow.ename);
end;
--
begin
      dbms_output.put_line('hello world');
end;

流程控制

if
-- if
declare
      sal emp.sal%type;
begin
      select sal into sal from emp where ename = 'JAMES';
      if sal > 1500 then
         dbms_output.put_line(1000);
      elsif sal > 900 then
         dbms_output.put_line(800);
      else
         dbms_output.put_line(400);
      end if;
end;
case
-- case
declare
      sal emp.sal%type;
begin
      select sal into sal from emp where ename = 'JAMES';
      case 
      when sal > 1500 then
         dbms_output.put_line(1000);
      when sal > 900 then
         dbms_output.put_line(800);
      else
         dbms_output.put_line(400);
      end case;
end;

循環

loop
-- loop循環
-- 1-100累計和
declare
   counter number(6):= 0;
   sumresult number(6):= 0;
begin
   loop 
     counter := counter+1;
     sumresult := sumresult+counter;
     exit when counter >= 100;
   end loop;
   dbms_output.put_line('result:'||to_char(sumresult));
end;
while
-- while循環
-- 1-100累計和
declare
   counter number(6):= 0;
   sumresult number(6):= 0;
begin
   while counter<100 loop 
     counter := counter+1;
     sumresult := sumresult+counter;
   end loop;
   dbms_output.put_line('result:'||to_char(sumresult));
end;
for
-- FOR
BEGIN
    FOR cnt IN 1..100 LOOP
        DBMS_OUTPUT.PUT_LINE(cnt);
    END LOOP;
END;

遊標

定義
  • 遊標可以將查詢結果集中的記錄逐一取出
  • 隱形遊標,在查詢返回一行時自動創建
  • 顯式遊標,當查詢返回多行是必須自行創建
使用
-- OPEN curs				打開遊標,遊標知道第一行
-- FETCH curs INTO val 		獲取遊標值,並把遊標指向下一行
-- CLOSE curs				關閉遊標

-- %ISOPEN					是否打開		
-- %FOUND | %NOTFOUND  		是否找到數據返回布爾值
-- %ROWCOUNT				一取數據行數


-- 標準迭代行數據
DECLARE
    CURSOR c_code IS
        SELECT "ts_code","trade_date","close" FROM "code" WHERE "trade_date" > TO_DATE('20191001','yyyymmdd');
    t_row c_code%ROWTYPE;
    ret VARCHAR2(255);
BEGIN
    OPEN c_code;  
    LOOP
        FETCH c_code INTO t_row;
        EXIT WHEN c_code%NOTFOUND;
        ret := t_row."ts_code"||' in '||TO_CHAR(t_row."trade_date",'yyyy/mm/dd')||' close is '||t_row."close";
        DBMS_OUTPUT.PUT_LINE(ret);
    END LOOP;
    CLOSE c_code;
END;


-- for迭代行數據
DECLARE
    CURSOR c_code IS
        SELECT "ts_code","trade_date","close" FROM "code" WHERE "trade_date" > TO_DATE('20191001','yyyymmdd');
    t_row c_code%ROWTYPE;
    ret VARCHAR2(255);
BEGIN
    FOR row in c_code LOOP
        ret := row."ts_code"||' in '||TO_CHAR(row."trade_date",'yyyy/mm/dd')||' close is '||row."close";
        DBMS_OUTPUT.PUT_LINE(ret);
    END LOOP;
END;
引用遊標類型
declare
  type tp_curs is ref cursor;
  curs tp_curs;
  r_emp emp%rowtype;
begin
  open curs for select * from emp;
  loop
    fetch curs into r_emp;
    exit when curs%notfound;
    dbms_output.put_line(r_emp.ename||'--'||r_emp.sal);
  end loop;
end;

動態SQL

--	動態SQL
EXECUTE IMMEDIATE 	動態語句字符串
[INTO 變量列表]:		把查詢的結果保存到INTO後面的變量中
[USING 參數列表]USING爲語句中的參數傳值,格式是[:參數名]

BEGIN
    EXECUTE IMMEDIATE 'insert into table values(:date1,:date2)'
    [into v_ret] USING in 100,in 200;
END;

存儲與函數

區別與特性
  • 函數必須有返回值,包含return語句
  • 函數可以在查詢語句中直接調用,過程需要單獨調用
  • 函數一般用於計算,過程一般用於數據操作
  • 返回1個值時用函數,返回多個值用過程
參數
  • in 輸入參數,不可賦值
  • out輸出參數,可以賦值
  • in out,輸入或輸出,可以取值及賦值
使用過程
  • 命名標準,函數名sp_name,參數名p_attr,局部變量v_val
  • return 強制退出不執行後面代碼,返回null
set serveroutput on;

-- 創建過程
create or replace procedure sp_test(p_val in varchar2)
as
  v_val varchar2(24) := 'hello sp_test';
begin
  dbms_output.put_line(p_val);
  dbms_output.put_line(v_val);
end sp_test;

-- 調用存儲過程
begin
    sp_test('abc');
end;

-- 刪除過程
drop procedure sp_test;
使用函數
  • 命名標準,函數名fc_name,參數名p_attr,局部變量v_val
  • return null 強制退出後面代碼不執行
set serveroutput on;

-- 創建函數
set serveroutput on;

create or replace function fc_test(p_val in varchar2)
return number
as
  	v_val varchar2(24) := 'hello sp_test';
begin
  	dbms_output.put_line(p_val);
 	dbms_output.put_line(v_val);
  	return 1;
end fc_test;

-- 調用
begin
	dbms_output.put_line(fc_test('123'));
end;

-- 刪除
drop function fc_test;

結構
-- 包頭(包內全局變量)
create [or replace] package pg_name as
	類型;
	遊標;
	變量;
	函數;
	過程;
end [pg_name];

-- 包體
create [or replace] package body pg_name as
	函數;
	過程;
end [pg_name];
包頭
create or replace package pk_mypg as

  -- 變量
  n number(4);

  -- 遊標
  cursor c_emp is select * from emp;

  -- 類型
  type tp_cursor is ref cursor;
  function getempcurs return tp_cursor;

  -- 過程
  procedure mypg_sp_02(p_empno in emp.empno%type);

  -- 函數
  function mypg_fc_08(p_empno in emp.empno%type) return emp.sal%type;

end;
包體
create or replace package body pk_mypg as

  -- 返回遊標
  function getempcurs return tp_cursor as
    curs tp_cursor;
  begin
    open curs for
      select * from emp;
    return curs;
  end getempcurs;

  -- 執行sp_02函數
  procedure mypg_sp_02(p_empno in emp.empno%type) as
  begin
    sp_02(p_empno);
  end mypg_sp_02;

  -- 執行fc_08函數
  function mypg_fc_08(p_empno in emp.empno%type) return emp.sal%type as
  begin
    return fc_08(p_empno);
  end mypg_fc_08;

end;

調用
-- 調用
declare
  c_emp pk_mypg.tp_cursor;
  r_emp emp%rowtype;
begin
  -- 返回遊標
  c_emp:=pk_mypg.getempcurs;
  
  -- 迭代遊標
  loop
    fetch c_emp into r_emp;
    exit when c_emp%notfound;
    -- 打印
    dbms_output.put_line(r_emp.empno||'--'||r_emp.ename);
  end loop;
end;
  • 其他
    desc pk_mypg查看包結構

觸發器

結構
create or replace trigger name
before or after
delete or insert or update of [col]
on tbl
[for each row]
[when condition]
[declare]
begin
end;
行級觸發器
  • 寫日誌記錄
-- 寫日誌記錄
create or replace trigger modify_stu
after insert or delete or update of stu_name
on student_123
for each row 
begin
    if inserting then
      insert into stu_log_123 values(1,'insert',sysdate,:new.stu_name);
    elsif deleting then
      insert into stu_log_123 values(2,'delete',sysdate,:old.stu_name);
    elsif updating then
      insert into stu_log_123 values(3,'update_old',sysdate,:old.stu_name);
      insert into stu_log_123 values(4,'update_new',sysdate,:new.stu_name);
    end if;
end;
語句級觸發器
  • 控制對錶的修改
-- 控制對錶的修改
create or replace trigger banl_modify_stu
before insert or delete or update
on student_123
begin
  if inserting then
    raise_application_error(-20001,'該表不允許插入');
  elsif deleting then
    raise_application_error(-20002,'該表不允許刪除');
  elsif updating then
    raise_application_error(-20003,'該表不允許修改');
  end if;
end;

同步

  • merge
merge into emp1 e1
using emp e
on (e.empno = e1.empno)
when matched then
  update
     set e1.ename    = e.ename,
         e1.job      = e.job,
         e1.mgr      = e.mgr,
         e1.hiredate = e.hiredate,
         e1.sal      = e.sal,
         e1.comm     = e.comm,
         e1.deptno   = e.deptno
when not matched then
  insert
    (e1.empno,
     e1.ename,
     e1.job,
     e1.mgr,
     e1.hiredate,
     e1.sal,
     e1.comm,
     e1.deptno)
  values
    (e.empno, e.ename, e.job, e.mgr, e.hiredate, e.sal, e.comm, e.deptno);

日誌

例外

  • 異常直接跳到異常捕捉,中間代碼不執行
內部例外
-- 內部例外
declare 
  p number;
begin
  p := 1/0;
  exception
  when DUP_VAL_ON_INDEX then
       dbms_output.put_line('試圖向唯一索引列插入重複值');
  when INVALID_CURSOR then
       dbms_output.put_line('試圖進行非法遊標操作');
  when INVALID_NUMBER then
       dbms_output.put_line('試圖將字符串轉換爲數字');
  when NO_DATA_FOUND then
       dbms_output.put_line('SELECT INTO語句中沒有返回任何記錄');
  when TOO_MANY_ROWS then
       dbms_output.put_line('SELECT INTO語句中返回多於1條記錄');
  when ZERO_DIVIDE then
       dbms_output.put_line('試圖用0作爲除數');
  when CURSOR_ALREADY_OPEN then
       dbms_output.put_line('試圖打開一個已經打開的遊標');
  when others then
       dbms_output.put_line('其他');
end;
自定義例外
-- 自定義例外
declare
   p number;
   myex exception;
begin
   p:=0;
   if p = 0 then
     	raise myex;
   end if;
   
   exception 
     when myex 
          then dbms_output.put_line('自定義例外');
     when others 
          then dbms_output.put_line('其他例外');
end;
拋出系統異常
begin
	raise_application_error(-20001,'系統例外'); -- [20001-20999]
end;

其他

-- 解除內存限制
DBMS_OUTPUT.ENABLE(buffer_size => null);

-- 回車
chr(10);
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章