天天看點

ORACLE PL/SQL程式設計詳解

SQL語言隻是通路、操作資料庫的語言,并不是一種具有流程控制的程式設計語言,而隻有程式設計語言才能用于應用軟體的開發。PL /SQL是一種進階資料庫程式設計語言,該語言專門用于在各種環境下對ORACLE資料庫進行通路。由于該語言內建于資料庫伺服器中,是以PL/SQL代碼可以對資料進行快速高效的處理。除此之外,可以在ORACLE資料庫的某些用戶端工具中,使用PL/SQL語言也是該語言的一個特點。本章的主要内容是讨論引入PL/SQL語言的必要性和該語言的主要特點,以及了解PL/SQL語言的重要性和資料庫版本問題。還要介紹一些貫穿全書的更詳細的進階概念,并在本章的最後就我們在本書案例中使用的資料庫表的若幹約定做一說明。

1.1 SQL與PL/SQL

1.1.1 什麼是PL/SQL?

PL/SQL是 Procedure Language & Structured Query Language 的縮寫。ORACLE的SQL是支援ANSI(American national Standards Institute)和ISO92 (International Standards Organization)标準的産品。PL/SQL是對SQL語言存儲過程語言的擴充。從ORACLE6以後,ORACLE的RDBMS附帶了PL/SQL。它現在已經成為一種過程處理語言,簡稱PL/SQL。目前的PL/SQL包括兩部分,一部分是資料庫引擎部分;另一部分是可嵌入到許多産品(如C語言,JAVA語言等)工具中的獨立引擎。可以将這兩部分稱為:資料庫PL/SQL和工具PL/SQL。兩者的程式設計非常相似。都具有程式設計結構、文法和邏輯機制。工具PL/SQL另外還增加了用于支援工具(如ORACLE Forms)的句法,如:在窗體上設定按鈕等。本章主要介紹資料庫PL/SQL内容。

1.2 PL/SQL的優點或特征

1.2.1 有利于客戶/伺服器環境應用的運作

對于客戶/伺服器環境來說,真正的瓶頸是網絡上。無論網絡多快,隻要用戶端與伺服器進行大量的資料交換。應用運作的效率自然就回受到影響。如果使用PL/SQL進行程式設計,将這種具有大量資料處理的應用放在伺服器端來執行。自然就省去了資料在網上的傳輸時間。

1.2.2 适合于客戶環境

PL/SQL由于分為資料庫PL/SQL部分和工具PL/SQL。對于用戶端來說,PL/SQL可以嵌套到相應的工具中,用戶端程式可以執行本地包含PL/SQL部分,也可以向服務發SQL指令或激活伺服器端的PL/SQL程式運作。

1.2.3 過程化

PL/SQL是Oracle在标準SQL上的過程性擴充,不僅允許在PL/SQL程式内嵌入SQL語句,而且允許使用各種類型的條件分支語句和循環語句,可以多個應用程式之間共享其解決方案。

1.2.4 子產品化

PL/SQL程式結構是一種描述性很強、界限分明的塊結構、嵌套塊結構,被分成單獨的過程、函數、觸發器,且可以把它們組合為程式包,提高程式的子產品化能力。

1.2.5 運作錯誤的可處理性

使用PL/SQL提供的異常處理(EXCEPTION),開發人員可集中處理各種ORACLE錯誤和PL/SQL錯誤,或處理系統錯誤與自定義錯誤,以增強應用程式的健壯性。

1.2.6 提供大量内置程式包

ORACLE提供了大量的内置程式包。通過這些程式包能夠實作DBS的一些低層操作、進階功能,不論對DBA還是應用開發人員都具有重要作用。

當然還有其它的一些優點如:更好的性能、可移植性和相容性、可維護性、易用性與快速性等。

1.3 PL/SQL 可用的SQL語句

INSERT,UPDATE,DELETE,SELECT INTO,COMMIT,ROLLBACK,SAVEPOINT。

提示:在 PL/SQL中隻能用 SQL語句中的 DML 部分,不能用 DDL 部分,如果要在PL/SQL中使用DDL(如CREATE table 等)的話,隻能以動态的方式來使用。

l ORACLE 的 PL/SQL 元件在對 PL/SQL 程式進行解釋時,同時對在其所使用的表名、列名及資料類型進行檢查。

l PL/SQL 可以在SQL*PLUS 中使用。

l PL/SQL 可以在進階語言中使用。

l PL/SQL可以在ORACLE的開發工具中使用(如:SQL Developer或Procedure Builder等)。

l 其它開發工具也可以調用PL/SQL編寫的過程和函數,如Power Builder 等都可以調用伺服器端的PL/SQL過程。

1.4 運作PL/SQL程式

2.1 PL/SQL塊

  PL/SQL程式由三個塊組成,即聲明部分、執行部分、異常處理部分。

  PL/SQL塊的結構如下: 

DECLARE 

--聲明部分: 在此聲明PL/SQL用到的變量,類型及遊标,以及局部的存儲過程和函數

BEGIN

-- 執行部分: 過程及SQL 語句 , 即程式的主要部分

EXCEPTION

-- 執行異常部分: 錯誤處理

END;

其中:執行部分不能省略。

  PL/SQL塊可以分為三類:

  1. 無名塊或匿名塊(anonymous):動态構造,隻能執行一次,可調用其它程式,但不能被其它程式調用。

  2. 命名塊(named):是帶有名稱的匿名塊,這個名稱就是标簽。

  3. 子程式(subprogram):存儲在資料庫中的存儲過程、函數等。當在資料庫上建立好後可以在其它程式中調用它們。

  4. 觸發器(Trigger):當資料庫發生操作時,會觸發一些事件,進而自動執行相應的程式。

  5. 程式包(package):存儲在資料庫中的一組子程式、變量定義。在包中的子程式可以被其它程式包或子程式調用。但如果聲明的是局部子程式,則隻能在定義該局部子程式的塊中調用該局部子程式。

2.2 PL/SQL結構

l PL/SQL塊中可以包含子塊;

l 子塊可以位于 PL/SQL中的任何部分;

l 子塊也即PL/SQL中的一條指令; 

2.3 辨別符

  PL/SQL程式設計中的辨別符定義與SQL 的辨別符定義的要求相同。要求和限制有:

l 辨別符名不能超過30字元;

l 第一個字元必須為字母;

l 不分大小寫;

l 不能用’-‘(減号);

l 不能是SQL保留字。

  提示: 一般不要把變量名聲明與表中字段名完全一樣,如果這樣可能得到不正确的結果.

  例如:下面的例子将會删除所有的紀錄,而不是’EricHu’的記錄; 

DECLARE

ename varchar2(20) :='EricHu';

DELETE FROM scott.emp WHERE ename=ename;

變量命名在PL/SQL中有特别的講究,建議在系統的設計階段就要求所有程式設計人員共同遵守一定的要求,使得整個系統的文檔在規範上達到要求。下面是建議的命名方法:

ORACLE PL/SQL程式設計詳解

2.4 PL/SQL 變量類型

  在前面的介紹中,有系統的資料類型,也可以自定義資料類型。下表給出ORACLE類型和PL/SQL中的變量類型的合法使用清單:

2.4.1 變量類型 

四類資料類型

标量類型(SCALAR,或稱基本資料類型):用于儲存單個值,例如字元串,數字,日期,布爾;

複合類型(COMPOSITE):複合類型可以在内部存放多種數值,類似于多個變量的集合,例如記錄類型,嵌套表,索引表,可變數組等;

引用類型(REFERENCE):用于指向另一個不同對象,例如REF CURSOR,REF;

LOB類型:大資料類型,最多可以存儲4GB的資訊,主要用來處理二進制資料;

标量類型

标量類型也被稱為基本資料類型

常見标量類型

ORACLE PL/SQL程式設計詳解
ORACLE PL/SQL程式設計詳解

數值型

NUMBER資料類型

采用十進制類型,需将十進制轉為二進制進行計算

定義整型:NUMBER(n);

定義浮點型資料:NUMBER(m,n)

執行個體1:定義NUMBER變量

SQL> set serveroutput on

SQL> DECLARE

2 v_x NUMBER(3) ; -- 最多隻能為3位數字

3 v_y NUMBER(5,2) ; -- 3位整數,2位小數

4 BEGIN

5 v_x := -500 ;

6 v_y := 999.88 ;

7 DBMS_OUTPUT.put_line('v_x = ' || v_x) ;

8 DBMS_OUTPUT.put_line('v_y = ' || v_y) ;

9 DBMS_OUTPUT.put_line('加法運算:' || (v_x + v_y)) ; -- 整數 + 浮點數 = 浮點數

10 END ;

11 /

v_x = -500

v_y = 999.88

加法運算:499.88

PL/SQL procedure successfully completed.

BINARY_INTEGER與PLS_INTEGER

說明:

兩者具有相同的範圍長度。與NUMBER比較,占用的範圍更小;

采用二進制補碼存儲,運算性能比NUMBER高;

兩者差別:

BINARY_INTEGER操作的資料大于其資料範圍時,會自動轉換為NUMBER型進行儲存;

PLS_INTEGER操作的資料大于範圍時,會抛出異常資訊

示例1:驗證PLS_INTEGER操作

2 v_pls1 PLS_INTEGER := 100 ;

3 v_pls2 PLS_INTEGER := 200 ;

4 v_result PLS_INTEGER ;

5 BEGIN

6 v_result := v_pls1 + v_pls2 ;

7 DBMS_OUTPUT.put_line('計算結果:' || v_result) ;

8 END ;

9 /

計算結果:300

BINARY_FLOAT與BINARY_DOUBLE

兩者比NUMBER節約空間,同時範圍更大,采用二進制存儲資料;

示例1:驗證BINARY_DOUBLE操作

2 v_float BINARY_FLOAT := 8909.51F ;

3 v_double BINARY_DOUBLE := 8909.51D ;

5 v_float := v_float + 1000.16 ;

6 v_double := v_double + 1000.16 ;

7 DBMS_OUTPUT.put_line('BINARY_FLOAT變量内容:' || v_float) ;

8 DBMS_OUTPUT.put_line('BINARY_DOUBLE變量内容:' || v_double) ;

9 END ;

10 /

BINARY_FLOAT變量内容:9.90966992E+003

BINARY_DOUBLE變量内容:9.9096700000000001E+003

兩者定義的常量:這些常量隻能在PL/SQL中使用。這些常量分别表示BINARY_FLOAT與BINARY_DOUBLE的資料範圍,同時針對非數字與超過其類型最大值的資料标記

