在本章中,我們將討論和學(xué)習(xí)PL/SQL中的異常。 程序執(zhí)行過程中的錯(cuò)誤情況是一個(gè)例外(異常)。 PL/SQL支持程序員在程序中使用EXCEPTION塊捕獲這些發(fā)生錯(cuò)誤的條件,并針對(duì)錯(cuò)誤情況采取適當(dāng)?shù)拇胧?。PL/SQL中有兩種異常 -
異常處理的一般語法如下。在這里,可以列舉盡可能多的異常并且指定處理方式。默認(rèn)的異常將使用WHEN...THEN處理,如下語法所示 -
DECLARE
<declarations section>
BEGIN
<executable command(s)>
EXCEPTION
<exception handling goes here >
WHEN exception1 THEN
exception1-handling-statements
WHEN exception2 THEN
exception2-handling-statements
WHEN exception3 THEN
exception3-handling-statements
........
WHEN others THEN
exception3-handling-statements
END;
SQL
下面寫一個(gè)代碼來說明和理解這個(gè)概念,這里使用前面章節(jié)中創(chuàng)建和使用的CUSTOMERS表,結(jié)構(gòu)和數(shù)據(jù)如下 -
CREATE TABLE CUSTOMERS(
ID INT NOT NULL,
NAME VARCHAR (20) NOT NULL,
AGE INT NOT NULL,
ADDRESS CHAR (25),
SALARY DECIMAL (18, 2),
PRIMARY KEY (ID)
);
-- 向CUSTOMERS表中插入一些數(shù)據(jù)記錄
INSERT INTO CUSTOMERS (ID,NAME,AGE,ADDRESS,SALARY)
VALUES (1, 'Ramesh', 32, 'Ahmedabad', 2000.00 );
INSERT INTO CUSTOMERS (ID,NAME,AGE,ADDRESS,SALARY)
VALUES (2, 'Khilan', 25, 'Delhi', 1500.00 );
INSERT INTO CUSTOMERS (ID,NAME,AGE,ADDRESS,SALARY)
VALUES (3, 'kaushik', 23, 'Kota', 2000.00 );
INSERT INTO CUSTOMERS (ID,NAME,AGE,ADDRESS,SALARY)
VALUES (4, 'Chaitali', 25, 'Mumbai', 6500.00 );
INSERT INTO CUSTOMERS (ID,NAME,AGE,ADDRESS,SALARY)
VALUES (5, 'Hardik', 27, 'Bhopal', 8500.00 );
INSERT INTO CUSTOMERS (ID,NAME,AGE,ADDRESS,SALARY)
VALUES (6, 'Komal', 22, 'MP', 4500.00 );
SQL
下面是一個(gè)未找到數(shù)據(jù)記錄時(shí)的異常處理 -
SET SERVEROUTPUT ON SIZE 99999;
DECLARE
c_id customers.id%type := 100;
c_name customerS.name%type;
c_addr customers.address%type;
BEGIN
SELECT name, address INTO c_name, c_addr
FROM customers
WHERE id = c_id;
DBMS_OUTPUT.PUT_LINE ('姓名: '|| c_name);
DBMS_OUTPUT.PUT_LINE ('地址: ' || c_addr);
EXCEPTION
WHEN no_data_found THEN
dbms_output.put_line('沒有找到符合條件的客戶信息!');
WHEN others THEN
dbms_output.put_line('Error!');
END;
/
SQL
執(zhí)行上面示例代碼,得到以下結(jié)果 -
上面的程序用于顯示指定ID的客戶的名字和地址。但是由于數(shù)據(jù)庫customers表中并沒有ID值為100的客戶,因此程序引發(fā)異常,并在EXCEPTION塊中捕獲的運(yùn)行時(shí)異常NO_DATA_FOUND,因此最后打印了信息:‘沒有找到符合條件的客戶信息!’。
只要有內(nèi)部數(shù)據(jù)庫錯(cuò)誤,數(shù)據(jù)庫服務(wù)器就會(huì)自動(dòng)產(chǎn)生(引發(fā))異常,但程序員可以使用命令RAISE明確地引發(fā)異常。以下是引發(fā)異常的簡單語法 -
DECLARE
exception_name EXCEPTION;
BEGIN
IF condition THEN
RAISE exception_name;
END IF;
EXCEPTION
WHEN exception_name THEN
statement;
END;
SQL
可以使用上述語法來引發(fā)Oracle標(biāo)準(zhǔn)異?;蛉魏斡脩舳x的異常。 在下一節(jié)中,我們將舉例說明引發(fā)用戶定義的異常。您可以用類似的方式引發(fā)Oracle中標(biāo)準(zhǔn)異常。
PL/SQL允許根據(jù)程序的需要定義自己的異常。 用戶定義的異常必須聲明,然后使用RAISE語句或過程DBMS_STANDARD.RAISE_APPLICATION_ERROR顯式地引發(fā)。
聲明異常的語法是 -
DECLARE
my-exception EXCEPTION;
SQL
以下示例說明了這個(gè)概念。這個(gè)程序要求輸入一個(gè)客戶ID,當(dāng)用戶輸入一個(gè)無效的ID時(shí),會(huì)引發(fā)異常invalid_id。參考以下示例代碼的實(shí)現(xiàn) -
SET SERVEROUTPUT ON SIZE 9999;
DECLARE
c_id customers.id%type := &cc_id;
c_name customerS.name%type;
c_addr customers.address%type;
-- user defined exception
ex_invalid_id EXCEPTION;
BEGIN
IF c_id <= 0 THEN
RAISE ex_invalid_id;
ELSE
SELECT name, address INTO c_name, c_addr
FROM customers
WHERE id = c_id;
DBMS_OUTPUT.PUT_LINE ('姓名: '|| c_name);
DBMS_OUTPUT.PUT_LINE ('地址: ' || c_addr);
END IF;
EXCEPTION
WHEN ex_invalid_id THEN
dbms_output.put_line('編號(hào)ID必須要大于0!');
WHEN no_data_found THEN
dbms_output.put_line('未找到指定ID的客戶信息!');
WHEN others THEN
dbms_output.put_line('Error!');
END;
/
SQL
執(zhí)行上面示例代碼,得到以下結(jié)果 -
輸入 cc_id 的值: -1
原值 2: c_id customers.id%type := &cc_id;
新值 2: c_id customers.id%type := -1;
編號(hào)ID必須要大于0!
PL/SQL 過程已成功完成。
Shell
PL/SQL提供了許多預(yù)定義的異常,這些異常在程序違反任何數(shù)據(jù)庫規(guī)則時(shí)執(zhí)行。 例如,當(dāng)SELECT INTO語句不返回任何行時(shí),會(huì)引發(fā)預(yù)定義的異常NO_DATA_FOUND。下表列出了一些重要的預(yù)定義異常情況 -
異常 | Oracle錯(cuò)誤代碼 | SQLCODE | 描述 |
---|---|---|---|
ACCESS_INTO_NULL |
06530 | -6530 |
當(dāng)一個(gè)空對(duì)象被自動(dòng)分配一個(gè)值時(shí)會(huì)引發(fā)它。 |
CASE_NOT_FOUND |
06592 | -6592 |
當(dāng)沒有選擇CASE 語句的WHEN 子句中的任何選項(xiàng)時(shí),會(huì)引發(fā)這個(gè)錯(cuò)誤,并且沒有ELSE 子句。 |
COLLECTION_IS_NULL |
06531 | -6531 |
當(dāng)程序嘗試將EXISTS 以外的集合方法應(yīng)用于未初始化的嵌套表或varray 時(shí),或程序嘗試將值分配給未初始化的嵌套表或varray 的元素時(shí),會(huì)引發(fā)此問題。 |
DUP_VAL_ON_INDEX |
00001 | -1 |
當(dāng)嘗試將重復(fù)值存儲(chǔ)在具有唯一索引的列中時(shí)引發(fā)此錯(cuò)誤。 |
INVALID_CURSOR |
01001 | -1001 |
當(dāng)嘗試進(jìn)行不允許的游標(biāo)操作(例如關(guān)閉未打開的游標(biāo))時(shí)會(huì)引發(fā)此錯(cuò)誤。 |
INVALID_NUMBER |
01722 | -1722 |
當(dāng)字符串轉(zhuǎn)換為數(shù)字時(shí)失敗,因?yàn)樽址淮碛行У臄?shù)字。 |
LOGIN_DENIED |
01017 | -1017 |
當(dāng)程序嘗試使用無效的用戶名或密碼登錄到數(shù)據(jù)庫時(shí)引發(fā)。 |
NO_DATA_FOUND |
01403 | +100 |
當(dāng)SELECT INTO 語句不返回任何行時(shí)會(huì)引發(fā)它。 |
NOT_LOGGED_ON |
01012 | -1012 |
當(dāng)數(shù)據(jù)庫調(diào)用沒有連接到數(shù)據(jù)庫時(shí)引發(fā)。 |
PROGRAM_ERROR |
06501 | -6501 |
當(dāng)PL/SQL遇到內(nèi)部問題時(shí)會(huì)引發(fā)。 |
ROWTYPE_MISMATCH |
06504 | -6504 |
當(dāng)游標(biāo)在具有不兼容數(shù)據(jù)類型的變量中獲取值時(shí)引發(fā)。 |
SELF_IS_NULL |
30625 | -30625 |
當(dāng)調(diào)用成員方法時(shí)引發(fā),但對(duì)象類型的實(shí)例未初始化。 |
STORAGE_ERROR |
06500 | -6500 |
當(dāng)PL/SQL用盡內(nèi)存或內(nèi)存已損壞時(shí)引發(fā)。 |
TOO_MANY_ROWS |
01422 | -1422 |
當(dāng)SELECT INTO 語句返回多行時(shí)引發(fā)。 |
VALUE_ERROR |
06502 | -6502 |
當(dāng)發(fā)生算術(shù),轉(zhuǎn)換,截?cái)嗷蛘?code>sizeconstraint錯(cuò)誤時(shí)引發(fā)。 |
ZERO_DIVIDE |
01476 | 1476 |
當(dāng)嘗試將數(shù)字除以零時(shí)引發(fā)。 |
更多建議: