一、概述 pl/sql (procedural lanaguage/sql)是 oracle 在標準 sql 上的擴展 。不僅允許嵌入sql 語言,還可以定義變數和常量,允許使用條件語句和迴圈語句,允許使用例外處理錯誤。 -- 可以用來編寫過程,函數,和觸發器 -- 上述對象是放在資料庫中的 //數 ...
一、概述
pl/sql (procedural lanaguage/sql)是 oracle 在標準 sql 上的擴展 。不僅允許嵌入sql 語言,還可以定義變數和常量,允許使用條件語句和迴圈語句,允許使用例外處理錯誤。
-- 可以用來編寫過程,函數,和觸發器
-- 上述對象是放在資料庫中的 //資料庫端
-- 過程和函數可以在java程式中調用 ,觸發器只能被觸發,不能被調用
優點:
1.提高程式性能
2.模塊化的程式設計思想
3.減少網路傳輸流量
4.安全性高
5.能處理較複雜的sql操作
缺點:
1.移植性不好
2.可維護性差
3.不好調試
//例子 創建存儲過程 create procedure sp_adduser is //可以寫成 create or replace (如果存在就替換 ) begin insert into userinfo(userName) values ('zhangsan'); end;
如何調用?
exec 存儲過程名 -> exec sp_adduser
二、pl-sql 的簡單分類和編寫規範
簡單分類
1.過程(存諸過程)塊(編程的基本單位)
2.函數
3.觸發器
4.包
變數編寫規範
註釋 -- 或 /* */
變數 v_ // 比如 v_sal
常量 c_ // 比如 c_rate
游標 _cursor 為尾碼 //比如 emp_cursor
例外 e_ //比如 e_xxxerror
塊
塊(block) 是pl/sql 的基本程式單元,編寫pl/sql程式實際上就是編pl/sql塊. 簡單的功能可能只需一個塊,但是如果複雜,可能在一個塊中嵌套其他塊。
塊由三部分組成 定義部,執行部分,例外部分
如下
declear //可選,比如定義常量,變數,游標,複雜數據類型等 begin exception //可選 end;
//例一 只包擴執行部分的pl/sql塊 set serveroutput on //打開輸出選項,如果是off,則不會輸出,預設是off begin dbms_output.put_line('嘻嘻'); end; //說明 dbms_output 是 oracle 提供的包(類似java中的類),該包中有一個過程叫 put_line
//例二 包含定義部分和執行部分的pl/sql塊 declare v_ename varchar2(50) ; --定義字元串型變數,這個變數的長度要夠 begin select ename into v_ename from emp where empno =&no; -- // &no這種寫法是讓用戶可以手動輸入值 dbms_output.put_line('員工姓名'||v_ename); - -// ||是連接字元 end;
//例三 將上例改為,連薪水也列印出來,包含例外處理 declare v_ename varchar2(150) ; v_sal number(7,2); begin select ename,sal into v_ename ,v_sal from emp where empno =&no; dbms_output.put_line('員工姓名'||v_ename || '工資:'||v_sal); exception //進行例外處理 when no_data_found then dbms_output.put_line('沒有查詢到對應的數據'); end;
說明 : 輸入了不存在的員工號,會出錯,要進行例外處理
oracle 預先定義了一些例外
NO_DATA_FOUND 就是找不到數據的時候出現的例外
預定義例外: INVALID_CURSOR,ZERO_DIVIDE,VALUE_ERROR,INVALID_NUMBER .... //等共22種,都對應一個errorcod
過程
過程用於執行特定的操作,當建立過程的時候,即可指定輸入參數(in) ,也可以指定輸出參數 (out)
==通過輸入參數,可以將數據傳給過程的執行部分
==通過輸出參數,可以將執行部分的摟據傳給應用環境 (存儲過程的調用者)
可以用 create procedure 創建過程
例子
編寫過程,可以輸入員工名,新工資,可以修改員工的工資
調用過程 //exec 過程名
在java程式中調用一個過程
create procedure sp_01(p_name varchar2, p_sal number) is --//這裡不用指定長度 begin update emp set sal=p_sal where ename=p_name; end;
執行 exec sp_01('SCOTT',9090);
//在java中調用 public static void test() { Connection conn = null; CallableStatement stm = null; try { conn = DBUtil.getConn(); stm = conn.prepareCall("{call sp_01(?,?)}"); 如果是sa 登錄 要寫成 {scott.call sp_01(?,?)} stm.setString(1, "SCOTT"); stm.setInt(2, 10900); stm.execute(); System.out.println("---操作成功----"); } catch (Exception ex) { ex.printStackTrace(); } finally { DBUtil.close(null, stm, conn); } }
附: 驅動類名: oracle.jdbc.driver.OracleDriver ,連接字串 jdbc:oracle:thin:@localhost:1521:orcl
函數
函數用於返回特定的數據,當建立函數時,在函數頭部必須包括 return 子句,而在函數體內必須包含return語句返回的數據。我們可以使用 create function 來建立函數。
函數一般只返回一個值
//輸入員工的姓名,返回他的年薪 create function fun01(p_name varchar2) return number is -- 在函數頭部必須包括 return 子句 ,number 表示返回的是數值類型 totalsal number(8,2); begin select sal*12+nvl(comm,0)*12 into totalsal from emp where ename=p_name; return totalsal; end;
執行函數
SQL> var result number;
SQL> call fun01('SCOTT') into :result;
//在java程式中調用 public static void test2(){ Connection conn = null; CallableStatement stm = null; try { conn = DBUtil.getConn(); stm=conn.prepareCall("{?=call fun01('SCOTT')}"); //fun01 是函數名,SCOTT是函數要求的參數,? 是返回值的占位符 stm.registerOutParameter(1, java.sql.Types.VARCHAR); //聲要接收返回值,1 代表第一個問號 java.sql.Types.VARCHAR 代表返回值的類型 stm.execute(); String result= stm.getString(1); //取出返回值 System.out.println("年薪是:"+result); } catch (Exception ex) { ex.printStackTrace(); } finally { DBUtil.close(null, stm, conn); } }
包
包用於在邏輯上組合過程和函數,由包規範和包體組成
1) 可以使用 create package 命令來創建包
create package sp_package_01 is procedure sq_update_sal(name varchar2,newsal number); --//只有聲明,沒有實現 function fun02(name varchar2) return number; --//這個函數有一個 number類型的返回值 end;
包的規範只包含了過程和函數的說明,但沒有實現
2) 建立包體 create package body 命令
create package body sp_package_01 is procedure sq_update_sal(name varchar2,newsal number) is begin update emp set sal=newsal where ename=name; --//更新工資 end;
function fun02(name varchar2) return number is totalsal number(8,2); begin select sal*12+nvl(comm,0)*12 into totalsal from emp where ename=name; return totalsal; end;
//顯示錯誤信息 show errors
如何調用包里的內容,要用包名,如果是其他方案的,要有方案名.
//執行 exec sp_package_01.sq_update_sal('SCOTT',5000);
變數-之標量
在編寫pl/sql 的時候,可以定義變數和常量,包括
== 標量類型(scalar)
== 複合類型(composite)
== 參照類型(reference)
== lob (large object) //大對象
標量(scalar)
1) 常用類型的標量的定義
標量主要用來放單個數據
如果要使用變數,要在定義部分定義
語法
identifier [constant] datatype [not null] [:=| default expr]
identifier:名稱
constant: 指定它是常量, 需要指定初值,其值不能變
datatype: 數據類型
not null: 表示不能為空
default: 初值
expr: 指定初始值的pl/sql 表達式,可以是文本,其他變數,函數等
例子
-- 定義一個變長字元串
v_empnam varchar2(10)
-- 定義一個小數 值在 -9999.99 - 9999.99 之間
v_sal number(6,2)
-- 定義一個小數,並給初值為 3.14
v_pai number(3,2) :=3.14 // := 賦值符號
-- 定義一個日期類型的變數
v_hiredate data
-- 定義一個布爾型變數,不能為空,初值為false
v_result boolean not null default false
2)標量的使用
例子 輸入員工號,顯示員工工資,姓名,個人所得稅(稅率 0.02)
set serveroutput on ; declare c_tax_rage number(3,2):=0.02 ; --//稅率,是常量,必須給初值 v_ename varchar2(50); v_sal number(8,2); v_tax_sal number(8,2); --//應交的所得稅 begin select ename ,sal into v_ename,v_sal from emp where empno=&no; v_tax_sal:=v_sal*c_tax_rage; --//所得稅=工資* 稅率 dbms_output.put_line('姓名'||v_ename||'稅'||v_tax_sal); end ;
3) 使用 %type 類型定義標量
上例存在一個問題
如果員工的姓名超過了50 ,會報錯 字元串緩衝區太小
可以使用 %type 屬性定義標量,它會根據你的資料庫中列的長度和類型來定義標量
標識符名稱 表名 列名 %type
比如上例中 可以 v_ename emp.ename%type
變數-之複合變數
複合類型變數(composite) 好比數組
用於存放多個值
-- pl/sql 記錄
-- pl/sql 表名
-- 嵌套表
-- varry //動態數組
1) pl/sql 記錄
類似高級語言中的結構體 ,當引用 pl/sql 記錄成員時,必須要加記錄變數做為首碼(記錄變數.記錄名)
declare type emp_recorder_type is record --//聲明一個複合類型變數 ( name emp.ename%type, salary emp.sal%type, title emp.job%type ); sp_record emp_recorder_type; --//定義了一個複合類型的變數,名稱是sp_record,類型是 emp_recorder_type begin select ename,sal,job into sp_record from emp where empno=7788; dbms_output.put_lne('員工名'||sp_record.name||'工資'||sp_record.salary||'工作'||sp_record.title); end;
2) pl/sql 表名
相當於數組 ,但它的下標可以負值,並且元素的下標沒有限制
declare type sp_table_type is table of emp.ename%type --//定義一個 sp_table_type 自定義類型,用於存放 emp.ename%type 類型的數據 index by binary_integer ; --//讓這個表的下標是按整數來排序的 sp_table sp_table_type; --//定義了一個 sp_table_type 類型的變數,名字是 sp_table begin select ename into sp_table(0) from emp where empno='7788'; --//目前只放了一條數據,如果是多條會報錯 dbms_output.put_line('員工名'||sp_table(0)); --//這個下標是負數也可以 end;
變數-之參照類型變數 (reference)
用於存放數值指針的變數,通過它可以使得應用程式共用相同的對象,降低占用空間。
有以下兩種
游標類型 (ref cursor)
對象類型 (ref obj_type) //不講
1) 參照類型 -- ref cursor 游標變數
定義游標
用的時候(open ),要指定select 語句,這樣一個游標就和select語句關聯了,需求 寫一個pl/sql語句塊,可以輸入部門號,並顯示該部門的所有員工姓名和工資。如果某個員工的工資低於5000,就增加10000 元。
declare type sp_cursor is ref cursor ; --//定義一個游標類型叫 sp_cursor v_ename emp.ename%type; v_sal emp.sal%type; test_cursor sp_cursor; --//定義一個游標類型的變數,名叫 test_cursor begin open test_cursor for select ename,sal from emp where deptno=&no; --//打開游標 ,要指定一個select語句 loop fetch test_cursor into v_ename,v_sal; exit when test_cursor % notfound; dbms_output.put_line(v_ename||v_sal); if v_sal<3000 then update emp set sal=v_sal+10000 where ename=v_ename; end if; end loop; close test_cursor; end if;
end;
控制結構語句
條件分支語句
if then
if then else
if then elsif else //註意,不是elseif
迴圈語句
loop ... end loop
while ... loop end loop
for
控制語句
goto 語句
null 語句
1)條件分支語句
== if then
編寫一個過程,可以輸入一個員工名,如果工資小於5000,則再扣3000
和上例相似
if v_sal<5000 then
update emp set sal=v_sal-3000 where ename=v_ename;
end if;
== if then else //二重條件分支
輸入一個員工名,如果補助不是0 就在原來的基礎上增加100,如果為0 就把補助改成200
create or replace procedure sp_02 (spName varchar2) is v_comm emp.comm%type; begin select comm into v_comm from emp where ename=spName; if v_comm <>0 then update emp set comm=comm+100 where ename=spName; else update emp set comm=200 where ename=spName; end if; end;
執行: exec sp_02('SCOTT');
== 多重條件分支
if then elsif else
編寫一個過程,可以輸入一個員工編號,如果職位是 CLERK 給工資加 100 ,SALESMAN 給加工資 500,其他加 200
create or replace procedure sp_03(sp_no number) is v_job emp.job%type; begin select job into v_job from emp where empno=sp_no; if v_job='CLERK' then update emp set sal=sal+100 where empno=sp_no; elsif v_job='SALESMAN' then update emp set sal=sal+500 where empno=sp_no; else update emp set sal=sal+200 where empno=sp_no; end if; end;