ORACLE PL/SQL程式設計詳解

示例2:觀察表示範圍的常量内容

2 BEGIN

3 DBMS_OUTPUT.put_line('1、BINARY_FLOAT_MIN_NORMAL = ' || BINARY_FLOAT_MIN_NORMAL) ;

4 DBMS_OUTPUT.put_line('1、BINARY_FLOAT_MAX_NORMAL = ' || BINARY_FLOAT_MAX_NORMAL) ;

5 DBMS_OUTPUT.put_line('1、BINARY_FLOAT_MIN_SUBNORMAL = ' || BINARY_FLOAT_MIN_SUBNORMAL) ;

6 DBMS_OUTPUT.put_line('1、BINARY_FLOAT_MAX_SUBNORMAL = ' || BINARY_FLOAT_MAX_SUBNORMAL) ;

7 DBMS_OUTPUT.put_line('2、BINARY_DOUBLE_MIN_NORMAL = ' || BINARY_DOUBLE_MIN_NORMAL) ;

8 DBMS_OUTPUT.put_line('2、BINARY_DOUBLE_MAX_NORMAL = ' || BINARY_DOUBLE_MAX_NORMAL) ;

9 DBMS_OUTPUT.put_line('2、BINARY_DOUBLE_MIN_SUBNORMAL = ' || BINARY_DOUBLE_MIN_SUBNORMAL) ;

10 DBMS_OUTPUT.put_line('2、BINARY_DOUBLE_MAX_SUBNORMAL = ' || BINARY_DOUBLE_MAX_SUBNORMAL) ;

11 END ;

12 /

1、BINARY_FLOAT_MIN_NORMAL = 1.17549435E-038

1、BINARY_FLOAT_MAX_NORMAL = 3.40282347E+038

1、BINARY_FLOAT_MIN_SUBNORMAL = 1.40129846E-045

1、BINARY_FLOAT_MAX_SUBNORMAL = 1.17549421E-038

2、BINARY_DOUBLE_MIN_NORMAL = 2.2250738585072014E-308

2、BINARY_DOUBLE_MAX_NORMAL = 1.7976931348623157E+308

2、BINARY_DOUBLE_MIN_SUBNORMAL = 4.9406564584124654E-324

2、BINARY_DOUBLE_MAX_SUBNORMAL = 2.2250738585072009E-308

示例3:超過範圍的計算

3 DBMS_OUTPUT.put_line('超過範圍計算的結果:' ||

4 BINARY_DOUBLE_MAX_NORMAL * BINARY_DOUBLE_MAX_NORMAL) ;

5 DBMS_OUTPUT.put_line('超過範圍計算的結果:' ||

6 BINARY_DOUBLE_MAX_NORMAL / 0) ;

7 END ;

8 /

超過範圍計算的結果:Inf

字元型

CHAR與VARCHAR2

CHAR采用定長方式儲存字元串。如果使用者設定的内容不足其定義長度,則會自動補充空格;

VARCHAR2是可變字元串。如果設定的内容不足其長度,也不會為其補充内容;

示例1:觀察CHAR和VARCHAR2的差別

2 v_info_char CHAR(10) ;

3 v_info_varchar VARCHAR2(10) ;

5 v_info_char := 'MLDN' ; -- 長度不足10個

6 v_info_varchar := 'java' ; -- 長度不足10個

7 DBMS_OUTPUT.put_line('v_info_char内容長度:' || LENGTH(v_info_char)) ;

8 DBMS_OUTPUT.put_line('v_info_varchar内容長度:' || LENGTH(v_info_varchar)) ;

v_info_char内容長度:10

v_info_varchar内容長度:4

NCHAR和NVARCHAR2

兩者的特性與CHAR,VARCHAR2一樣。差別在于它們儲存的資料為UNICODE編碼,中文與英文都會變為十六進制編碼儲存;

示例1:驗證NCHAR和NVARCHAR2

2 v_info_nchar NCHAR(10) ;

3 v_info_nvarchar NVARCHAR2(10) ;

5 v_info_nchar := 'CSDN' ; -- 長度不足10個

6 v_info_nvarchar := '高端教育訓練' ; -- 長度不足10個

7 DBMS_OUTPUT.put_line('v_info_nchar内容長度:' || LENGTH(v_info_nchar)) ;

8 DBMS_OUTPUT.put_line('v_info_nvarchar内容長度:' || LENGTH(v_info_nvarchar)) ;

v_info_nchar内容長度:10

v_info_nvarchar内容長度:8

LONG與LONG RAW

兩者用于向後相容;

LONG說明:

使用LONG的地方都會使用CLOB或NCLOB;

LONG用于存儲字元流;

可以使用"UTL_RAW.cast_to_varchar2(RAW資料)"函數,将RAW轉為字元串

LONG RAW說明:

使用LONG RAW的地方都替換為BLOB或BILE;

LONG RAW用于存儲二進制資料流

為LONG RAW變量設定内容,要使用"UTL_RAW.cast_to_raw(字元串)"進行轉換;

示例1:使用LONG和LONG RAW操作

2 v_info_long LONG ;

3 v_info_longraw LONG RAW ;

5 v_info_long := 'CSDN' ; -- 直接設定字元串

6 v_info_longraw := UTL_RAW.cast_to_raw('高端教育訓練') ; -- 将字元串變為RAW

7 DBMS_OUTPUT.put_line('v_info_long内容:' || v_info_long) ;

8 DBMS_OUTPUT.put_line('v_info_longraw内容:' || UTL_RAW.cast_to_varchar2(v_info_longraw)) ;

v_info_long内容:CSDN

v_info_longraw内容:高端教育訓練

OWID與UROWID

ROWID表示的是一條資料的實體行位址,由18個字元組合而成,與ROWID僞列功能相同;

UROWID具備ROWID的功能,還增加了一個邏輯行位址,在PL/SQL中應将所有的ROWID交給UROWID管理;

示例1:使用ROWID及UROWID

SQL> create synonym emp for scott.emp@CLONEPDB_PLUG;

Synonym created.

2 v_emp_rowid ROWID ;

3 v_emp_urowid UROWID ;

5 SELECT ROWID INTO v_emp_rowid FROM emp WHERE empno=7369 ; -- 取得ROWID

6 SELECT ROWID INTO v_emp_urowid FROM emp WHERE empno=7369 ; -- 取得ROWID

7 DBMS_OUTPUT.put_line('7369雇員的ROWID = ' || v_emp_rowid) ;

8 DBMS_OUTPUT.put_line('7369雇員的UROWID = ' || v_emp_urowid) ;

7369雇員的ROWID = AAAR7bAALAAAACTAAA

7369雇員的UROWID = AAAR7bAALAAAACTAAA

日期型

DATE資料類型

用來存儲日期時間資料;

可通過SYSDATE或SYSTIMESTAMP兩個僞列來擷取目前的日期時間;

主要字段索引:

示例1:定義DATE型變量

2 v_date1 date := SYSDATE;

3 v_date2 date := systimestamp;

4 v_date3 date :=TO_DATE('2015-01-01','YYYY-MM-DD');

6 DBMS_OUTPUT.put_line('日期資料:' || TO_CHAR(v_date1,'yyyy-mm-dd hh24:mi:ss'));

7 DBMS_OUTPUT.put_line('日期資料:' || TO_CHAR(v_date2,'yyyy-mm-dd hh24:mi:ss'));

8 DBMS_OUTPUT.put_line('日期資料:' || TO_CHAR(v_date3,'yyyy-mm-dd hh24:mi:ss'));

9 END;

日期資料:2017-12-19 15:22:01

日期資料:2015-01-01 00:00:00

TIMESTAMP資料類型

該類型與DATE的差別在于,可以提供更為準确的時間。但是要使用SYSTIMESTAMP僞列來為其指派;

如果隻是使用SYSDATE,那麼TIMESTAMP與DATE沒有任何差別;

示例1:定義TIMESTAMP型變量

2 v_timestamp1 TIMESTAMP := SYSDATE;

3 v_timestamp2 TIMESTAMP := SYSTIMESTAMP;

4 v_timestamp3 TIMESTAMP := to_timestamp('2011-12-15 10:40:10.345', 'yyyy-MM-dd HH24:MI:ss.ff');

6 DBMS_OUTPUT.put_line('日期資料:' || v_timestamp1) ;

7 DBMS_OUTPUT.put_line('日期資料:' || v_timestamp2) ;

8 DBMS_OUTPUT.put_line('日期資料:' || v_timestamp3) ;

日期資料:19-DEC-17 03.32.31.000000 PM

日期資料:19-DEC-17 03.32.31.988000 PM

日期資料:15-DEC-11 10.40.10.345000 AM

TIMESTAMP的兩個擴充子類型:

TIMESTAMP WITH TIME ZONE:包含與格林威治時間的時區偏移量

2 v_timestamp TIMESTAMP WITH TIME ZONE := SYSTIMESTAMP ;

3 BEGIN

4 DBMS_OUTPUT.put_line(v_timestamp) ;

5 END ;

6 /

19-DEC-17 03.36.53.909000 PM +08:00

TIMESTAMP WITH LOCAL TIME ZONE:不管是何種時區的資料,都使用目前資料庫的時區;

2 v_timestamp TIMESTAMP WITH LOCAL TIME ZONE := SYSTIMESTAMP ;

19-DEC-17 03.37.32.894000 PM

NTERVAL資料類型

該類型可以儲存兩個時間戳之間的時間間隔,此類型分為兩個子類型:

INTERVAL YEAR[(年的精度)] TO MONTHS:儲存的和操作年與月之間的時間間隔,使用者可以設定年的資料精度。預設值為2;

指派格式:'年-月'

INTERVAL DAY[(天的精度)] TO SECEND[(秒的精度)]:儲存和操作天,時,分,秒之間的時間間隔,預設為2。秒的預設值為6;

指派格式:'天時:分:秒.毫秒'

當取得時間間隔後,可利用下列公式進行計算:

ORACLE PL/SQL程式設計詳解

示例1:定義INTERVAL YEAR TO MONTHS類型變量

2 v_interval INTERVAL YEAR(3) TO MONTH := INTERVAL '27-09' YEAR TO MONTH ;

4 DBMS_OUTPUT.put_line('時間間隔:' || v_interval) ;

5 DBMS_OUTPUT.put_line('目前時間戳 + 時間間隔:' || (SYSTIMESTAMP + v_interval)) ;

6 DBMS_OUTPUT.put_line('目前日期 + 時間間隔:' || (SYSDATE + v_interval)) ;

時間間隔:+027-09

目前時間戳 + 時間間隔:19-SEP-45 03.43.24.742000000 PM +08:00

目前日期 + 時間間隔:2045-09-19 15:43:24

示例2:定義INTERVAL DAY TO SECOND類型變量

2 v_interval INTERVAL DAY(6) TO SECOND (3) := INTERVAL '8 18:19:27.367123909' DAY TO SECOND;

時間間隔:+000008 18:19:27.367

目前時間戳 + 時間間隔:28-DEC-17 10.03.46.657000000 AM +08:00

目前日期 + 時間間隔:2017-12-28 10:03:46

布爾型

該類型儲存TRUE,FALSE,NULL

示例1:定義布爾型變量

2 v_flag BOOLEAN ;

4 v_flag := true ;

5 IF v_flag THEN

6 DBMS_OUTPUT.put_line('條件滿足。') ;

7 END IF ;

條件滿足。

子類型

在某一标量類型的基礎上定義更多限制,進而建立一個新的類型,這種新類型被稱為子類型;

建立文法:

subtype 子類型名稱 is 父資料類型[(限制)] [not null];

在定義子類型的時候必須設定好父資料類型。父類型可以是Oracle的各種資料類型

示例1:定義NUMBER子類型

2 SUBTYPE score_subtype IS NUMBER(5,2) NOT NULL ;

3 v_score score_subtype := 99.35 ; --依據score_subtype類型為v_score變量指派為99.35

5 DBMS_OUTPUT.put_line('成績為:' || v_score) ;

6 END ;

7 /

成績為:99.35

示例2:定義VARCHAR2子類型

2 SUBTYPE string_subtype IS VARCHAR2(200) ;

3 v_company string_subtype ;--聲明一個子類型為 string_subtype的變量v_company

5 v_company := 'test' ;

6 DBMS_OUTPUT.put_line(v_company) ;

test

RETURNING子句

SQL> set serveroutput on;

2 Row_id ROWID;

3 info VARCHAR2(40);

5 INSERT INTO dept VALUES (90, '财務室', '海口')

6 RETURNING rowid, dname||':'||to_char(deptno)||':'||loc

7 INTO row_id, info;

8 DBMS_OUTPUT.PUT_LINE('ROWID:'||row_id);

9 DBMS_OUTPUT.PUT_LINE(info);

10 END;

ROWID:AAAR7ZAALAAAACFAAA

财務室:90:海口

RETURNING子句用于檢索INSERT語句中所影響的資料行數,當INSERT語句使用VALUES 子句插入資料時,RETURNING 字句還可将清單達式、ROWID和REF值傳回到輸出變量中。在使用RETURNING 子句是應注意以下幾點限制:

  1.不能與DML語句和遠端對象一起使用;

  2.不能檢索LONG 類型資訊;

  3.當通過視圖向基表中插入資料時,隻能與單基表視圖一起使用。 

例2. 修改一條記錄并顯示

5 UPDATE dept SET deptno=10 WHERE DNAME='ACCOUNTING'

ROWID:AAAR7ZAALAAAACDAAA

ACCOUNTING:10:NEW YORK

RETURNING子句用于檢索被修改行的資訊。當UPDATE語句修改單行資料時,RETURNING 子句可以檢索被修改行的ROWID和REF值,以及行中被修改列的清單達式,并可将他們存儲到PL/SQL變量或複合變量中;當UPDATE語句修改多行資料時,RETURNING 子句可以将被修改行的ROWID和REF值,以及清單達式值傳回到複合變量數組中。在UPDATE中使用RETURNING 子句的限制與INSERT語句中對RETURNING子句的限制相同。

  例3.删除一條記錄并顯示 

5 DELETE dept WHERE DNAME='OPERATIONS'

ROWID:AAAR7ZAALAAAACDAAD

OPERATIONS:40:BOSTON

RETURNING子句用于檢索被删除行的資訊:當DELETE語句删除單行資料時,RETURNING 子句可以檢索被删除行的ROWID和REF值,以及被删除列的清單達式,并可将他們存儲到PL/SQL變量或複合變量中;當DELETE語句删除多行資料時,RETURNING 子句可以将被删除行的ROWID和REF值,以及清單達式值傳回到複合變量數組中。在DELETE中使用RETURNING 子句的限制與INSERT語句中對RETURNING子句的限制相同。

2.4.2 複合類型

2.4.2.1 記錄類型

記錄類型類似于C語言中的結構資料類型,它把邏輯相關的、分離的、基本資料類型的變量組成一個整體存儲起來,它必須包括至少一個标量型或RECORD 資料類型的成員,稱作PL/SQL RECORD 的域(FIELD),其作用是存放互不相同但邏輯相關的資訊。在使用記錄資料類型變量時,需要先在聲明部分先定義記錄的組成、記錄的變量,然後在執行部分引用該記錄變量本身或其中的成員。

  定義記錄類型文法如下:

TYPE record_name IS RECORD(

v1 data_type1 [NOT NULL] [:= default_value ],

v2 data_type2 [NOT NULL] [:= default_value ],

......

vn data_typen [NOT NULL] [:= default_value ] );

例1 :

2 TYPE test_rec IS RECORD(

3 Name VARCHAR2(30) NOT NULL := '我的自學',

4 Info VARCHAR2(100));

5 rec_book test_rec;

6 BEGIN

7 rec_book.Info :='Oracle PL/SQL程式設計;';

8 DBMS_OUTPUT.PUT_LINE(rec_book.Name||' ' ||rec_book.Info);

我的自學 Oracle PL/SQL程式設計;

可以用 SELECT語句對記錄變量進行指派,隻要保證記錄字段與查詢結果清單中的字段相配即可。 

例2 :

SQL> conn hr/hr@pdbtest

Connected.

2 --定義與employees表中的這幾個列相同的記錄資料類型

3 TYPE RECORD_TYPE_EMPLOYEES IS RECORD(

4 f_name employees.first_name%TYPE,

5 h_date employees.hire_date%TYPE,

6 j_id employees.job_id%TYPE);

7 --聲明一個該記錄資料類型的記錄變量

8 v_emp_record RECORD_TYPE_EMPLOYEES;

9

10 BEGIN

11 SELECT first_name, hire_date, job_id INTO v_emp_record

12 FROM employees

13 WHERE employee_id = &emp_id;

14

15 DBMS_OUTPUT.PUT_LINE('雇員名稱:'||v_emp_record.f_name

16 ||' 雇傭日期:'||v_emp_record.h_date

17 ||' 崗位:'||v_emp_record.j_id);

18 END;

19 /

Enter value for emp_id: 206

old 13: WHERE employee_id = &emp_id;

new 13: WHERE employee_id = 206;

雇員名稱:William 雇傭日期:2002-06-07 00:00:00 崗位:AC_ACCOUNT

一個記錄類型的變量隻能儲存從資料庫中查詢出的一行記錄,若查詢出了多行記錄,就會出現錯誤。 

2.4.2.2 數組類型

資料是具有相同資料類型的一組成員的集合。每個成員都有一個唯一的下标,它取決于成員在數組中的位置。在PL/SQL中,數組資料類型是VARRAY。

定義VARRY資料類型文法如下:

TYPE varray_name IS VARRAY(size) OF element_type [NOT NULL];

varray_name是VARRAY資料類型的名稱,size是下整數,表示可容納的成員的最大數量,每個成員的資料類型是element_type。預設成員可以取空值,否則需要使用NOT NULL加以限制。對于VARRAY資料類型來說,必須經過三個步驟,分别是:定義、聲明、初始化。 

例1 : 

2 --定義一個最多儲存5個VARCHAR(25)資料類型成員的VARRAY資料類型

3 TYPE reg_varray_type IS VARRAY(5) OF VARCHAR(25);

4 --聲明一個該VARRAY資料類型的變量

5 v_reg_varray REG_VARRAY_TYPE;

6

7 BEGIN

8 --用構造函數文法賦予初值

9 v_reg_varray := reg_varray_type

10 ('中國', '美國', '英國', '日本', '法國');

11

12 DBMS_OUTPUT.PUT_LINE('地區名稱:'||v_reg_varray(1)||'、'

13 ||v_reg_varray(2)||'、'

14 ||v_reg_varray(3)||'、'

15 ||v_reg_varray(4));

16 DBMS_OUTPUT.PUT_LINE('賦予初值NULL的第5個成員的值:'||v_reg_varray(5));

17 --用構造函數文法賦予初值後就可以這樣對成員指派

18 v_reg_varray(5) := '法國';

19 DBMS_OUTPUT.PUT_LINE('第5個成員的值:'||v_reg_varray(5));

20 END;

21 /

地區名稱:中國、美國、英國、日本

賦予初值NULL的第5個成員的值:法國

第5個成員的值:法國

2.4.2.3 使用%TYPE

定義一個變量,其資料類型與已經定義的某個資料變量(尤其是表的某一列)的資料類型相一緻,這時可以使用%TYPE。

  使用%TYPE特性的優點在于:

  l 所引用的資料庫列的資料類型可以不必知道;

  l 所引用的資料庫列的資料類型可以實時改變,容易保持一緻,也不用修改PL/SQL程式。

  例1: 

SQL> conn scott/tiger@clonepdb_plug

2 -- 用%TYPE 類型定義與表相配的字段

3 TYPE T_Record IS RECORD(

4 T_no emp.empno%TYPE,

5 T_name emp.ename%TYPE,

6 T_sal emp.sal%TYPE );

7 -- 聲明接收資料的變量

8 v_emp T_Record;

9 BEGIN

10 SELECT empno, ename, sal INTO v_emp FROM emp WHERE empno=7782;

11 DBMS_OUTPUT.PUT_LINE

12 (TO_CHAR(v_emp.t_no)||' '||v_emp.t_name||' ' || TO_CHAR(v_emp.t_sal));

13 END;

14 /

7782 CLARK 2450

例2: 

2 v_empno emp.empno%TYPE :=&no;

3 Type t_record is record (

4 v_name emp.ename%TYPE,

5 v_sal emp.sal%TYPE,

6 v_date emp.hiredate%TYPE);

7 Rec t_record;

8 BEGIN

9 SELECT ename, sal, hiredate INTO Rec FROM emp WHERE empno=v_empno;

10 DBMS_OUTPUT.PUT_LINE(Rec.v_name||'---'||Rec.v_sal||'--'||Rec.v_date);

