pl/sql編程

一、什麼是pl/sql

pl/sql(procedural language/sql)是Oracle在標準的sql語言上的擴展,pl/sql不僅允許嵌入Sql語言,還可以定義變量和常量,允許使用條件語句和循環語句,允許使用例外處理各種錯誤,這樣使得它的功能變得更加強大。

二、pl/sql學習的必要性

1、提高應用程序的運行性能。

2、模塊化的設計思想。

3、減少網絡傳輸量。

4、提高安全性。

三、編寫規範

1、註釋

單行註釋–

sql>select * from emp where empno=7788; --取得員工信息

多行註釋

/………………../

2、標識符號的命名規範

(1)當定義變量時,建議用v_作爲前綴 v_sal。

(2)當定義常量時,建議用c_作爲前綴 c_rate。

(3)當定義遊標時,建議用_cursor作爲後綴emp_cursor。

(4)當定義例外時,建議用e_作爲前綴 e_error。

四、塊

塊(block)是pl/sql的基本程序單元,編寫pl/sql程序實際上 就是編寫pl/sql塊。要完成相對簡單的應用功能,可能只需要編寫一個pl/sql快;但是如果想要實現複雜的功能,可能需要在一個pl/sql塊中嵌套其他的pl/sql快。

1、塊結構示意圖

pl/sql塊由三個部分構成:定義部分、執行部分、例外處理部分。

declear:定義部分是從declare開始的,這部分是可選的。定義常量、變量、遊標例外、複雜數據類型。

begin:執行部分是從begin開始的,這部分是必須的。

exception:例外處理部分是exception開始的,該部分可選的。

A、包含定義部分和執行部分的pl/sql塊

declare
v_ename varchar2(5); --定義字符串變量
begin
select ename into v_ename from emp where empno=&no;
dbms_output.put_line('僱員名:'||v_ename);
end;
/

相關說明:

& 表示要接收從控制檯輸入的變量

B、包含定義部分、執行部分和例外處理部分

爲了避免pl/sql程序的運行錯誤,提高pl/sql的健壯性,應該對可能的錯誤今夕處理,這個很有必要。

declare
v_ename varchar2(5); --定義字符串變量
begin
select ename into v_ename from emp where empno=&no;
dbms_output.put_line('僱員名:' || v_ename);
exception
when no_data_found then
dbms_output.put_line('輸入有誤');
end;
/

五、過程

過程用於執行特定的操作,當建立過程時,即可以指定輸入參數(in),也可以指定輸出參數(out),通過在過程中使用輸入參數,可以將數據傳遞到執行部分,通過使用輸出參數,可以將執行部分的數據傳遞到應用環境。在sqlplus中可以使用create procedure命令來建立過程。

實例:

請考慮編寫一個過程,可以輸入僱員們,新工資,可修改僱員的工資。

create procedure Update_Sal(spName varchar2,newSal number) is
begin
update emp set sal = newSal where ename = spName;
end;
/

如何調用過程有兩種方法:

A、EXEC Update_Sal('tom',6800);

B、CALL Update_Sal('tom',6800);

六、函數

函數用於返回特定的數據,當建立函數時,在函數頭部必須包含return子句,而在函數體內必須包含return語句返回的數據,我們可以使用create function 來建立函數。

案例:

create function annual_income(name varchar2)
return numeber is annual_salary number(7,2);
begin
select sal*12+nvl(comm,0)*12 into annual_salary from emp where ename=name;
return annual_salary;
end;
/

在sqlplus中調用函數

sql>var income number

sql>call annual_income('SCOTT') into: income;

sql>print income;

七、包

包用於在邏輯上組合過程和函數,它由包規範和包體兩部分組成

1、我們可以使用create package命令來創建包。

實例:

create package sp_package as
procedure update_sal(name varchar2,new sal number);
function annual_income(name varchar2) return number;
end;

包的規範只包含了過程和函數的說明,但是沒有過程和函數的實現代碼。包體用於實現包規範中的過程和函數。

2、建立包體可以使用create package body命令

實例:

create package body sp_package as
procedure Update_Sal(spName varchar2,new sal number) is
begin
update emp set sal = newSal where ename = spName;
end;
function annual_income(name varchar2)
return numeber is annual_salary number(7,2);
begin
select sal*12+nvl(comm,0)*12 into annual_salary from emp where ename=name;
return annual_salary;
end;
end;

3、如何調用包的過程或是函數

當調用包的過程或是函數時,在過程和函數前需要帶有包名,如果要訪問其他方案的包,還需要在包名前加方案名。

案例:

sql>call sp_package.update_sal('SCOTT',1500);

八、定義並使用變量

概述:在編寫pl/sql程序時,可以定義變量和常量。在pl/sql程序中包括有標量類型(scalar)、複合類型(composite)、參照類型(references)、lob(large object)。

1、標量(scalar)

在編寫pl/sql塊時,如果要使用變量,需要在定義部分定義變量。

pl/sql中定義變量和常量的語法如下:

identifier [constant] datatype [not null] [:=| default expr]

identifier:名稱

constant:指定常量。需要指定它的初始值,其其值是不能改變的。

datatype:數據類型

not null:指定變量值不能爲NULL

:=:給變量或是常量指定初始值

default:用於指定初始值

expr:指定初始值pl/sql表達式,可是文本值、其他變量、函數等。

標量定義的案例

(1)定義一個變長字符串

v_ename varchar2(10);

(2)定義一個小數,範圍-9999.99~9999.99

v_sal number(6,2);

(3)定義一個小數並給一個初始值爲5.4 :=是pl/sql的賦值號

v_sal2 number(6,2):=5.3;

(4)定義一個日期類型的數據

v_hiredate date;

(5)定義一個布爾變量,不能爲空,初始值爲false

v_valid boolean not null default false;

A、使用標量

在定義好變量後,就可以使用這些變量。這裏需要 說明的是pl/sql塊爲變量賦值不同於其他的編程語言,需要在等號前面加冒號(:=)。

下面以輸入員工號,顯示僱員姓名、工資、個人所得稅(稅率爲0.03)爲例,說明變量的使用,看看如何編寫。

declare
c_tax_rate number(3,2):=0.03;
v_ename varchar2(5);
v_sal number(7,2);
v_tax number(7,2);
begin
select ename,sal into v_ename,v_sal from emp where empno=&no;
v_tax:=v_sal*c_tax_rate;
dbms_output.put_line('姓名是:'||v_ename||' 工資是:'||v_sal||' 所得稅是:'||v_tax);
end;

B、使用%type類型

對於上面的pl/sql塊有一個問題:

就是如果員工的姓名超過了5字符的話,就會有錯誤,爲了降低pl/sql程序的維護工作量,可以使用%type屬性定義變量,這樣它會按照數據庫列來確定你定義的變量的類型和長度。

語法:標識符名 表名.列名%type;

案例:

declear
c_tax_rate number(3,2):=0.03;
v_ename emp.ename%type;
v_sal emp.sal%type;
v_tax number(7,2);
begin
select ename,sal into v_ename,v_sal from emp where empno=&no;
v_tax:=v_sal*c_tax_rate;
dbms_output.put_line('姓名是:'||v_ename||' 工資是:'||v_sal||' 所得稅是:'||v_tax);
end;

2、複合變量(composite)

用於存儲多個值的變量。主要包括這幾種pl/sql記錄、pl/sql表、嵌套表、varray。

1、pl/sql記錄

類似於高級語言中的結構體,需要注意的是,當引用pl/sql記錄成員時,必須要加記錄變量作爲前綴(記錄變量.記錄成員)。

案例:

declare
type emp_record_type is record(name emp.ename%type, salary emp.sal%type,title emp.job%type);
sp_record emp_record_type;
begin
select ename,sal,job into sp_record from emp where empno=7788;
dbms_output.put_line('員工名:'||sp_record.name);
end;

2、pl/sql表

相當於高級語言中的數組,但是需要注意的是在高級語言中數組的下標不能爲負數,而pl/sql是可以爲負數的,並且表元素的下標沒有限制。

案例:

declare
type sp_table_type is table of emp.ename%type index by binary_integer;
sp_table sp_table_type;
begin
select ename into sp_table(0) from emp where empno=7788;
dbms_output.put_line('員工名:'||sp_table(0));
end;

說明:

sp_table_type:是pl/sql表類型

emp.ename%type:指定了表的元素的類型和長度

sp_table:是pl/sql表變量

sp_table(0):表示下標爲0的元素

3、參照變量

概述:參照變量是指用於存放數值指針的變量,通過使用參照變量,可以使得應用程序共享相同對象,從而降低佔用空間。在編寫pl/sql程序時,可以使用遊標變量(ref cursor)和對象類型變量(ref obj_type)兩種參照變量類型。

1、遊標變量(ref cursor)

使用遊標時,當定義遊標時不需要指定相應的select語句,但是當使用遊標時(open時)需要指定select語句,這樣一個遊標就與一個select 語句結合了。

案例:

請使用pl/sql編寫一個塊,可以輸入部門號,並顯示該部門所有員工姓名和他的工資

declare
type sp_emp_cursor is ref cursor;
test_cursor sp_emp_cursor;
v_ename emp.ename%type;
v_sal emp.sal%type
begin
opent test_cursor for select ename,sal from emp where deptno=&no;
loop
fetch test_cursor into v_ename,v_sal;
exit when test_cursor%notfound
dbms_output.putline('姓名:'||v_ename||' 工資:'||v_sal);
endloop;
close test_cursor;
end;

九、控制結構

概述:在任何計算機語言(C、Java、Pascal)都有各種控制語句(條件語句、循環結構、順序控制結構),在pl/sql中也存在這樣的控制結構。

1、條件分支語句

pl/sql中提供了三種條件分支語句。if-then、if-then-else、if-then-elsif-else。

(1)簡單的條件判斷if-then

案例:編寫一個過程,可以輸入一個僱員名,如果該僱員的工資低於2000,就給該僱員工資增加10%。

create procedure IncreaseSal(spName varchar2) is
v_sal emp.sal%type;
begin
select sal into v_sal from emp where ename=spName;
if v_sal<2000 then
update emp sal=sal+sal*10% where ename=spName;
end if;
end;

(2)二重條件分支if-then-else

案例:編寫一個過程,可以輸入一個僱員名,如果該僱員的補助不是0,就在原來的基礎上增加100;如果補助爲0就把補助設爲200。

