PL/SQL程式設計
pl/sql(procedural language/sql)是Oracle在標準的sql語言上的擴充套件。pl/sql不僅允許嵌入式sql語言,還可以定義變數和常量,允許使用條件語句和迴圈語句,允許使用例外處理各種錯誤。這樣使得他的功能變的更強大。缺點是移植性不好。
編寫一個儲存過程,向表中新增資料。
- create table mytest (name varchar2(30),passwd varchar2(30));
- create or replace procedure xxc_pro1 is
begin
insert into mytest values ('小紅','m123');
end;
- 呼叫過程 exec 過程名(引數1,引數2…)或call 過程名引數1,引數2…)
① exec xxc_pro1; 或者是
② call xxc_pro1;
pl/sql可以做什麼?
塊:包括過程、函式、觸發器、包。
編寫規範:
- 註釋 --:單行註釋
eg:select * from emp where empno=7788;--取得員工資訊
/*……*/多行註釋
- 表示符號(變數)的命名規範:
① 當定義變數時,建議用v_作為字首:v_ename
② 當定義常量時,建議用c_作為字首:c_rate
③ 當定義遊標時,建議用_cursor作為字尾:emp_cursor
④ 當定義例外時,建議用e_作為字首:e_error
塊(block)是pl/sql的今本程式單元,編寫pl/sql程式實際上就是在編寫pl/sql塊;pl/sql塊由三部分組成:定義部分,執行部分,例外處理部分。
declare --可選部分
/*定義部分:定義常量,變數,遊標,例外,複雜資料型別*/
begin --必選部分
/*執行部分:要執行的pl/sql語句和sql語句*/
exception --可選部分
/*例外處理部分:處理執行的各種錯誤*/
例項1:只包含執行部分的pl/sql塊
SQL> set serveroutput on --開啟輸出
SQL> begin
2 dbms_output.put_line('hello');
3 end;
4 /
說明:dbms_output是oracle提供的包,該包包含一些過程,put_line就是其中之一。
例項2:包含定義部分和執行部分
SQL> declare
2 v_ename varchar2(5);
3 begin
4 select ename into v_ename from emp where empno = &no;
5 dbms_output.put_line('僱員名'||v_ename);
6 end;
7 /
說明:&:從控制檯輸入變數,會彈出一個對話方塊。
例項3.同時輸出僱員名和工資
SQL> declare
2 v_ename varchar2(20);
3 v_sal number(10,2);
4 begin
5 select ename,sal into v_ename,v_sal from emp where empno=&no;
6 dbms_output.put_line('僱員名:'||v_ename||' 工資:'||v_sal);
7 end;
8 /
包含定義,執行,和例外處理的pl/sql塊。
例項4.當輸入的員工號不存在時
SQL> declare
2 v_ename varchar2(20);
3 v_sal number(10,2);
4 begin
5 select ename,sal into v_ename,v_sal from emp where empno =&no;
6 dbms_output.put_line('僱員名:'||v_ename||' 工資:'||v_sal);
7 exception --異常處理部分
8 when no_data_found then
9 dbms_output.put_line('請輸入正確的員工號!');
10 end;
11 /
以上為塊的基礎,下面來介紹塊的各個組成:過程,函式,觸發器,包。
過程
過程用於執行特定的操作,當執行過程的時候,可以指定輸入引數(in),也可以指定輸出引數(out)。通過在過程中使用輸入引數,可以講資料輸入到執行部分,通過使用輸出引數,可以將執行部分的資料輸出到應用環境,在pl/sql中可以使用create procedure命令來建立過程。
編寫一個儲存過程,可以輸入僱員名和新工資來改變員工工資。
--案例
create or replace procedure xxc_pro3(newname in varchar2,newsal in number) is
begin
update emp set sal=newsal where ename=newname;
end;
呼叫 exec xxc_pro3(‘SCOTT’,2900);[A1]
--in表示是輸入引數,可以不寫,預設是in,但out必須寫。
在java程式中呼叫儲存過程來修改工資;
//演示java程式呼叫oracle中的儲存過程
package TestOraPro;//根據不同的包,包名不同
import java.sql.*;
public class TestOraPro {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
try{
//載入驅動
Class.forName("oracle.jdbc.driver.OracleDriver");
//得到連線 1521為埠號
Connection ct = DriverManager.getConnection("jdbc:oracle:thin:@127.0.0.1:1521:CUIXIAO2","scott","xxc");
//建立callablestatement
CallableStatement cs=ct.prepareCall("{call xxc_pro3(?,?)}");
//給?賦值
cs.setString(1, "SMITH");
cs.setInt(2,2600);
//執行
cs.execute();
}catch(Exception e){
e.printStackTrace();
} finally{
//關閉資源
cs.close();
ct.close();
}
}
}
以上為過程基礎,後會詳細講。
函式
函式用於返回特定的資料,當建立函式時,在函式頭部必須包含return子句,而在函式體內必須包含return語句來返回資料,可以使用create function來建立函式。
案例1.返回工人年工資。
--函式案例
create or replace function xxc_fun1(newname varchar2)
return number is yearSal number(10,2);
begin
select sal*12+nvl(comm,0)*12 into yearSal from emp where ename=newname;
return yearSal;
end;
在sqlplus中呼叫函式
sql>var income number;
sql>call xxc_fun1(‘SCOTT’) into: income;
sql>print income;
在java中呼叫函式
select xxc_fun1(‘SCOTT’) from dual;
這樣可以通過rs.setInt(1)得到返回結果。
包
包用於在邏輯上組合過程和函式,它由包規範和包體兩部分組成。
1.我們可以用create(or replace) package命令來建包;
例項:create package xxc_package is
Procedure update_sal (name varchar2,newsal number);
Function annual_sal(name varchar2) return number;
End;
包規只包含過程和函式的說明,但是沒有函式和過程的實現程式碼。[A2]
包體用於實現包規範中的過程和函式。
2.建立包體可以用create package body命令。
create package body xxc_package is
Procedure update_sal(name varchar2,newsal number) is
begin
update emp set sal=newsal where ename=name;
end;
function annual_sal(name varchar2) return number is
annual_salary number;
begin
select sal*12+nvl(comm,0)*12 into annual_salary from emp where ename=name;
return;
end;
end;
[A3] 3.如何呼叫包中的過程和函式。
在過程和函式前加上包名。
exec xxc_package.update_sal(‘SCOTT’,120);
觸發器
觸發器是指隱含的儲存過程,當定義觸發器是,必須指定觸發事件和觸發的操作,常用的觸發操作有insert、update和delete。觸發器操作實際上是一個pl/sql塊,可以使用create trigger命令來建立觸發器。因為觸發器內容很多,會在後面詳細介紹,觸發器是非常有用的,可以用來維護資料庫的安全和一致性。
在編寫pl/sql塊時可以定義的變數和常量;
- 標量型別(scalar);
- 複合型別(composite);
- 參照型別(reference);
- lob(large ofject);
標量型別(scalar)—常用型別
語法格式(即前邊最常見的格式)
變數名 [constant] datatype [not null] [:=/default expr]
expr:指定初始值的pl/sql表示式,可以是文字,其他變數,函式等。
定義標量案例
① 定義一個變長字串
V_ename varchar2(10);
② 定義一個小數,範圍在-9999.99~9999.99
v_sal number(6,2);
③ 定義一個小數並賦初值 :=是pl/sql的賦值符號;
v_sal number(6,2):=12.3;
④ 定義一個日期型別數
v_birthday date;
⑤ 定義一個布林型別變數,不能為空,預設是false;
v_valid boolen not null default false;
[A4] 下面以輸入員工號,顯示員工姓名,工資,個人所得稅(稅率為0.03)為例,說明變數的使用。
declare
c_tax_rate number(3,2):=0.03;
v_ename varchar2(5);
v_sal number(10,2);
v_tax_sal number(10,2);
begin
select ename,sal into v_ename,v_sal from emp where empno=&no;
v_tax_sal:=v_sal*c_tax_rate;
dbms_output.put_line(‘員工名:’||v_ename||’工資:’||v_sal||’繳稅:’||v_tax_sal);
end;
對於上邊的pl/sql塊,有一個問題。
如果員工的名字長於5個字元,就會出現錯誤,為了降低pl/sql的維護工作量,可以使用%type屬性來定義變數,這樣它會按照資料庫列來確定你定義的變數的型別和長度。
變數名 表名.列名%type
對於上例的改動
v_ename emp.ename%type;
v_sal emp.sal%type;
複合型別(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);[A5]
xxc_record emp_record_type;
begin
select ename,sal,job into xxc_record from emp where empno=&no;
dbms_output.put_line(‘員工名:’||xxc_record.name||’工資:’||xxc_record.salary);
end;
2.pl/sql表
相當於高階語言中的陣列,需要注意的是高階語言中的陣列的下標不能為負,但表的下標沒有限制,可以為負。[A6]
例如:declare -- binary_integer表示下標是整數
type xxc_table_type is table of emp.ename%type index by binary_integer;
xxc_table xxc_table_type;
begin --下標0可以是任何整數,-1,-2,23都可以,但輸出的時候也要是同一個數,不然會出錯。
select ename into xxc_table(0) from emp where empno=7788;
dbms_output.put_line(‘員工名:’||xxc_table(0));
end;
在這個例項中,如果把where子句去掉,就會出錯,因為你只請求了一個資料,而返回了多行資料。解決辦法是使用參照變數。
參照變數
參照變數是指用於存放資料指標的變數,通過使用參照變數可以使得應用程式共享相同的物件,從而降低佔用的空間。在編寫pl/sql程式時,可以使用遊標變數(ref cursor)和物件型別變數(ref obj_type)這兩種參照變數,用得多的是遊標變數。
遊標變數
定義遊標是,不需要指定select語句,但當使用遊標(open)時,需要指定select語句,這樣一個遊標就與一個遊標就與一個select語句結合了。
例項:① 編寫一個pl/sql塊,輸入部門號,顯示所有員工名和工資。
declare
type xxc_emp_cursor is ref cursor;
test_cursor xxc_emp_cursor;
v_ename emp.ename%type;
v_sal emp.sal%type;
begin
open 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.put_line(‘名字:’||v_ename||’工資:’||v_sal);
end loop;
close test_cursor;
[A7] end;
② 在① 的基礎上,如果員工的工資低於2000,加100
declare
type xxc_emp_cursor is ref cursor;
test_cursor xxc_emp_cursor;
v_ename emp.ename%type;
v_sal emp.sal%type;
begin
open 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;
if v_sal<2000 then
update emp set sal=sal+100 where ename=v_ename;
v_sal=v_sal+100;
end if;
dbms_output.put_line(‘名字:’||v_ename||’工資:’||v_sal);
end loop;
close test_cursor;
end;
[A8] Pl/sql分支控制語句
1.條件分支語句
if…then if…then…else if…then…eslif…then…else…
當if語句結束時要有end if。
不等於號是<>,不是!=,
if job=’MANAGER’,是單引號,不是雙引號。
2.迴圈語句
--loop是pl/sql中最簡單的迴圈語句,這種迴圈以loop開始,以end loop結束,此迴圈至少會被執行一次。
例:編寫一個迴圈過程,可輸入使用者名稱,並迴圈新增10個使用者到user表中,使用者編號從1開始。
Sql>create table user(userid number(10),username varchar2(20));
Sql>create procedure xxc_insert_user1(name varchar2) is
declare
v_Num number:=1; --不用指定大小嗎?
begin
loop
insert into user values(v_num,name);
exit when v_num=10;[A9]
v_num:=v_num+1; --Oracle中沒有++符號
end loop;
end;
--while迴圈
當while條件為真時,執行迴圈體。
以while…loop開頭,以end loop結尾。
例:上題,使用者號從11開始,新增10名使用者。
create procedure xxc_insert_user2(name varchar2) is
declare
v_Num number:=11; --不用指定大小嗎?
begin
while v_num<=20 loop
insert into user values(v_num,name);
v_num:=v_num+1;
end loop;
end;
--for迴圈
迴圈基本結構如下:
begin
for i in reverse 1..10 loop
insert into users(I,’小紅’);
end loop;
end;
順序控制語句 --goto,null
Goto語句會增加程式的複雜性,降低可讀性,一般不使用,但有時會很方便。
基本語法 goto lable ,lable是定義好了的標籤。
例:declare
i int:=1;
begin
loop
dbms_output.put_line(‘i=’||i);
if i=10 then
goto end_loop;
end if;
end loop;
<<end_loop>> --此為標籤,注意格式
dbms_output.put_line(‘迴圈結束’);
end;
null語句不執行任何操作,好處是可以提高程式的可讀性。
例:if sal<2000 then
update emp set sal=sal+100 where ename=name;
else
null;
編寫分頁過程
從易到難,先寫簡單的過程。
寫無返回值的過程,並被Java程式呼叫,前邊已寫過,不再重寫。現在寫一個有返回值的過程,並用Java程式呼叫。
例:輸入員工號,輸出員工姓名。
Create procedure xxc_pro4 (empid in number,empname out varchar2) is
Begin
Select ename into empname from emp where empno = empid;
End;
Java程式呼叫。
import java.sql.*;
public class TestOraPro1 {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
try{
//載入驅動
Class.forName("oracle.jdbc.driver.OracleDriver");
//得到連線 1521為埠號
Connection ct = DriverManager.getConnection("jdbc:oracle:thin:@127.0.0.1:1521:CUIXIAO2","scott","xxc");
//建立callablestatement
CallableStatement cs=ct.prepareCall("{call xxc_pro4(?,?)}");
//給第一個?賦值
cs.setInt(1,7788);
//給第二個?賦值
cs.registerOutParameter(2,oracle.jdbc.OracleTypes.VARCHAR);
//執行
cs.execute();
//取出返回值,要注意?的順序,第幾個?是返回值就寫幾。
String name = cs.getString(2);
System.out.println(“7788的名字是”+name);
}catch(Exception e){
e.printStackTrace();
}finally{
//關閉資源
cs.close();
ct.close();
}
}
}
案例擴充套件:輸入一個員工號,輸出該員工的名字,工資和崗位。
create procedure xxc_pro5(empid in number,empname out varchar2,empsal out number,empjob out varchar2) is
begin
select ename,sal,job into empname,empsal,empjob from emp where empno=empid;
end;
Java呼叫
import java.sql.*;
public class TestOraPro1 {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
try{
//載入驅動
Class.forName("oracle.jdbc.driver.OracleDriver");
//得到連線 1521為埠號
Connection ct = DriverManager.getConnection("jdbc:oracle:thin:@127.0.0.1:1521:CUIXIAO2","scott","xxc");
//建立callablestatement
CallableStatement cs=ct.prepareCall("{call xxc_pro4(?,?,?,?)}");
//給第一個?賦值
cs.setInt(1,7788);
//給後3個?賦值
cs.registerOutParameter(2,oracle.jdbc.OracleTypes.VARCHAR);
cs.registerOutParameter(3,oracle.jdbc.OracleTypes.DOUBLE);
cs.registerOutParameter(4,oracle.jdbc.OracleTypes.VARCHAR);
//執行
cs.execute();
//取出返回值,要注意?的順序,第幾個?是返回值就寫幾。
String name = cs.getString(2);
String job = cs.getString(4);
System.out.println(“7788的名字是”+name+” 工作是”+job);
}catch(Exception e){
e.printStackTrace();
}finally{
//關閉資源
cs.close();
ct.close();
}
}
}
帶有返回值的儲存過程(結果集是個列表)。
案例:編寫個儲存過程,輸入部門號,輸出該部門的所有員工的資訊。
分析如下:
由於Oracle的儲存過程沒有返回值,它的所有返回值都是通過out引數來替代的,列表同樣也不例外,但由於是個集合,所以不能用一般引數,必須要用package。所以分為兩部分。
① 建立一個包,在包中定義一個test_package,是個遊標;
create package test_package as type test_cursor is ref cursor;
End test_package;
② 建立儲存過程
create procedure xxc_pro6(xxcno in number,xxc_cursor out test_package.test_cursor) is
begin
open xxc_cursor for select * from emp where deptno=xxcno;
end;
在Java中呼叫此儲存過程
import java.sql.*;
public class TestOraPro1 {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
try{
//載入驅動
Class.forName("oracle.jdbc.driver.OracleDriver");
//得到連線 1521為oracle埠號
Connection ct = DriverManager.getConnection("jdbc:oracle:thin:@127.0.0.1:1521:CUIXIAO2","scott","xxc");
//建立callablestatement
CallableStatement cs=ct.prepareCall("{call xxc_pro6(?,?)}");
//給第一個?賦值
cs.setInt(1,10);
//給第二個?賦值
cs.registerOutParameter(2,oracle.jdbc.OracleTypes.CURSOR);
//執行
cs.execute();
//得到結果集
ResultSet rs = (ResustSet)cs.getObject(2);
While(rs.next()){
System.out.println(rs.getInt(1)+” ”+rs.getString(2)+” ”+rs.getString(3));
}
}catch(Exception e){
e.printStackTrace();
}finally{
//關閉資源
cs.close();
ct.close();
}
}
}
開始編寫分頁過程。
例:請編寫一個儲存過程,要求可以輸入表名、每頁顯示記錄數、當前頁,返回總記錄數、總頁數和返回的結果集。
現在先回顧一下分頁的語句,這是個模板。
select * from (select t1.*,rownum rn from (select * from emp) t1 where rownum<=10) where rn>=6;
① 建立一個包,在包中定義一個test_package2,是個遊標;
create package test_package2 as type test_cursor2 is ref cursor;
End test_package2;
② 建立儲存過程;
create procedure fenye is
(tablename in varchar2, --表名
rowsize in number, --每頁記錄數
pagenow in number, --當前頁
recordnum out number, --總記錄數
pagenum out number, --總頁數
set_cursor out test_package2.test_cursor2 --結果集的遊標
) is
v_sql varchar2(1000);
v_begin nuber:=(pagenow-1)*rowsize+1;
v_end nuber:=pagenow*rowsize;
begin
--v_sql是連線字串,有點繞,看仔細
v_sql := 'SELECT * FROM (SELECT T.*,ROWNUM rn FROM (SELECT * FROM ' ||
TABLENAME || ' ) T WHERE ROWNUM <= ' || V_END ||
' ) WHERE rn >= ' || V_BEGIN;
--開啟遊標,把遊標和sql語句結合起來
open set_cursor for v_sql;
--計算recordnum和pagenum
v_sql:= 'SELECT COUNT(*) FROM ' || TABLENAME;
execute immediate v_sql into recordnum;
if mod(recordnum,rowsize)=0 then
pagenum:=recordnum/rowsize;
else
pagenum:=recordnum/rowsize+1;
end if;
close set_cursor;
end;
用Java程式呼叫:
import java.sql.*;
public class TestOraPro1 {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
try{
//載入驅動
Class.forName("oracle.jdbc.driver.OracleDriver");
//得到連線 1521為埠號
Connection ct = DriverManager.getConnection("jdbc:oracle:thin:@127.0.0.1:1521:CUIXIAO2","scott","xxc");
//建立callablestatement
CallableStatement cs=ct.prepareCall("{call fenye(?,?,?,?,?,?)}");
//給?賦值
cs.setString(1,”emp”);
cs.setInt(2,5);
cs.setInt(3,1);
//註冊總記錄數
cs.registerOutParameter(4,oracle.jdbc.OracleTypes.INTEGER);
//註冊總頁數
cs.registerOutParameter(5,oracle.jdbc.OracleTypes.INTEGER);
//註冊結果集
cs.registerOutParameter(6,oracle.jdbc.OracleTypes.CURSOR);
//執行
cs.execute();
//取出返回值,要注意?的順序,第幾個?是返回值就寫幾。
int rnum=cs.getInt(4);
int pnum=cs.getInt(5);
ResultSet rs=(ResultSet)cs.getObject(6);
System.out.println(”記錄數是:”+rnum+” 總頁數是:”+pnum);
while(rs.next()){
System.out.println(”編號:”+rs.getInt(1)+”姓名:”+rs.getString(2));
}
}catch(Exception e){
e.printStackTrace();
}finally{
//關閉資源
cs.close();
ct.close();
}
}
}
例外處理
例外處理分為預定義例外,其他預定義例外和自定義例外。
案例:當輸入的僱員編號不存在時
declare
v_ename emp.ename%type;
begin
select ename into v_name from emp where empno=&no;
dbms_output.putline(’名字是:’||v_name);
exception
when no_data_found then
dbms_output.putline(’編號不存在!’);
end;
no_data_found就是一個預定義例外,在執行select語句時會出異常,但在執行update等語句時不會觸發該例外。
預定義例外是由pl/sql所提供的系統例外。當pl/sql違反Oracle的規定時就會隱含觸發一個內部例外。
常用的例外:
- case_no_found 當編寫pl/sql塊中編寫case語句時,如果在when子句中沒有包含必須的條件分支,就會觸發case_no_found例外。
例:create or replace procedure xxc_pro7(no number) is
v_sal emp.sal%type;
begin
select sal into v_sal from emp where empno=no;
case
when v_sal<1000 then
update emp set sal=sal+200 where empno=no;
when 1000<=v_sal<2000 then
update emp set sal=sal+300 where empno=no;
end case;
exception
when case_no_found then
dbms_output.putline(’case中沒有和’||v_sal||’相匹配的條件’);
end;
- 2. 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.putline(emp_cursor.ename);
end loop;
exception
when cursor_already_open then
dbms_output.putline(’遊標已開啟!’);
end;
3.dup_val_on_index例外
在唯一索引所對應的列上插入重複的值時,會隱含的觸發此例外。
begin
insert into dept values(10,’公關部’,’北京’);
exception
when dup_val_on_index then
dbms_output.putline(’在deptno列上不能有重複值!’);
end;
4.invaild_cursor
當試圖在不合法的遊標上執行操作時,就會觸發該例外。例如當試圖從沒有開啟的遊標提取資料或是關閉沒有開啟的遊標時,就會觸發。
declare
cursor emp_cursor is select ename,sal from emp;
emp_record emp_cursor%rowtype;
begin
--open emp_cursor; --開啟遊標(但被註釋掉了)
fetch emp_cursor into emp_record;
dbms_output.putline(emp_cursor.ename);
close emp_cursor;
exception
when invalid_cursor then
dbms_output.putline(’請檢查遊標是否開啟’);
end;
5.invalid_number例外
當輸入的資料有誤時,會觸發該例外。比如把100寫成1oo就會觸發。
begin
update emp set sal=sal+’1oo’;
exception
when invalid_number then
dbms_output.putline(’輸入的數字不正確!’);
end;
6.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.putline(’返回了多行!’);
end;
7.zero_divide例外 當2/0,即0做分母的時候觸發。
8.value_error例外
當在執行賦值操作時,如果變數的長度不足以容納實際資料,就會觸發該例外。
declare
v_ename varchar2(5);
begin
select ename into v_ename from emp where empno=&no;
dbms_output.putline(’名字是:’||v_ename);
exception
when value_error then
dbms_output.putline(’資料超出變數長度!’);
end;
其他預定義例外。
- login_denide 當使用者非法登入時,會觸發該例外。
- not_logged_on 如果使用者沒有登入就執行dml操作,就會觸發。
- storage_error 如果超過了記憶體空間或記憶體被破壞,就會觸發。
- timeout_on_resoure 如果oracle在等待資源是出現了超時,觸發。
自定義例外
預定義例外是和oracle的錯誤相關的,而自定義例外於oracle錯誤沒有任何關係,它是由開發人員為特定的情況所定義的例外。
例:編寫一個pl/sql塊,接收一個僱員號,並給該僱員的工資增加1000元,如果該僱員不存在,請提示。
create or replace procedure test_ex(no number) is
declare
myex exception; --定義一個例外
begin
update emp set sal=sal+1000 where empno=no;
if sql%notfound then -- sql%notfound表示沒有更新資料
raise myex; --觸發myex
end if;
exception
when myex then
dbms_output.putline(’未更新成功!’);
end;
檢視
檢視與表的區別
- 表需要佔用磁碟空間,檢視不需要;
- 檢視不能新增索引;
- 使用檢視可以簡化複雜查詢;
- 檢視可以提高安全性
建立檢視
create view 檢視名 as select語句 [with read only];
例:建立檢視,把emp表的sal<1000的僱員對映到該檢視;
create view myview as select * from emp where sal<1000;
為簡化操作,用檢視顯示員工編號,姓名和所在部門
create view myview2 as select emp.empno,emp.ename,dept.dname from emp,dept where emp.deptno=dept.deptno;
建立或修改檢視
create or replace view 檢視名 as select語句 [with read only];
刪除檢視
drop view 檢視名;