11 END;

Enter value for no: 7782

old 2: v_empno emp.empno%TYPE :=&no;

new 2: v_empno emp.empno%TYPE :=7782;

CLARK---2450--1981-06-09 00:00:00

2.4.3 使用%ROWTYPE

PL/SQL 提供%ROWTYPE操作符, 傳回一個記錄類型, 其資料類型和資料庫表的資料結構相一緻。

  使用%ROWTYPE特性的優點在于:

  l 所引用的資料庫中列的個數和資料類型可以不必知道;

  l 所引用的資料庫中列的個數和資料類型可以實時改變,容易保持一緻,也不用修改PL/SQL程式。 

例1: 

3 rec emp%ROWTYPE;

5 SELECT * INTO rec FROM emp WHERE empno=v_empno;

6 DBMS_OUTPUT.PUT_LINE('姓名:'||rec.ename||'工資:'||rec.sal||'工作時間:'||rec.hiredate);

7 END;

姓名:CLARK工資:2450工作時間:1981-06-09 00:00:00

2.4.4 LOB類型

  ORACLE提供了LOB (Large OBject)類型,用于存儲大的資料對象的類型。ORACLE目前主要支援BFILE, BLOB, CLOB 及 NCLOB 類型。

BFILE (Movie)

BLOB(Photo)

CLOB(Book)

NCLOB

2.4.5 BIND 變量

  綁定變量是在主機環境中定義的變量。在PL/SQL 程式中可以使用綁定變量作為他們将要使用的其它變量。為了在PL/SQL 環境中聲明綁定變量,使用指令VARIABLE。例如:

VARIABLE return_code NUMBER

VARIABLE return_msg VARCHAR2(20)

可以通過SQLPlus指令中的PRINT 顯示綁定變量的值。例如: 

PRINT return_code

PRINT return_msg

例1:

SQL> VARIABLE result NUMBER;

SQL> BEGIN

2 SELECT (sal10)+nvl(comm, 0) INTO :result FROM emp

3 WHERE empno=7844;

4 END;

5 /

SQL> --然後再執行

SQL> PRINT result

2.4.6 PL/SQL 表(TABLE) 

定義記錄表(或索引表)資料類型。它與記錄類型相似,但它是對記錄類型的擴充。它可以處理多行記錄,類似于進階中的二維數組,使得可以在PL/SQL中模仿資料庫中的表。

定義記錄表類型的文法如下:

TYPE table_name IS TABLE OF element_type [NOT NULL]

INDEX BY [BINARY_INTEGER | PLS_INTEGER | VARRAY2];

關鍵字INDEX BY表示建立一個主鍵索引,以便引用記錄表變量中的特定行。 

ORACLE PL/SQL程式設計詳解

2 TYPE dept_table_type IS TABLE OF

3 dept%ROWTYPE INDEX BY BINARY_INTEGER;

4 my_dname_table dept_table_type;

5 v_count number(2) :=3;

7 FOR int IN 1 .. v_count LOOP

8 SELECT INTO my_dname_table(int) FROM dept WHERE deptno=int10;

9 END LOOP;

10 FOR int IN my_dname_table.FIRST .. my_dname_table.LAST LOOP

11 DBMS_OUTPUT.PUT_LINE('Department number: '||my_dname_table(int).deptno);

12 DBMS_OUTPUT.PUT_LINE('Department name: '|| my_dname_table(int).dname);

13 END LOOP;

14 END;

15 /

Department number: 10

Department name: ACCOUNTING

Department number: 20

Department name: RESEARCH

Department number: 30

Department name: SALES

例2:按一維數組使用記錄表 

2 --定義記錄表資料類型

3 TYPE reg_table_type IS TABLE OF varchar2(25)

4 INDEX BY BINARY_INTEGER;

5 --聲明記錄表資料類型的變量

6 v_reg_table REG_TABLE_TYPE;

7

9 v_reg_table(1) := 'Europe';

10 v_reg_table(2) := 'Americas';

11 v_reg_table(3) := 'Asia';

12 v_reg_table(4) := 'Middle East and Africa';

13 v_reg_table(5) := 'NULL';

15 DBMS_OUTPUT.PUT_LINE('地區名稱:'||v_reg_table (1)||'、'

16 ||v_reg_table (2)||'、'

17 ||v_reg_table (3)||'、'

18 ||v_reg_table (4));

19 DBMS_OUTPUT.PUT_LINE('第5個成員的值:'||v_reg_table(5));

地區名稱:Europe、Americas、Asia、Middle East and Africa

第5個成員的值:NULL

PL/SQL procedure successfully completed

例3:按二維數組使用記錄表 

3 TYPE emp_table_type IS TABLE OF employees%ROWTYPE

6 v_emp_table EMP_TABLE_TYPE;

8 SELECT first_name, hire_date, job_id INTO

9 v_emp_table(1).first_name,v_emp_table(1).hire_date, v_emp_table(1).job_id

10 FROM employees WHERE employee_id = 177;

11 SELECT first_name, hire_date, job_id INTO

12 v_emp_table(2).first_name,v_emp_table(2).hire_date, v_emp_table(2).job_id

13 FROM employees WHERE employee_id = 178;