create procedure IncreaseComm(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;

(3)多重條件分支if-then-elsif-else

案例:編寫一個過程,可以輸入一個僱員編號,如果該僱員的職位是PRESIDENT就給他的工資增加1000,如果該僱員的職位是MANAGER就給他的工資增加500,其他職位的僱員工資增加200。

create procedure IncreaseSal(spNo number) is
v_job emp.job%type;
begin
select job into v_job from emp where empno=spNo;
if v_job='PRESIDENT' then
update emp set sal=sal+1000 where empno=spNo;
elsif v_job='MANAGER'
update emp set sal=sal+500 where empno=spNo;
else
update emp set sal=sal+200 where empno=spNo;
end if;
end;

2、循環語句

(1)基本循環

loop是pl/sql中最簡單的循環語句,這種循環語句以loop開頭,以end loop結尾,這種循環至少會被執行一次。

案例:編寫一個過程,可輸入用戶名,並循環添加10個用戶到Users表中,用戶編號從1開始增加。

create procedure AddUser(spName varchar2) is
v_num number(2):=1;
begin
loop
insert into Users values(v_num,spName);
exit when v_num=10;
v_num:=v_num+1;
endloop;
end;

(2)While循環

基本循環至少要執行循環體一次,而對於while循環來說,只有條件爲true時,纔會執行循環體語句,while循環以while 條件 loop開始,以end loop結束。

案例:

編寫一個過程,可輸入用戶名,並循環添加10個用戶到Users表中,用戶編號從11開始增加

create procedure AddUser(spName varchar2) is
v_num number(3):=11;
begin
while v_num<=20 loop
insert into Users values(v_num,spName);
v_num:=v_num+1;
endloop;
end;

(3)for循環

基本for循環的基本結構如下

begin
for i in reverse 1..10 loop
insert into Users values(i,'Kevin');
end loop;
end;

3、順序控制語句

(1)goto語句

goto語句用於跳轉到特定標號去執行有車,注意由於使用goto語句會增加程序的複雜度,並使得應用程序可讀性變差,所以在一般應用開發時,建議大家不要使用goto語句

基本語法如下:

goto label:其中label是已經定義好的標號名。
declare
i number(4):=1;
begin
loop
dbms_output.put_line("輸出i='||i");
if i=10 then
goto end_loop
end if;
i:=i+1;
endloop;
<<end_loop>>
dbms_output.put_line("循環結束");
end;

(2)null

null語句不會執行任何操作,並且會直接將控制傳遞到下一條語句。使用null語句的主要好處是可以提高pl/sql的可讀性。

declare
v_sal emp.sal%type;
begin
select sal into v_sal from emp where ename=spName;
if v_sal<2000 then
update emp sal=sal+sal*10% where ename=spName;
else
null;
end if;
end;

十、編寫分頁過程

概述:分頁時任何一個網站(bbs、網上商城、Blog)都會使用到的技術,因此學習pl/sql編程開發就一定要掌握該技術。

案例:編寫一個過程完成分頁。

–創建一個包,存放遊標。

create package ListPackage as
type page_cursor is ref cursor;
end;

–開始編寫分頁的過程

create procedure PagedList(
tableName in varchar2, --要查詢的表名
sortColumn in varchar2, --欲排序的列名
pagesize in number, --每頁顯示的記錄數
currentPage in number, --當前頁號
myRowCount out number, --表中總共的記錄數
myPageCount out number, --共有多少頁
p_cursor out ListPackage.page_cursor --遊標變量
) is
v_sql varchar2(1000);
v_begin number:=(currentPage-1)*pagesize+1;
v_end number:=currentPage*pagesize;
begin
v_sql:='select * from(select *, rownum rn from (select * from '||tableName||' order by '||sortColumn||') t1 where rownum<='||v_end||') where rn>='||v_begin;
open p_cursor for v_sql;
--計算myRowCount和myPageCount
v_sql:='select count(*) from '||tableName;
execute immediate v_sql into myRowCount;
if mod(myRowCount,pagesize)=0 then
myPageCount:=myRowCount/pagesize;
else
myPageCount:=myRowCount/pagesize+1;
end if;
close p_cursor;
end;

十一、例外處理

1、例外的分類

Oracle將例外分爲預定義例外、非預定義例外和自定義例外三種。

預定義例外:用於處理常見的Oracle錯誤。

非預定義例外:用於處理預定義例外不能處理的例外。

自定義例外:用於處理與Oracle錯誤無關的其他情況。

2、處理預定義例外

預定義例外時由pl/sql所提供的系統例外。當pl/sql應用程序違反了Oracle規定的限制時,則會隱含的觸發一個內部例外。pl/sql爲開發人員提供了二十多個預定義例外。我們給大家介紹常用的例外。

(1)預定義例外case_not_found

在開發pl/sql塊中編寫case語句時,如果在when子句中沒有包含必須的條件分支,就會觸發case_not_found的例外。

案例:

create procedure sp_pro6(spno number) is
v_sal emp.sal%type;
begin
select sal into v_sal from emp where empno=spno;
case
when v_sal<1000 then
update emp set sal=sal+100 where empno=spno;
when v_sal<2000 then
update emp set sal=sal+200 where empno=spno;
end case;
exception
when case_not_found then
dbms_output.put_line('case語句沒有與'||v_sal||'相匹配的條件');
end;

(2)預定義例外cursor_already_open

當從新打開已經打開的遊標時,會隱含的觸發例外cursor_already_open。

案例:

declare
cursor emp_cursor is select ename,sal from emp;
begin
open emp_cursor;
for emp_record1 in emp_cursor loop
dbms_output.put_line(emp_record1.ename);
end loop;
exception
when cursor_already_open then
dbms_output.put_line('遊標已經打開');
end;

(3)預定義例外dup_val_on_index

在唯一索引所對應的列上插入重複的值時,會隱含的觸發例外dup_val_on_index例外。

案例:

begin
insert into dept values(10.'公關部','北京');
exception
when dup_val_on_index then
dbms_output.put_line('在deptno列上不能出現重複值');
end;

(4)預定義例外invalid_cursor

當試圖在不合法的遊標上執行操作時,會觸發該例外。

例如:視圖從沒有打開的遊標提取數據,或是關閉沒有打開的遊標。則會觸發該例外。

declare
cursor emp_cursor is select ename,sal from emp;
emp_record emp_cursor%rowtype;
begin
fetch emp_cursor into emp_record;
dbms_output.put_line(emp_record.ename);
close emp_cursor;
exception
when invalid_cursor then
dbms_output.put_line('請檢查遊標是否打開');
end;

(5)預定義例外invalid_number

當輸入的數據有誤時,會觸發該例外。

案例:數字100寫成了1oo就會觸發該例外

begin
update emp set sal=sal+'1oo';
exception
when invalid_number then
dbms_output.put_line('輸入數字不正確');
end;

(6)預定義例外no_data_found

下面是一個pl/sql塊,當執行select into沒有返回行,就會觸發該例外。

declare
v_sal emp.sal%type;
begin
select sal into v_sal from emp where ename='&name';
exception
when no_data_found then
dbms_output.put_line('不存在該員工');
end;

(7)預定義例外too_many_rows

當執行select into語句時,如果返回超過了一行,則會觸發該例外。

declare
v_ename emp.ename%type;
begin
select ename into v_ename from emp;
exception
when too_many_rows then
dbms_output.put_line('返回了多行');
end;

(8)預定義例外zero_divide

當執行除0操作時,則會觸發給例外。

(9)預定義例外value_error

當在執行賦值操作時,如果變量的長度不足以容納實際數據,則會觸發該例外value_error。

案例:

declare
v_ename varchar2(5);
begin
select ename into v_ename from emp where empno=&no;
dbms_output.put_line(v_ename);
exception
when value_error then
dbms_output.put_line('變量尺寸不足');
end;

(10)其它預定義例外

A、login_denide

當用戶非法登錄時,會觸發該例外。

B、not_logged_on

如果用戶沒有登錄就執行dml操作,就會觸發該例外。

C、storage_error

如果超出了內存空間或是內存被損壞,就觸發該例外。

D、timeout_on_resourece

如果Oracle在等待資源時,出現超時就觸發該例外。

3、非預定義例外

非預定義例外用於處理與預定義例外無關的Oracle錯誤,使用預定義例外只能處理21個Oracle錯誤,而當使用pl/sql開發應用程序時,可能會遇到其他的一些Oracle錯誤。比如在pl/sql快中執行dml語句時,違反了約束規定等等。在這樣的情況下,也可以處理Oracle的各種例外。

4、處理自定義例外

預定義例外和非預定義例外都是Oracle錯誤相關的,並且處在的Oracle錯誤會隱含的觸發相應的例外。而自定義例外與Oracle錯誤沒有任何關聯,它是由開發人員爲特定情況所定義的例外。

案例:請編寫一個pl/sql塊,接收一個僱員的編號,並給該僱員工資增加1000元,如果該僱員不存在請提示。

create procedure IncreaseSal(spno number) is
myException exception;
begin
update emp set sal=sal+100 where empno=spno;
if sql%not_found then
raise myException;
end if;
exception
when myException then
dbms_output.put_line('沒有更新任何用戶');
end;

十二、Oracle視圖

概述:視圖是一個虛擬表,其內容由查詢定義,同真實表一樣,視圖包含一系列帶有名稱的列和行數據。但是,視圖並不在數據庫中以存儲的數據值集形式存在,行和列數據來自有定義視圖的查詢所引用的表,並且在引用視圖時動態生成。

1、視圖與表的區別

A、表需要佔用磁盤空間,視圖不需要。

B、視圖不能添加索引。

C、使用視圖可以簡化複雜查詢。

D、視圖有利於提高安全性。

2、創建視圖

create view 視圖名 as select語句[with read only]

3、創建或修改視圖

create or replace view 視圖名 as select語句[with read only]

4、刪除視圖

drop view 視圖名
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章