天天看點

[頂]ORACLE PL/SQL程式設計詳解之二: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):存儲在資料庫中的一組子程式、變量定義。在包中的子程式可以被其它程式包或子程式調用。但如果聲明的是局部子程式,則隻能在定義該局部子程式的塊中調用該局部子程式。

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

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

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

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

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

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

l       不分大小寫;

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

l       不能是SQL保留字。

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

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

DECLARE

   ename varchar2(20) :='EricHu';

BEGIN

  DELETE FROM scott.emp WHERE ename=ename;

END;

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

辨別符

命名規則

例子

程式變量

V_name

程式常量

C_Name

C_company_name

遊标變量

Cursor_Name

Cursor_Emp

異常辨別

E_name

E_too_many

表類型

Name_table_type

Emp_record_type

Name_table

Emp

記錄類型

Name_record

Emp_record

SQL*Plus 替代變量

P_name

P_sal

綁定變量

G_name

G_year_sal

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

在ORACLE8i中可以使用的變量類型有:

類型

子類

說     明

範   圍

ORACLE限制

CHAR

Character

String

Rowid

Nchar

定長字元串

民族語言字元集

0à32767

可選,确省=1

2000

VARCHAR2

Varchar, String

NVARCHAR2

可變字元串

4000

BINARY_INTEGER

帶符号整數,為整數計算優化性能

NUMBER(p,s)

Dec

Double precision

Integer

Int

Numeric

Real

Small int

小數, NUMBER 的子類型

高精度實數

整數, NUMBER 的子類型

與NUMBER等價

整數, 比 integer 小

LONG

變長字元串

0->2147483647

32,767位元組

DATE

日期型

公元前4712年1月1日至公元後4712年12月31日

BOOLEAN

布爾型

TRUE, FALSE,NULL

不使用

ROWID

存放資料庫行号

UROWID

通用行辨別符,字元類型

例1. 插入一條記錄并顯示;

   Row_id ROWID;

   info    VARCHAR2(40);

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

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

  INTO row_id, info;

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

  DBMS_OUTPUT.PUT_LINE(info);

其中:

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

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

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

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

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

   info   VARCHAR2(40);

  UPDATE dept SET deptno=100 WHERE DNAME='财務室'

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

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

  DELETE dept WHERE DNAME='辦公室'

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

    ORACLE 在 PL/SQL 中除了提供象前面介紹的各種類型外,還提供一種稱為複合類型的類型---記錄和表.

記錄類型類似于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 ] );

例4 :

DECLARE 

   TYPE test_rec IS RECORD(

         Name VARCHAR2(30) NOT NULL := '胡勇',

         Info VARCHAR2(100));

   rec_book test_rec;

   rec_book.Name :='胡勇';

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

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

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

例5 :

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

   TYPE RECORD_TYPE_EMPLOYEES IS RECORD(

        f_name   hr.employees.first_name%TYPE,

        h_date   hr.employees.hire_date%TYPE,

        j_id     hr.employees.job_id%TYPE);

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

   v_emp_record RECORD_TYPE_EMPLOYEES;

   SELECT first_name, hire_date, job_id INTO v_emp_record

   FROM employees

   WHERE employee_id = &emp_id;

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

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

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

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

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

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

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

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

例6 :

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

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

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

   v_reg_varray REG_VARRAY_TYPE;

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

   v_reg_varray := reg_varray_type

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

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

                                    ||v_reg_varray(2)||'、'

                                    ||v_reg_varray(3)||'、'

                                    ||v_reg_varray(4));

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

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

   v_reg_varray(5) := '法國';

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

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

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

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

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

例7:

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

   TYPE T_Record IS RECORD(

        T_no emp.empno%TYPE,

        T_name emp.ename%TYPE,

        T_sal emp.sal%TYPE );

   -- 聲明接收資料的變量

   v_emp T_Record;

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

   DBMS_OUTPUT.PUT_LINE

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

例8:

   v_empno emp.empno%TYPE :=&no;

   Type t_record is record (

        v_name   emp.ename%TYPE,

        v_sal    emp.sal%TYPE,

        v_date   emp.hiredate%TYPE);

   Rec t_record;

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

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

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

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

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

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

例9:

    v_empno emp.empno%TYPE :=&no;

    rec emp%ROWTYPE;

    SELECT * INTO rec FROM emp WHERE empno=v_empno;

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

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

BFILE (Movie)

    存放大的二進制資料對象,這些資料檔案不放在資料庫裡,而是放在作業系統的某個目錄裡,資料庫的表裡隻存放檔案的目錄。

BLOB(Photo)

    存儲大的二進制資料類型。變量存儲大的二進制對象的位置。大二進制對象的大小<=4GB。

CLOB(Book)

    存儲大的字元資料類型。每個變量存儲大字元對象的位置,該位置指到大字元資料塊。大字元對象的大小<=4GB。

NCLOB

    存儲大的NCHAR字元資料類型。每個變量存儲大字元對象的位置,該位置指到大字元資料塊。大字元對象的大小<=4GB。

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

VARIABLE return_code NUMBER

VARIABLE return_msg VARCHAR2(20)

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

PRINT return_code

PRINT return_msg

例10:

VARIABLE result NUMBER;

  SELECT (sal*10)+nvl(comm, 0) INTO :result FROM emp 

  WHERE empno=7844;

--然後再執行

PRINT result

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

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

TYPE table_name IS TABLE OF element_type [NOT NULL]

INDEX BY [BINARY_INTEGER | PLS_INTEGER | VARRAY2];

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

方法

描述

EXISTS(n)

如果集合的第n個成員存在,則傳回true

COUNT

傳回已經配置設定了存儲空間即指派了的成員數量

FIRST

LAST

FIRST:傳回成員的最低下标值

LAST: 傳回成員的最高下标值

PRIOR(n)

傳回下标為n的成員的前一個成員的下标。如果沒有則傳回NULL

NEXT(N)

傳回下标為n的成員的後一個成員的下标。如果沒有則傳回NULL

TRIM

TRIM:删除末尾一個成員

TRIM(n) :删除末尾n個成員

DELETE

DELETE:删除所有成員

DELETE(n) :删除第n個成員

DELETE(m, n) :删除從n到m的成員

EXTEND

EXTEND:添加一個null成員

EXTEND(n):添加n個null成員

EXTEND(n,i):添加n個成員,其值與第i個成員相同

LIMIT

傳回在varray類型變量中出現的最高下标值

例11:

  TYPE dept_table_type IS TABLE OF 

       dept%ROWTYPE INDEX BY BINARY_INTEGER;

  my_dname_table dept_table_type;

  v_count number(2) :=4;

  FOR int IN 1 .. v_count LOOP

    SELECT * INTO my_dname_table(int) FROM dept WHERE deptno=int*10;

  END LOOP;

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

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

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

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

--定義記錄表資料類型

   TYPE reg_table_type IS TABLE OF varchar2(25)

   INDEX BY BINARY_INTEGER;

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

   v_reg_table REG_TABLE_TYPE;

   v_reg_table(1) := 'Europe';

   v_reg_table(2) := 'Americas';

   v_reg_table(3) := 'Asia';

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

   v_reg_table(5) := 'NULL';

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

                                    ||v_reg_table (2)||'、'

                                    ||v_reg_table (3)||'、'

                                    ||v_reg_table (4));

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

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

   TYPE emp_table_type IS TABLE OF employees%ROWTYPE

   v_emp_table EMP_TABLE_TYPE;

   SELECT first_name, hire_date, job_id INTO

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

   FROM employees WHERE employee_id = 177;

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

   FROM employees WHERE employee_id = 178;

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

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

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

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

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

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

運算符

意義

=

等于

<> , != , ~= , ^=

不等于

< 

小于

> 

大于

<=

小于或等于

>=

大于或等于

+

加号

-

減号

*

乘号

/

除号

:=

指派号

=>

關系号

..

範圍運算符

||

字元連接配接符

IS NULL

是空值 

BETWEEN  AND

介于兩者之間

IN

在一列值中間 

AND

邏輯與

OR

邏輯或

NOT

取返,如IS NOT NULL, NOT IN

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

   variable  := expression ;

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

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

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

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

  bDone BOOLEAN;

  bDone := FALSE;

  WHILE NOT bDone LOOP

  Null;

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

例14:

  emp_id    emp.empno%TYPE :=7788;

  emp_name  emp.ename%TYPE;

  wages     emp.sal%TYPE;

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

  FROM emp WHERE empno = emp_id;

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

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

l       CHAR 轉換為 NUMBER:

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

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

l       NUMBER 轉換為CHAR:

    使用 TO_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') ;

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

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

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

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

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

例15:

   Emess char(80);

   DECLARE

      V1 NUMBER(4);

   BEGIN

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

      DBMS_OUTPUT.PUT_LINE(V1);

   EXCEPTION

      When TOO_MANY_ROWS THEN

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

   END;

   DECLARE 

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

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

EXCEPTION

   When others THEN

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

      DBMS_OUTPUT.PUT_LINE(emess);

    在PL/SQL裡,可以使用兩種符号來寫注釋,即:

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

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

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

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

/***********************************************/

/* 檔案名: department_salary.sql      */

/* 作 者: EricHu                     */

/* 時 間: 2011-5-9                  */

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

例16:

/* 檔案名: test.sql                  */

/* 說 明:

       一個簡單的插入測試,無實際應用。*/

/* 作 者: EricHu                     */

/* 時 間: 2011-5-9                  */

  v_ename   VARCHAR2(20) := 'Bill';

  v_sal       NUMBER(7,2) :=1234.56;

  v_deptno   NUMBER(2) := 10;

  v_empno   NUMBER(4) := 8888;

  INSERT INTO emp ( empno, ename, JOB, sal, deptno , hiredate )  

  VALUES (v_empno, v_ename, 'Manager', v_sal, v_deptno, 

            TO_DATE('1954.06.09','yyyy.mm.dd') );

  COMMIT;

例17:

/* 檔案名: test_deletedata.sql      */

       簡單的删除例子,不是實際應用。 */

VALUES ( v_empno, v_ename, ‘Manager’, v_sal, v_deptno, 

TO_DATE(’1954.06.09’,’yyyy.mm.dd’) );

 DECLARE

  v_empno   number(4) := 8888;

  DELETE FROM emp WHERE empno=v_empno;

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