1.pl/sql編程
1.理解oracle的pl/sql的概念
2.掌握pl/sql編程技術(過程、函數、觸發器)
pl/sql是標准sql語句的擴展
簡介
1.過程、函數、觸發器都是由pl/sql編寫
2.過程、函數、觸發器是在oracle中
3.pl/sql是非常強大的過程語言
4.過程、函數等可以在java程序被調用
學習必要性:
1.提高應用程序的性能
2.模塊化的設計思想
3.減少網絡傳輸量
4.提高安全性
不好的方面:
移植性差
pl/sql可以使用變量和邏輯控制語句
可編寫:分頁存儲過程模塊,訂單處理存儲過程模塊,轉賬存儲過程模塊……
塊:block 由三部分構成
定義部分,從declare開始,可選;
執行部分,從begin開始,必須;
例外處理部分,從exception開始,可選;
實例1-只包括執行部分的pl/sql塊
set serveroutput on --打開輸出選項 begin dbms_output.put_line('hello'); end; --說明:dbms_output是oracle所提供的包(類似於java的開發包),該包包含一些過程,put_line就是dbms_output包的一個過程。
實例2-包含定義部分和執行部分的pl/sql塊
declare v_name varchar2(5); v_sal number(7,2); begin select ename,sal into v_name,v_sal from scott.emp where empno=&jingyu; dbms_output.put_line('員工名:'||v_name||' 員工薪水:'||v_sal); end; --如果返回值不惟一,需要用到參照變量;
實例3-包含定義部分,執行部分和例外處理部分
--例外處理部分,為了提高程序的健壯性,應該對可能的錯誤進行處理
oracle事先定義了一些例外,no_data_found就是找不到數據的例外。
上例就可以在end前加入如下異常處理部分代碼:
exception when no_data_found then dbms_output.put_line('數據不存在,請重新輸入!');
pl/sql編寫規范
1.注釋
單行注釋 --
多行注釋 /* */
2.標識符號的命名規范
1).定義變量時,用v_ 做前綴
2).定義常量時,用c_ 做前綴
3).定義游標時,用_cursor 做后綴
4).定義例外時,用e_ 做前綴
2.存儲過程
a.最簡單的存儲過程:
create or replace procedure sp_pro1 is begin insert into mytest1 values('shunping','m123'); end;
b.調用存儲過程:
exec sp_pro1;
c.存儲過程示例:
1.編寫一個存儲過程,實現功能:可以輸入雇員名,新工資,可以修改雇員的工資。
create or replace procedure sp_pro2(v_ename varchar2,v_newsal number) is begin update emp1 set sal=v_newsal where ename=v_ename; end;
2.演示java程序調用oracle的存儲過程
pl/sql第二講最后幾分鍾。
3.如何使用過程返回值
以后解決
3.函數
函數用於返回特定的數據,當建立函數時,在函數頭部必須包含return子句,而在函數體內必須包含return語句返回的數據。
案例1:輸入雇員的姓名,返回該雇員的年薪。
create function sp_fun1(spName varchar2) return number is yearSal number(7,2); begin select sal*12+nvl(comm,0)*12 into yearSal from emp1 where ename=spName; return yearSal; end;
sql>調用函數 var income number; call sp_fun1('SCOTT') into:income;
4.觸發器
觸發器是指隱含執行的存儲過程。當定義一個觸發器時,必須要指定觸發的事件和觸發的操作,常用的觸發事件包括insert,update,delete語句,而觸發操作實際就是一個pl/sql塊。
5.包
包用於在邏輯上組合過程和函數,它由包規范和包體兩部分組成。
實例1:創建包
create package sp_package is procedure update_sal(ename varchar2,newsal number); function annual_sal(name varchar2) return number; end;
--包的規范只包含了過程和函數的說明,沒有過程和函數的實現代碼。
包體用於實現包規范中的過程和函數
實例2:建立包體
create or replace package body sp_package is procedure update_sal(name varchar2,newSal number) is begin update emp1 set sal=newSal where ename=name; end; function annual_sal(name varchar2)return number is yearSal number; begin select sal*12+nvl(comm,0)*12 into yearSal from emp1 where ename=name; return yearSal; end; end;
實例3:如何調用包的過程和函數
調用過程:
call sp_package.update_sal('SMITH',133);
調用函數:
var abc number call sp_package.annual('SMITH') into:abc;
6.pl/sql基礎 -定義並使用變量
在編寫pl/sql程序時,可以定義變量和常量;在pl/sql程序中包括有:
①標量類型(scalar)
標量定義的案例
v_name varchar2(20); v_sal number(6,2); v_sal number(6,2):=5.4; v_hiredate date; v_valid boolean not null default false;
實例1:輸入員工號,顯示雇員姓名、工資、個人所得稅(稅率為0.03)。
declare c_taxRate number(3,2):=0.03; v_ename varchar2(5); v_sal number(7,2); v_taxSal number(7,2); begin select ename,sal into v_ename,v_sal from emp1 where empno=&no; v_taxSal:=v_sal*c_taxRate; dbms_output.put_line('雇員姓名:'||v_ename||' 工資:'||v_sal||' 個人所得稅:'||v_taxSal); end;
ORA-06502: PL/SQL: 數字或值錯誤 : 字符串緩沖區太小
當ename為MARTIN時,會報上述錯誤,解決方法,定義v_ename時,v_ename emp1.ename%type;
②復合類型(composite)
用於存放多個值的變量。主要包括這幾種
1)pl/sql記錄
類似於高級語言中的結構體(不知道結構體可以先簡單理解成類)
實例1:
declare --定義一個pl/sql類型,emp_record_type,類型包含3個數據,分別是name,salary,title type emp_record_type is record (name emp1.ename%type,salary emp1.sal%type,title emp1.job%type); sp_record emp_record_type; begin select ename,sal,job into sp_record from emp1 where empno=&no; dbms_output.put_line('雇員姓名:'||sp_record.name||' 雇員工資'||sp_record.salary); end;
2)pl/sql表
相當於高級語言中的數組,但是需要注意的是在高級語言中數組的下標不能為負數,而pl/sql是可以為負數的,並且表元素的下標沒有限制。
實例1:
declare
--定義一個pl/sql表類型,該類型用於存放emp1表中ename字段的這種數據 type emp_table_type is table of emp1.ename%type index by binary_integer; sp_table emp_table_type; begin select ename into sp_table(-1) from emp1 where empno=&no; dbms_output.put_line('名字是:'||sp_table(-1)); end;
3)嵌套表;
4)varray;
③參照類型(reference)
參照變量是指用於存放數值指針的變量,通過使用參照變量,可以使得應用程序共享相同對象,從而降低占用的空間。
在編寫pl/sql時,可以使用游標變量(ref_cursor)和對象類型變量(ref_obj_type)兩種參照變量類型。
1).游標變量(ref_cursor)
實例1:請使用pl/sql編寫一個塊,可以輸入部門號,顯示該部門所有員工的姓名及其工資。
--pl/sql參照變量 之游標變量 declare --定義一個游標類型 type sp_emp_cursor is ref cursor; --定義一個游標變量 test_cursor sp_emp_cursor; --定義變量 v_ename emp1.ename%type; v_sal emp1.sal%type; begin --將游標和select結合 open test_cursor for select ename,sal from emp1 where deptno=&no; --循環取出 loop fetch test_cursor into v_ename,v_sal; --判斷何時退出循環 應該是test_cursor為空時退出。 exit when test_cursor%notfound; dbms_output.put_line('名字:'||v_ename||' 工資'||v_sal); end loop; end;
實例2:在實例1基礎上,如果哪個員工的工資少於200元,就增加100元。
④lob(large object);
7.pl/sql的進階
控制結構
(1)使用if語句,
條件分支語句:if then;if then else;if then elsif then else;
案例1:編寫一個過程,可以輸入一個雇員名,如果該雇員的工資低於2000,就給該雇員工資增加10%。
--Method1: create procedure update_sal(name varchar2) is begin update emp1 set sal=1.1*sal where sal<2000 and ename=name; end;
--Method2: create or replace procedure update_sal2(name varchar2) is v_sal emp1.sal%type; begin select sal into v_sal from emp1 where ename=name; if v_sal<2000 then update emp1 set sal=1.1*sal where ename=name; --exec調用沒有問題;但call調用有問題,在下次查詢才會打印出來 dbms_output.put_line('此員工工資太低,已經給默認漲10%!'); end if; end;
案例2:編寫一個過程,輸入一個雇員名,如果他的補助不是0,就增加100.如果是0,就改成200;
create procedure update_comm(name varchar2) is v_comm emp1.comm%type; begin select comm into v_comm from emp1 where ename=name; if v_comm<>0 then update emp1 set comm=comm+100 where ename=name; else update emp1 set comm=200 where ename=name; end if; end;
案例3:編寫一個過程,輸入一個雇員編號,如果他是president,工資加1000,如果是manager,工資加500;如果是其他職位,工資加200;
create or replace procedure update_sal(spNo number) is --當上面參數寫empNo時,實驗是不可以的,過程可以成功創建,但是調用有問題。所以最好取不同名字。 v_job emp1.job%type; begin select job into v_job from emp1 where empno=spNo; if v_job='PRESIDENT' then update emp1 set sal=sal+1000 where empno=spNo; elsif v_job='MANAGER' then update emp1 set sal=sal+500 where empno=spNo; else update emp1 set sal=sal+200 where empno=spNo; end if; end;
(2)循環語句,
1.第一種循環結構:
loop
..
exit when v_num>=10;
end loop;
案例:user表中插入10條記錄,編號1-10,姓名"小明"。
create or replace procedure loop_insert_data(name varchar2) is v_num number:=1; begin loop insert into user1 values(v_num,name); --判斷退出條件 exit when v_num>=10; v_num:=v_num+1; end loop; end;
2.第二種循環結構:
while v_num<=20 loop
..
end loop;
案例:user表中插入10條記錄,編號11-20,姓名"小亮"。
create or replace procedure loop_insert_data2(name varchar2) is v_num number:=11; begin while v_num<=20 loop insert into user1 values(v_num,name); v_num:=v_num+1; end loop; end;
3.第三種循環結構:for循環(不推薦使用,不夠靈活)
begin for i in reverse 1..10 loop insert into user1 values (i,'順平'); end loop; end;
(3)控制語句goto和null;
goto不推薦使用,降低了程序的可讀性。
null 不執行任何操作,只是為了提高程序的可讀性。
pl/sql進階:綜合案例之編寫分頁的過程。
1).無返回值的存儲過程,向book表中添加書籍;
create table book(bookid number,bookname varchar2(50),publishhouse varchar2(50));
2).有返回值的存儲過程,可以輸入一個員工的編號,可以返回員工的姓名;
3).有返回值的存儲過程(列表[結果集]),輸入一個部門號,返回該部門所有員工的信息;
①創建一個包,定義一個類型test_cursor
②創建一個過程
③如何在java中調用
4).作業:有了上面的基礎,相信大家可以完成分頁的存儲過程了,要求:可以輸入表名,每頁顯示記錄數,當前頁。返回總記錄數,總頁數,和返回的結果集。
--1.建立一個包,定義一個游標的類型
create or replace package sp_package_fy as type sp_fy_cursor is ref cursor; end sp_package_fy;
--2.建立一個實現分頁的存儲過程_修正版
create or replace procedure sp_fenye(spTable in varchar2,spNum in number,spCurrentPage in number, spTotalRecord out number,spTotalPage out number,sp_cursor out sp_package_fy.sp_fy_cursor) is v_sql varchar2(1000); v_begin number:=spNum*(spCurrentPage-1)+1; v_end number:=spCurrentPage*spNum; begin --v_sql賦值 v_sql:='select * from (select rownum rn,t1.* from (select * from '||spTable||') t1 where rownum<='||v_end||') where rn>='||v_begin; --顯示指定分頁后詳細的信息 open sp_cursor for v_sql; --顯示總記錄數 v_sql:='select count(*) from '||spTable; execute immediate v_sql into spTotalRecord; --顯示總頁數 if mod(spTotalRecord,spNum) <> 0 then spTotalPage:=spTotalRecord/spNum+1; else spTotalPage:=spTotalRecord/spNum; end if; --關閉游標 --close sp_cursor; end; --在java程序中調用分頁存儲過程。以后學java時再分析。
例外處理
例外的分類:Oracle將例外分為預定義例外,非預定義例外和自定義例外3類。
(1)預定義例外:定義了oracle常見的錯誤;
data_not_found
case_not_found
cursor_already_open
invalid number
too_many_rows
zero_divide
logon_denied
timeout_on_resourse
(2)非預定義例外:用於處理預定義例外不能處理的例外;
略
(3)自定義例外:用於處理與oracle錯誤無關的其他情況。
實例:編寫一個存儲過程,輸入一個名字,工資加1000,如果沒有該人,拋出自定義例外。
8.oracle的視圖
視圖是一個虛擬表,其內容由查詢定義,同真實的表一樣,視圖包含一系列帶有名稱的行和列數據。但是視圖並不在數據庫中以存儲的數據集形式存在,行和列數據來自由定義視圖的查詢所引用的表。
視圖與表的區別
①表需要占用磁盤空間,視圖不需要;
②視圖不能添加索引;
③使用視圖可以簡化復雜查詢;(比如學生選課系統)
④視圖有利於提高安全性;(比如不同用戶查看不同視圖)
創建只讀視圖:create or replace view 視圖名 as select 語句 with read only;
刪除視圖:drop view 視圖名;