資料庫複習提綱之文字版(這個只是自己整理文案,答案是查的,勿噴,謝謝,希望給別人一些幫助) 1、Oracle和SQL server2005的區別 Oracle資料庫:Oracle Database,又名Oracle RDBMS,或簡稱Oracle。是甲骨文公司的一款關係資料庫管理系統。 它是在資料庫 ...
資料庫複習提綱之文字版(這個只是自己整理文案,答案是查的,勿噴,謝謝,希望給別人一些幫助)
1、Oracle和SQL server2005的區別
Oracle資料庫:Oracle Database,又名Oracle RDBMS,或簡稱Oracle。是甲骨文公司的一款關係資料庫管理系統。
它是在資料庫領域一直處於領先地位的產品。可以說Oracle資料庫系統是目前世界上流行的關係資料庫管理系統,系統可移植性好、使用方便、功能強,適用於各類大、中、小、微機環境。它是一種高效率、可靠性好的 適應高吞吐量的資料庫解決方案。
sql server資料庫:美國Microsoft公司推出的一種關係型資料庫系統。SQL Server是一個可擴展的、高性能的、為分散式客戶機/伺服器計算所設計的資料庫管理系統,實現了與WindowsNT的有機結合,提供了基於事務的企業級信息管理系統方案
兩者的區別:
(1)操作的平臺不同
- Oracle可在所有主流平臺上運行,Oracle資料庫採用開放的策略目標,它使得客戶可以選擇一種最適合他們特定需要的解決方案。客戶可以利用很多種第三方應用程式、工具。而SQL Server卻只能在Windows上運行了。
- 但SQL Sever在Window平臺上的表現,和Windows操作系統的整體結合程度,使用方便性,和Microsoft開發平臺的整合性都比Oracle強的很多。但Windows操作系統的穩定性及可靠性大家是有目共睹的,再說Microsoft公司的策略目標是將客戶都鎖定到Windows平臺的環境當中,只有隨著Windows性能的改善,SQL Server才能進一步提高。從操作平臺這點上Oracle是完全優勝於SQL Server的了。
- 也就是說Oracle支持多種操作系統,sql server支持window系統
(2)文體結構不同
- oracle的文件體繫結構為:
數據文件 .dbf(真實數據)
日誌文件 .rdo
控制文件 .ctl
參數文件 .ora
sql server的文件體繫結構為:
.mdf (數據字典)
.ndf (數據文件)
.ldf (日誌文件)
(3)存儲結構不同
-
oracle存儲結構:
在oracle里有兩個塊參數pctfree(填充因數)和pctused(復用因數),可控制塊確定塊本身何時有,何時沒有足夠的空間接受新信息(對塊的存儲情況的分析機制)
這樣可降低數據行連接與行遷移的可能性。塊的大小可設置(oltp塊和dss塊)
在oracle中,將連續的塊組成區,可動態分配區(區的分配可以是等額的也可以是自增長的)可減少空間分配次數
在oraclel里表可以分為多個段,段由多個區組成,每個段可指定分配在哪個表空間里(段的類型分為:數據段、索引段、回滾段、臨時段、cash段。oracle里還可對錶進行分區,可按照用戶定義的業務規則、條件或規範,物理的分開磁碟上的數據。
這樣大大降低了磁碟爭用的可能性。
oracle有七個基本表空間:
·system表空間(存放數據字典和數據管理自身所需的信息)
·rbs回滾表空間
·temp臨時表空間
·tools互動式表空間
·users用戶預設表空間
·indx索引表空間
·dbsys福數據表空間
不同的數據分別放在不同的表空間(數據字典與真實數據分開存放),在oracle里基表(存儲系統參數信息)是加密存儲,任何人都無法訪問。只能通過用戶可視視圖查看。
-
sql server 存儲結構
以頁為最小分配單位,每個頁為8k(不可控制,缺乏對頁的存儲情況的分析機制),可將8個連續的頁的組成一個‘擴展’,以進一步減少分配時所耗用的資源。(分配缺乏靈活性),在sql server里數據以表的方式存放,而表是存放在資料庫里。
sql server有五個基本資料庫: www.2cto.com
·master(數據字典)
·mode(存放樣版)
·tempdb(臨時資料庫)
·msdb(存放調度信息和日誌信息)
·pubs(示例資料庫)
真實數據與數據字典存放在一起。對系統參數信息無安全機制。
(4)安全性
Oracle的安全認證獲得最高認證級別的ISO標準認證,而SQL Server並沒有獲得什麼安全認證。這方面證明瞭Oracle的安全性是高於SQL Server的。
(5)性能不同
SQL Server 多用戶時性能不佳
Oracle 性能最高, 保持windowsNT下的TPC-D和TPC-C的世界記錄。
(6)開放性
SQL Server 只能在windows 上運行,沒有絲毫的開放性,操作系統的系統的穩定對資料庫是十分重要的。Windows9X系列產品是偏重於桌面應用,NT server只適合中小型企業。而且windows平臺的可靠性,安全性和伸縮性是非常有限的。它不象unix那樣久經考驗,尤其是在處理大數據量的關鍵業務時。
Oracle 能在所有主流平臺上運行(包括 windows)。完全支持所有的工業標準。採用完全開放策略。可以使客戶選擇最適合的解決方案。對開發商全力支持
(7)客戶端支持及應用模式
SQL Server C/S結構,只支持windows客戶,可以用ADO,DAO,OLEDB ,ODBC連接.
Oracle 多層次網路計算,支持多種工業標準,可以用ODBC, JDBC,OCI等網路客戶連接
意見:
SQL server 完全重寫的代碼,經歷了長期的測試,不斷延遲,許多功能需要時間來證明。並不十分相容早期產品。使用需要冒一定風險。
Oracle 長時間的開發經驗,完全向下相容。得到廣泛的應用。完全沒有風險。
2. 如何使用Oracle的游標?
游標的概念:
游標是SQL的一個記憶體工作區,由系統或用戶以變數的形式定義。游標的作用就是用於臨時存儲從資料庫中提取的數據塊。在某些情況下,需要把數據從存放在磁碟的表中調到電腦記憶體中進行處理,最後將處理結果顯示出來或最終寫回資料庫。這樣數據處理的速度才會提高,否則頻繁的磁碟數據交換會降低效率。
游標有兩種類型:顯式游標和隱式游標。在前述程式中用到的SELECT...INTO...查詢語句,一次只能從資料庫中提取一行數據,對於這種形式的查詢和DML操作,系統都會使用一個隱式游標。但是如果要提取多行數據,就要由程式員定義一個顯式游標,並通過與游標有關的語句進行處理。顯式游標對應一個返回結果為多行多列的SELECT語句。
游標一旦打開,數據就從資料庫中傳送到游標變數中,然後應用程式再從游標變數中分解出需要的數據,併進行處理。
隱式游標
如前所述,DML操作和單行SELECT語句會使用隱式游標,它們是:
* 插入操作:INSERT。
* 更新操作:UPDATE。
* 刪除操作:DELETE。
* 單行查詢操作:SELECT ... INTO ...。
當系統使用一個隱式游標時,可以通過隱式游標的屬性來瞭解操作的狀態和結果,進而控製程序的流程。隱式游標可以使用名字SQL來訪問,但要註意,通過SQL游標名總是只能訪問前一個DML操作或單行SELECT操作的游標屬性。所以通常在剛剛執行完操作之後,立即使用SQL游標名來訪問屬性。游標的屬性有四種,如下所示。
Sql代碼
- 隱式游標的屬性 返回值類型 意 義
- SQL%ROWCOUNT 整型 代表DML語句成功執行的數據行數
- SQL%FOUND 布爾型 值為TRUE代表插入、刪除、更新或單行查詢操作成功
- SQL%NOTFOUND 布爾型 與SQL%FOUND屬性返回值相反
- SQL%ISOPEN 布爾型 DML執行過程中為真,結束後為假
【訓練1】 使用隱式游標的屬性,判斷對雇員工資的修改是否成功。
步驟1:輸入和運行以下程式:
Sql代碼
- SET SERVEROUTPUT ON
- BEGIN
- UPDATE emp SET sal=sal+100 WHERE empno=1234;
- IF SQL%FOUND THEN
- DBMS_OUTPUT.PUT_LINE('成功修改雇員工資!');
- COMMIT;
- ELSE
- DBMS_OUTPUT.PUT_LINE('修改雇員工資失敗!');
- END IF;
- END;
運行結果為:
Sql代碼
- 修改雇員工資失敗!
- PL/SQL 過程已成功完成。
步驟2:將雇員編號1234改為7788,重新執行以上程式:
運行結果為:
Sql代碼
- 成功修改雇員工資!
- PL/SQL 過程已成功完成。
說明:本例中,通過SQL%FOUND屬性判斷修改是否成功,並給出相應信息。
顯式游標
游標的定義和操作
游標的使用分成以下4個步驟。
1.聲明游標
在DECLEAR部分按以下格式聲明游標:
CURSOR 游標名[(參數1 數據類型[,參數2 數據類型...])]
IS SELECT語句;
參數是可選部分,所定義的參數可以出現在SELECT語句的WHERE子句中。如果定義了參數,則必須在打開游標時傳遞相應的實際參數。
SELECT語句是對錶或視圖的查詢語句,甚至也可以是聯合查詢。可以帶WHERE條件、ORDER BY或GROUP BY等子句,但不能使用INTO子句。在SELECT語句中可以使用在定義游標之前定義的變數。
2.打開游標
在可執行部分,按以下格式打開游標:
OPEN 游標名[(實際參數1[,實際參數2...])];
打開游標時,SELECT語句的查詢結果就被傳送到了游標工作區。
3.提取數據
在可執行部分,按以下格式將游標工作區中的數據取到變數中。提取操作必須在打開游標之後進行。
FETCH 游標名 INTO 變數名1[,變數名2...];
或
FETCH 游標名 INTO 記錄變數;
游標打開後有一個指針指向數據區,FETCH語句一次返回指針所指的一行數據,要返回多行需重覆執行,可以使用迴圈語句來實現。控制迴圈可以通過判斷游標的屬性來進行。
下麵對這兩種格式進行說明:
第一種格式中的變數名是用來從游標中接收數據的變數,需要事先定義。變數的個數和類型應與SELECT語句中的欄位變數的個數和類型一致。
第二種格式一次將一行數據取到記錄變數中,需要使用%ROWTYPE事先定義記錄變數,這種形式使用起來比較方便,不必分別定義和使用多個變數。
定義記錄變數的方法如下:
變數名 表名|游標名%ROWTYPE;
其中的表必須存在,游標名也必須先定義。
4.關閉游標
CLOSE 游標名;
顯式游標打開後,必須顯式地關閉。游標一旦關閉,游標占用的資源就被釋放,游標變成無效,必須重新打開才能使用。
以下是使用顯式游標的一個簡單練習。
【訓練1】 用游標提取emp表中7788雇員的名稱和職務。
Sql代碼
- SET SERVEROUTPUT ON
- DECLARE
- v_ename VARCHAR2(10);
- v_job VARCHAR2(10);
- CURSOR emp_cursor IS
- SELECT ename,job FROM emp WHERE empno=7788;
- BEGIN
- OPEN emp_cursor;
- FETCH emp_cursor INTO v_ename,v_job;
- DBMS_OUTPUT.PUT_LINE(v_ename||','||v_job);
- CLOSE emp_cursor;
- END;
執行結果為:
Sql代碼
- SCOTT,ANALYST
- PL/SQL 過程已成功完成。
說明:該程式通過定義游標emp_cursor,提取並顯示雇員7788的名稱和職務。
作為對以上例子的改進,在以下訓練中採用了記錄變數。
【訓練2】 用游標提取emp表中7788雇員的姓名、職務和工資。
Sql代碼
- SET SERVEROUTPUT ON
- DECLARE
- CURSOR emp_cursor IS SELECT ename,job,sal FROM emp WHERE empno=7788;
- emp_record emp_cursor%ROWTYPE;
- BEGIN
- OPEN emp_cursor;
- FETCH emp_cursor INTO emp_record;
- DBMS_OUTPUT.PUT_LINE(emp_record.ename||','|| emp_record.job||','|| emp_record.sal);
- CLOSE emp_cursor;
- END;
執行結果為:
Sql代碼
- SCOTT,ANALYST,3000
- PL/SQL 過程已成功完成。
說明:實例中使用記錄變數來接收數據,記錄變數由游標變數定義,需要出現在游標定義之後。
註意:可通過以下形式獲得記錄變數的內容:
記錄變數名.欄位名。
【訓練3】 顯示工資最高的前3名雇員的名稱和工資。
Sql代碼
- SET SERVEROUTPUT ON
- DECLARE
- V_ename VARCHAR2(10);
- V_sal NUMBER(5);
- CURSOR emp_cursor IS SELECT ename,sal FROM emp ORDER BY sal DESC;
- BEGIN
- OPEN emp_cursor;
- FOR I IN 1..3 LOOP
- FETCH emp_cursor INTO v_ename,v_sal;
- DBMS_OUTPUT.PUT_LINE(v_ename||','||v_sal);
- END LOOP;
- CLOSE emp_cursor;
- END;
執行結果為:
Sql代碼
- KING,5000
- SCOTT,3000
- FORD,3000
- PL/SQL 過程已成功完成。
說明:該程式在游標定義中使用了ORDER BY子句進行排序,並使用迴圈語句來提取多行數據。
游標迴圈
【訓練1】 使用特殊的FOR迴圈形式顯示全部雇員的編號和名稱。
Sql代碼
- SET SERVEROUTPUT ON
- DECLARE
- CURSOR emp_cursor IS
- SELECT empno, ename FROM emp;
- BEGIN
- FOR Emp_record IN emp_cursor LOOP
- DBMS_OUTPUT.PUT_LINE(Emp_record.empno|| Emp_record.ename);
- END LOOP;
- END;
執行結果為:
Sql代碼
- 7369SMITH
- 7499ALLEN
- 7521WARD
- 7566JONES
- PL/SQL 過程已成功完成。
說明:可以看到該迴圈形式非常簡單,隱含了記錄變數的定義、游標的打開、提取和關閉過程。Emp_record為隱含定義的記錄變數,迴圈的執行次數與游標取得的數據的行數相一致。
【訓練2】 另一種形式的游標迴圈。
Sql代碼
- SET SERVEROUTPUT ON
- BEGIN
- FOR re IN (SELECT ename FROM EMP) LOOP
- DBMS_OUTPUT.PUT_LINE(re.ename)
- END LOOP;
- END;
執行結果為:
Sql代碼
- SMITH
- ALLEN
- WARD
- JONES
說明:該種形式更為簡單,省略了游標的定義,游標的SELECT查詢語句在迴圈中直接出現。
顯式游標屬性
雖然可以使用前面的形式獲得游標數據,但是在游標定義以後使用它的一些屬性來進行結構控制是一種更為靈活的方法。顯式游標的屬性如下所示。
Sql代碼
- 游標的屬性 返回值類型 意 義
- %ROWCOUNT 整型 獲得FETCH語句返回的數據行數
- %FOUND 布爾型 最近的FETCH語句返回一行數據則為真,否則為假
- %NOTFOUND 布爾型 與%FOUND屬性返回值相反
- %ISOPEN 布爾型 游標已經打開時值為真,否則為假
可按照以下形式取得游標的屬性:
游標名%屬性
要判斷游標emp_cursor是否處於打開狀態,可以使用屬性emp_cursor%ISOPEN。如果游標已經打開,則返回值為“真”,否則為“假”。具體可參照以下的訓練。
【訓練1】 使用游標的屬性練習。
Sql代碼
- SET SERVEROUTPUT ON
- DECLARE
- V_ename VARCHAR2(10);
- CURSOR emp_cursor IS
- SELECT ename FROM emp;
- BEGIN
- OPEN emp_cursor;
- IF emp_cursor%ISOPEN THEN
- LOOP
- FETCH emp_cursor INTO v_ename;
- EXIT WHEN emp_cursor%NOTFOUND;
- DBMS_OUTPUT.PUT_LINE(to_char(emp_cursor%ROWCOUNT)||'-'||v_ename);
- END LOOP;
- ELSE
- DBMS_OUTPUT.PUT_LINE('用戶信息:游標沒有打開!');
- END IF;
- CLOSE emp_cursor;
- END;
執行結果為:
Sql代碼
- 1-SMITH
- 2-ALLEN
- 3-WARD
- PL/SQL 過程已成功完成。
說明:本例使用emp_cursor%ISOPEN判斷游標是否打開;使用emp_cursor%ROWCOUNT獲得到目前為止FETCH語句返回的數據行數並輸出;使用迴圈來獲取數據,在迴圈體中使用FETCH語句;使用emp_cursor%NOTFOUND判斷FETCH語句是否成功執行,當FETCH語句失敗時說明數據已經取完,退出迴圈。
【練習1】去掉OPEN emp_cursor;語句,重新執行以上程式。
游標參數的傳遞
【訓練1】 帶參數的游標。
Sql代碼
- SET SERVEROUTPUT ON
- DECLARE
- V_empno NUMBER(5);
- V_ename VARCHAR2(10);
- CURSOR emp_cursor(p_deptno NUMBER, p_job VARCHAR2) IS
- SELECT empno, ename FROM emp
- WHERE deptno = p_deptno AND job = p_job;
- BEGIN
- OPEN emp_cursor(10, 'CLERK');
- LOOP
- FETCH emp_cursor INTO v_empno,v_ename;
- EXIT WHEN emp_cursor%NOTFOUND;
- DBMS_OUTPUT.PUT_LINE(v_empno||','||v_ename);
- END LOOP;
- END;
執行結果為:
Sql代碼
- 7934,MILLER
- PL/SQL 過程已成功完成。
說明:游標emp_cursor定義了兩個參數:p_deptno代表部門編號,p_job代表職務。語句OPEN emp_cursor(10, 'CLERK')傳遞了兩個參數值給游標,即部門為10、職務為CLERK,所以游標查詢的內容是部門10的職務為CLERK的雇員。迴圈部分用於顯示查詢的內容。
【練習1】修改Open語句的參數:部門號為20、職務為ANALYST,並重新執行。
也可以通過變數向游標傳遞參數,但變數需要先於游標定義,併在游標打開之前賦值。對以上例子重新改動如下:
【訓練2】 通過變數傳遞參數給游標。
Sql代碼
- SET SERVEROUTPUT ON
- DECLARE
- v_empno NUMBER(5);
- v_ename VARCHAR2(10);
- v_deptno NUMBER(5);
- v_job VARCHAR2(10);
- CURSOR emp_cursor IS
- SELECT empno, ename FROM emp
- WHERE deptno = v_deptno AND job = v_job;
- BEGIN
- v_deptno:=10;
- v_job:='CLERK';
- OPEN emp_cursor;
- LOOP
- FETCH emp_cursor INTO v_empno,v_ename;
- EXIT WHEN emp_cursor%NOTFOUND;
- DBMS_OUTPUT.PUT_LINE(v_empno||','||v_ename);
- END LOOP;
- END;
執行結果為:
Sql代碼
- 7934,MILLER
- PL/SQL 過程已成功完成。
說明:該程式與前一程式實現相同的功能。
動態SELECT語句和動態游標的用法
Oracle支持動態SELECT語句和動態游標,動態的方法大大擴展了程式設計的能力。
對於查詢結果為一行的SELECT語句,可以用動態生成查詢語句字元串的方法,在程式執行階段臨時地生成並執行,語法是:
execute immediate 查詢語句字元串 into 變數1[,變數2...];
以下是一個動態生成SELECT語句的例子。
【訓練1】 動態SELECT查詢。
Sql代碼
- SET SERVEROUTPUT ON
- DECLARE
- str varchar2(100);
- v_ename varchar2(10);
- begin
- str:='select ename from scott.emp where empno=7788';
- execute immediate str into v_ename;
- dbms_output.put_line(v_ename);
- END;
執行結果為:
Sql代碼
- SCOTT
- PL/SQL 過程已成功完成。
說明:SELECT...INTO...語句存放在STR字元串中,通過EXECUTE語句執行。
在變數聲明部分定義的游標是靜態的,不能在程式運行過程中修改。雖然可以通過參數傳遞來取得不同的數據,但還是有很大的局限性。通過採用動態游標,可以在程式運行階段隨時生成一個查詢語句作為游標。要使用動態游標需要先定義一個游標類型,然後聲明一個游標變數,游標對應的查詢語句可以在程式的執行過程中動態地說明。
定義游標類型的語句如下:
TYPE 游標類型名 REF CURSOR;
聲明游標變數的語句如下:
游標變數名 游標類型名;
在可執行部分可以如下形式打開一個動態游標:
OPEN 游標變數名 FOR 查詢語句字元串;
【訓練2】 按名字中包含的字母順序分組顯示雇員信息。
輸入並運行以下程式:
Sql代碼
- declare
- type cur_type is ref cursor;
- cur cur_type;
- rec scott.emp%rowtype;
- str varchar2(50);
- letter char:= 'A';
- begin
- loop
- str:= 'select ename from emp where ename like ''%'||letter||'%''';
- open cur for str;
- dbms_output.put_line('包含字母'||letter||'的名字:');
- loop
- fetch cur into rec.ename;
- exit when cur%notfound;
- dbms_output.put_line(rec.ename);
- end loop;
- exit when letter='Z';
- letter:=chr(ascii(letter)+1);
- end loop;
- end;
運行結果為:
Sql代碼
- 包含字母A的名字:
- ALLEN
- WARD
- MARTIN
- BLAKE
- CLARK
- ADAMS
- JAMES
- 包含字母B的名字:
- BLAKE
- 包含字母C的名字:
- CLARK
- SCOTT
說明:使用了二重迴圈,在外迴圈體中,動態生成游標的SELECT語句,然後打開。通過語句letter:=chr(ascii(letter)+1)可獲得字母表中的下一個字母。
異常處理
錯誤處理
錯誤處理部分位於程式的可執行部分之後,是由WHEN語句引導的多個分支構成的。錯誤處理的語法如下:
EXCEPTION
WHEN 錯誤1[OR 錯誤2] THEN
語句序列1;
WHEN 錯誤3[OR 錯誤4] THEN
語句序列2;
WHEN OTHERS
語句序列n;
END;
其中:
錯誤是在標準包中由系統預定義的標準錯誤,或是由用戶在程式的說明部分自定義的錯誤,參見下一節系統預定義的錯誤類型。
語句序列就是不同分支的錯誤處理部分。
凡是出現在WHEN後面的錯誤都是可以捕捉到的錯誤,其他未被捕捉到的錯誤,將在WHEN OTHERS部分進行統一處理,OTHENS必須是EXCEPTION部分的最後一個錯誤處理分支。如要在該分支中進一步判斷錯誤種類,可以通過使用預定義函數SQLCODE( )和SQLERRM( )來獲得系統錯誤號和錯誤信息。
如果在程式的子塊中發生了錯誤,但子塊沒有錯誤處理部分,則錯誤會傳遞到主程式中。
下麵是由於查詢編號錯誤而引起系統預定義異常的例子。
【訓練1】 查詢編號為1234的雇員名字。
Sql代碼
- SET SERVEROUTPUT ON
- DECLARE
- v_name VARCHAR2(10);
- BEGIN
- SELECT ename
- INTO v_name
- FROM emp
- WHERE empno = 1234;
- DBMS_OUTPUT.PUT_LINE('該雇員名字為:'|| v_name);
- EXCEPTION
- WHEN NO_DATA_FOUND THEN
- DBMS_OUTPUT.PUT_LINE('編號錯誤,沒有找到相應雇員!');
- WHEN OTHERS THEN
- DBMS_OUTPUT.PUT_LINE('發生其他錯誤!');
- END;
執行結果為:
Sql代碼
- 編號錯誤,沒有找到相應雇員!
- PL/SQL 過程已成功完成。
說明:在以上查詢中,因為編號為1234的雇員不存在,所以將發生類型為“NO_DATA_
FOUND”的異常。“NO_DATA_FOUND”是系統預定義的錯誤類型,EXCEPTION部分下的WHEN語句將捕捉到該異常,並執行相應代碼部分。在本例中,輸出用戶自定義的錯誤信息“編號錯誤,沒有找到相應雇員!”。如果發生其他類型的錯誤,將執行OTHERS條件下的代碼部分,顯示“發生其他錯誤!”。
【訓練2】 由程式代碼顯示系統錯誤。
Sql代碼
- SET SERVEROUTPUT ON
- DECLARE
- v_temp NUMBER(5):=1;
- BEGIN
- v_temp:=v_temp/0;
- EXCEPTION
- WHEN OTHERS THEN
- DBMS_OUTPUT.PUT_LINE('發生系統錯誤!');
- DBMS_OUTPUT.PUT_LINE('錯誤代碼:'|| SQLCODE( ));
- DBMS_OUTPUT.PUT_LINE('錯誤信息:' ||SQLERRM( ));
- END;
執行結果為:
Sql代碼
- 發生系統錯誤!
- 錯誤代碼:?1476
- 錯誤信息:ORA-01476: 除數為 0
- PL/SQL 過程已成功完成。
說明:程式運行中發生除零錯誤,由WHEN OTHERS捕捉到,執行用戶自己的輸出語句顯示錯誤信息,然後正常結束。在錯誤處理部分使用了預定義函數SQLCODE( )和SQLERRM( )來進一步獲得錯誤的代碼和種類信息。
預定義錯誤
Oracle的系統錯誤很多,但只有一部分常見錯誤在標準包中予以定義。定義的錯誤可以在EXCEPTION部分通過標準的錯誤名來進行判斷,併進行異常處理。常見的系統預定義異常如下所示。
Sql代碼
- 錯 誤 名 稱 錯誤代碼 錯 誤 含 義
- CURSOR_ALREADY_OPEN ORA_06511 試圖打開已經打開的游標
- INVALID_CURSOR ORA_01001 試圖使用沒有打開的游標
- DUP_VAL_ON_INDEX ORA_00001 保存重覆值到惟一索引約束的列中
- ZERO_DIVIDE ORA_01476 發生除數為零的除法錯誤
- INVALID_NUMBER ORA_01722 試圖對無效字元進行數值轉換
- ROWTYPE_MISMATCH ORA_06504 主變數和游標的類型不相容
- VALUE_ERROR ORA_06502 轉換、截斷或算術運算發生錯誤
- TOO_MANY_ROWS ORA_01422 SELECT…INTO…語句返回多於一行的數據
- NO_DATA_FOUND ORA_01403 SELECT…INTO…語句沒有數據返回
- TIMEOUT_ON_RESOURCE ORA_00051 等待資源時發生超時錯誤
- TRANSACTION_BACKED_OUT ORA_00060 由於死鎖,提交失敗
- STORAGE_ERROR ORA_06500 發生記憶體錯誤
- PROGRAM_ERROR ORA_06501 發生PL/SQL內部錯誤
- NOT_LOGGED_ON ORA_01012 試圖操作未連接的資料庫
- LOGIN_DENIED ORA_01017 在連接時提供了無效用戶名或口令
比如,如果程式向表的主鍵列插入重覆值,則將發生DUP_VAL_ON_INDEX錯誤。
如果一個系統錯誤沒有在標準包中定義,則需要在說明部分定義,語法如下:
錯誤名 EXCEPTION;
定義後使用PRAGMA EXCEPTION_INIT來將一個定義的錯誤同一個特別的Oracle錯誤代碼相關聯,就可以同系統預定義的錯誤一樣使用了。語法如下:
PRAGMA EXCEPTION_INIT(錯誤名,- 錯誤代碼);
【訓練1】 定義新的系統錯誤類型。
Sql代碼
- SET SERVEROUTPUT ON
- DECLARE
- V_ENAME VARCHAR2(10);
- NULL_INSERT_ERROR EXCEPTION;
- PRAGMA EXCEPTION_INIT(NULL_INSERT_ERROR,-1400);
- BEGIN
- INSERT INTO EMP(EMPNO) VALUES(NULL);
- EXCEPTION
- WHEN NULL_INSERT_ERROR THEN
- DBMS_OUTPUT.PUT_LINE('無法插入NULL值!');
- WHEN OTHERS THEN
- DBMS_OUTPUT.PUT_LINE('發生其他系統錯誤!');
- END;
執行結果為:
Sql代碼
- 無法插入NULL值!
- PL/SQL 過程已成功完成。
說明:NULL_INSERT_ERROR是自定義異常,同系統錯誤1400相關聯。
自定義異常
程式設計者可以利用引發異常的機制來進行程式設計,自己定義異常類型。可以在聲明部分定義新的異常類型,定義的語法是:
錯誤名 EXCEPTION;
用戶定義的錯誤不能由系統來觸發,必須由程式顯式地觸發,觸發的語法是:
RAISE 錯誤名;
RAISE也可以用來引發模擬系統錯誤,比如,RAISE ZERO_DIVIDE將引發模擬的除零錯誤。
使用RAISE_APPLICATION_ERROR函數也可以引發異常。該函數要傳遞兩個參數,第一個是用戶自定義的錯誤編號,第二個參數是用戶自定義的錯誤信息。使用該函數引發的異常的編號應該在20 000和20 999之間選擇。
自定義異常處理錯誤的方式同前。
【訓練1】 插入新雇員,限定插入雇員的編號在7000~8000之間。
Java代碼
- SET SERVEROUTPUT ON
- DECLARE
- new_no NUMBER(10);
- new_excp1 EXCEPTION;
- new_excp2 EXCEPTION;
- BEGIN
- new_no:=6789;
- INSERT INTO emp(empno,ename)
- VALUES(new_no, '小鄭');
- IF new_no<7000 THEN
- RAISE new_excp1;
- END IF;
- IF new_no>8000 THEN
- RAISE new_excp2;
- END IF;
- COMMIT;
- EXCEPTION
- WHEN new_excp1 THEN
- ROLLBACK;
- DBMS_OUTPUT.PUT_LINE('雇員編號小於7000的下限!');
- WHEN new_excp2 THEN
- ROLLBACK;
- DBMS_OUTPUT.PUT_LINE('雇員編號超過8000的上限!');
- END;
執行結果為:
雇員編號小於7000的下限!
PL/SQL 過程已成功完成。
說明:在此例中,自定義了兩個異常:new_excp1和new_excp2,分別代表編號小於7000和編號大於8000的錯誤。在程式中通過判斷編號大小,產生對應的異常,併在異常處理部分回退插入操作,然後顯示相應的錯誤信息。
【訓練2】 使用RAISE_APPLICATION_ERROR函數引發系統異常。
Sql代碼
- SET SERVEROUTPUT ON
- DECLARE
- New_no NUMBER(10);
- BEGIN
- New_no:=6789;
- INSERT INTO emp(empno,ename)
- VALUES(new_no, 'JAMES');
- IF new_no<7000 THEN
- ROLLBACK;
- RAISE_APPLICATION_ERROR(-20001, '編號小於7000的下限!');
- END IF;
- IF new_no>8000 THEN
- ROLLBACK;
- RAISE_APPLICATION_ERROR (-20002, '編號大於8000的下限!');
- END IF;
- END;
執行結果為:
Sql代碼
- DECLARE
-