15 DBMS_OUTPUT.PUT_LINE('177雇員名稱:'||v_emp_table(1).first_name

16 ||' 雇傭日期:'||v_emp_table(1).hire_date

17 ||' 崗位:'||v_emp_table(1).job_id);

18 DBMS_OUTPUT.PUT_LINE('178雇員名稱:'||v_emp_table(2).first_name

19 ||' 雇傭日期:'||v_emp_table(2).hire_date

20 ||' 崗位:'||v_emp_table(2).job_id);

21 END;

22 /

177雇員名稱:Jack 雇傭日期:2006-04-23 00:00:00 崗位:SA_REP

178雇員名稱:Kimberely 雇傭日期:2007-05-24 00:00:00 崗位:SA_REP

2.5 運算符和表達式(資料定義)

2.5.1 關系運算符 

ORACLE PL/SQL程式設計詳解

2.5.2 一般運算符 

ORACLE PL/SQL程式設計詳解

2.5.3 邏輯運算符 

ORACLE PL/SQL程式設計詳解

2.6 變量指派

  在PL/SQL程式設計中,變量指派是一個值得注意的地方,它的文法如下:

variable := expression ;

variable 是一個PL/SQL變量, expression 是一個PL/SQL 表達式.

2.6.1 字元及數字運算特點

  空值加數字仍是空值:NULL + < 數字> = NULL

  空值加(連接配接)字元,結果為字元:NULL || <字元串> = < 字元串>

2.6.2 BOOLEAN 指派

  布爾值隻有TRUE, FALSE及 NULL 三個值。

2.6.3 資料庫指派

  資料庫指派是通過 SELECT語句來完成的,每次執行 SELECT語句就指派一次,一般要求被指派的變量與SELECT中的列名要一一對應。如:

  例1:

2 emp_id emp.empno%TYPE :=7782;

3 emp_name emp.ename%TYPE;

4 wages emp.sal%TYPE;

6 SELECT ename, NVL(sal,0) + NVL(comm,0) INTO emp_name, wages

7 FROM emp WHERE empno = emp_id;

8 DBMS_OUTPUT.PUT_LINE(emp_name||'----'||to_char(wages));

CLARK----2450

提示:不能将SELECT語句中的列指派給布爾變量。

2.6.4 可轉換的類型指派

l CHAR 轉換為 NUMBER:

使用 TO_NUMBER 函數來完成字元到數字的轉換,如:

v_total := TO_NUMBER('100.0') + sal;

l NUMBER 轉換為CHAR:

v_comm := TO_CHAR('123.45') || '元' ;

l 字元轉換為日期:

使用 TO_DATE函數可以實作 字元到日期的轉換,如:

v_date := TO_DATE('2001.07.03','yyyy.mm.dd');

l 日期轉換為字元

使用 TO_CHAR函數可以實作日期到字元的轉換,如:

v_to_day := TO_CHAR(SYSDATE, 'yyyy.mm.dd hh24:mi:ss') ;

2.7 變量作用範圍及可見性

  在PL/SQL程式設計中,如果在變量的定義上沒有做到統一的話,可能會隐藏一些危險的錯誤,這樣的原因主要是變量的作用範圍所緻。變量的作用域是指變量的有效作用範圍,與其它進階語言類似,PL/SQL的變量作用範圍特點是:

l 變量的作用範圍是在你所引用的程式單元(塊、子程式、包)内。即從聲明變量開始到該塊的結束。

l 一個變量(辨別)隻能在你所引用的塊内是可見的。

l 當一個變量超出了作用範圍,PL/SQL引擎就釋放用來存放該變量的空間(因為它可能不用了)。

l 在子塊中重新定義該變量後,它的作用僅在該塊内。 

2 Emess char(80);

4

5 DECLARE

6 V1 NUMBER(4);

8 SELECT empno INTO v1 FROM emp WHERE LOWER(job)='president';

9 DBMS_OUTPUT.PUT_LINE(V1);

10 EXCEPTION

11 When TOO_MANY_ROWS THEN

12 DBMS_OUTPUT.PUT_LINE ('More than one president');

15 DECLARE

16 V1 NUMBER(4);

17 BEGIN

18 SELECT empno INTO v1 FROM emp WHERE LOWER(job)='manager';

19 EXCEPTION

20 When TOO_MANY_ROWS THEN

21 DBMS_OUTPUT.PUT_LINE ('More than one manager');

22 END;

23

24 EXCEPTION

25 When others THEN

26 Emess:=substr(SQLERRM,1,80);

27 DBMS_OUTPUT.PUT_LINE(emess);

28 END;

29 /

7839

More than one manager

2.8 注釋

l 使用雙 ‘-‘ ( 減号) 加注釋

PL/SQL允許用 – 來寫注釋,它的作用範圍是隻能在一行有效。如:

V_Sal NUMBER(12,2); -- 人員的工資變量。

l 使用 / / 來加一行或多行注釋,如:

//

/ 檔案名: /

/ 作 者: /

/ 時 間: /

提示:被解釋後存放在資料庫中的 PL/SQL 程式,一般系統自動将程式頭部的注釋去掉。隻有在 PROCEDURE 之後的注釋才被保留;另外程式中的空行也自動被去掉。

     本文轉自whshurk 51CTO部落格,原文連結:http://blog.51cto.com/shurk/2051989,如需轉載請自行聯系原作者