ORACLE PL/SQL編程總結(一)


----------PL/SQL 程序設計簡介-----------

1.1   SQL與PL/SQL

1.1.1   什么是PL/SQL?

        PL/SQL是 Procedure Language & Structured Query Language 的縮寫。PL/SQL是對SQL語言存儲過程語言的擴展。從ORACLE6以后,ORACLE的RDBMS附帶了PL/SQL。它現在已經成為一種過程處理語言,簡稱PL/SQL。目前的PL/SQL包括兩部分,一部分是數據庫引擎部分;另一部分是可嵌入到許多產品(如C語言,JAVA語言等)工具中的獨立引擎。可以將這兩部分稱為:數據庫PL/SQL和工具PL/SQL。兩者的編程非常相似。都具有編程結構、語法和邏輯機制。工具PL/SQL另外還增加了用於支持工具(如ORACLE Forms)的句法,如:在窗體上設置按鈕等。

 

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語句

       PL/SQL是ORACLE系統的核心語言,現在ORACLE的許多部件都是由PL/SQL寫成。在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程序

       PL/SQL程序的運行是通過ORACLE中的一個引擎來進行的。這個引擎可能在ORACLE的服務器端,也可能在 ORACLE 應用開發的客戶端。引擎執行PL/SQL中的過程性語句,然后將SQL語句發送給數據庫服務器來執行。再將結果返回給執行端。

 

************************************************

---------PL/SQL塊結構和組成元素--------

2.1   PL/SQL塊

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

   PL/SQL塊的結構如下: 

 

1  DECLARE 
2     --聲明部分: 在此聲明PL/SQL用到的變量,類型及游標,以及局部的存儲過程和函數
3   BEGIN
4     -- 執行部分:  過程及SQL 語句  , 即程序的主要部分
5   EXCEPTION
6     -- 執行異常部分: 錯誤處理
7   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’的記錄;

1   DECLARE
2       ename varchar2(20) := 'EricHu' ;
3   BEGIN
4      DELETE  FROM  scott.emp  WHERE  ename=ename;
5   END ;

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

標識符

命名規則

例子

程序變量

V_name

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

 

 2.4   PL/SQL 變量類型

   在前面的介紹中,有系統的數據類型,也可以自定義數據類型。

2.4.1   變量類型

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

類型

子類

說     明

范   圍

ORACLE限制

CHAR

Character

String

Rowid

Nchar

定長字符串

 

 

民族語言字符集

0à32767

可選,確省=1

2000

VARCHAR2

Varchar, String

NVARCHAR2

可變字符串

民族語言字符集

0à32767

4000

4000

BINARY_INTEGER

 

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

   

NUMBER(p,s)

Dec

 

Double precision

Integer

Int

Numeric

Real

Small int

小數, NUMBER 的子類型

高精度實數

整數, NUMBER 的子類型

整數, NUMBER 的子類型

與NUMBER等價

與NUMBER等價

整數, 比 integer 小

   

LONG

 

變長字符串

0->2147483647

32,767字節

DATE

 

日期型

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

 

BOOLEAN

 

布爾型

TRUE, FALSE,NULL

不使用

ROWID

 

存放數據庫行號

   

UROWID

 

通用行標識符,字符類型

   

在使用RETURNING 子句是應注意以下幾點限制:

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

 2.不能檢索LONG 類型信息;

 3.當通過視圖向基表中插入數據時,只能與單基表視圖一起使用。

 

2.4.2   復合類型

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

2.4.2.1   記錄類型

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

定義記錄類型語法如下:

1   TYPE record_name  IS  RECORD(
2       v1 data_type1  [ NOT  NULL ]  [:= default_value ],
3       v2 data_type2  [ NOT  NULL ]  [:= default_value ],
4       ......
5       vn data_typen  [ NOT  NULL ]  [:= default_value ] );

例子:

1   DECLARE
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. Name  := '胡勇' ;
8       rec_book.Info := '談PL/SQL編程;' ;
9       DBMS_OUTPUT.PUT_LINE(rec_book. Name || '  '  ||rec_book.Info);
10   END ;

 

可以用 SELECT語句對記錄變量進行賦值,只要保證記錄字段與查詢結果列表中的字段相配即可。

2.4.2.2  數組類型

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

定義VARRY數據類型語法如下:

TYPE varray_name IS VARRAY(sizeOF element_type [NOT NULL];

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

例子:

DECLARE
--定義一個最多保存5個VARCHAR(25)數據類型成員的VARRAY數據類型
    TYPE reg_varray_type  IS  VARRAY(5)  OF  VARCHAR (25);
--聲明一個該VARRAY數據類型的變量
    v_reg_varray REG_VARRAY_TYPE;
 
BEGIN
--用構造函數語法賦予初值
    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));
END ;

 

2.4.2.3  使用%TYPE

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

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

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

  l         所引用的數據庫列的數據類型可以實時改變,容易保持一致,也不用修改PL/SQL程序。

DECLARE
    -- 用%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;
BEGIN
    SELECT  empno, ename, sal  INTO  v_emp  FROM  emp  WHERE  empno=7369;
    DBMS_OUTPUT.PUT_LINE
     (TO_CHAR(v_emp.t_no)|| ' ' ||v_emp.t_name|| '  '  || TO_CHAR(v_emp.t_sal));
END ;

 

2.4.3  使用%ROWTYPE

PL/SQL 提供%ROWTYPE操作符, 返回一個記錄類型, 其數據類型和數據庫表的數據結構相一致。

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

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

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

DECLARE
     v_empno emp.empno%TYPE :=& no ;
     rec emp%ROWTYPE;
BEGIN
     SELECT  INTO  rec  FROM  emp  WHERE  empno=v_empno;
     DBMS_OUTPUT.PUT_LINE( '姓名:' ||rec.ename|| '工資:' ||rec.sal|| '工作時間:' ||rec.hiredate);
END ;

 

2.4.4  LOB類型

ORACLE提供了LOB (Large OBject)類型,用於存儲大的數據對象的類型。ORACLE目前主要支持BFILE, BLOB, CLOB 及 NCLOB 類型。 

BFILE (Movie)

    存放大的二進制數據對象,這些數據文件不放在數據庫里,而是放在操作系統的某個目錄里,數據庫的表里只存放文件的目錄。 

BLOB(Photo)

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

CLOB(Book)

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

NCLOB

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

 

2.4.5  BIND變量

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

VARIABLE return_code NUMBER
VARIABLE return_msg VARCHAR2(20)

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

PRINT return_code
PRINT return_msg

例子:

VARIABLE result NUMBER;

BEGIN
   SELECT  (sal*10)+nvl(comm, 0)  INTO  :result  FROM  emp
   WHERE  empno=7369;
END ;
--然后再執行
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表示創建一個主鍵索引,以便引用記錄表變量中的特定行。 

方法

描述

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類型變量中出現的最高下標值

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

DECLARE
--定義記錄表數據類型
    TYPE reg_table_type  IS  TABLE  OF  varchar2(25)
    INDEX  BY  BINARY_INTEGER;
--聲明記錄表數據類型的變量
    v_reg_table REG_TABLE_TYPE;
    
BEGIN
    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));
END ;

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

DECLARE
--定義記錄表數據類型
    TYPE emp_table_type  IS  TABLE  OF  employees%ROWTYPE
    INDEX  BY  BINARY_INTEGER;
--聲明記錄表數據類型的變量
    v_emp_table EMP_TABLE_TYPE;
BEGIN
    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;
    SELECT  first_name, hire_date, job_id  INTO
    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);
END ;

 

2.5   運算符和表達式(數據定義)

2.5.1  關系運算符

運算符

意義

=

等於

<> , != , ~= , ^=

不等於

小於

大於

<=

小於或等於

>=

大於或等於

2.5.2  一般運算符

運算符

意義

+

加號

-

減號

*

乘號

/

除號

:=

賦值號

=>

關系號

..

范圍運算符

||

字符連接符

2.5.3  邏輯運算符

運算符

意義

IS NULL

是空值 

BETWEEN  AND

介於兩者之間

IN

在一列值中間 

AND

邏輯與

OR

邏輯或

NOT

取返,如IS NOT NULL, NOT IN

 

 2.6   變量賦值

在PL/SQL編程中,變量賦值是一個值得注意的地方,它的語法如下:

1 variable  := expression ;

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

2.6.1  字符及數字運算特點

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

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

2.6.2  BOOLEAN 賦值

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

1   DECLARE
2      bDone BOOLEAN;
3   BEGIN
4      bDone :=  FALSE ;
5      WHILE  NOT  bDone LOOP
6      Null ;
7      END  LOOP;
8   END ;

 

2.6.3  數據庫賦值

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

DECLARE
   emp_id    emp.empno%TYPE :=7788;
   emp_name  emp.ename%TYPE;
   wages     emp.sal%TYPE;
BEGIN
   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));
END ;

提示:不能將SELECT語句中的列賦值給布爾變量。

 

2.6.4  可轉換的類型賦值

l       CHAR 轉換為 NUMBER

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

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

l       NUMBER 轉換為CHAR

    使用 TO_CHAR函數可以實現數字到字符的轉換,如:

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

l       字符轉換為日期:

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

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

l       日期轉換為字符

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

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

 

2.7   變量作用范圍及可見性

變量的作用域是指變量的有效作用范圍,與其它高級語言類似,PL/SQL的變量作用范圍特點是:

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

l       一個變量(標識)只能在你所引用的塊內是可見的。

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

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

 

2.8   注釋

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

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

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

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

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

/***********************************************/
/* 文件名: department_salary.sql      */
/* 作 者: ZRJ                     */
/* 時 間: 2017-3-22                  */
/***********************************************/

提示:被解釋后存放在數據庫中的 PL/SQL 程序,一般系統自動將程序頭部的注釋去掉。只有在 PROCEDURE 之后的注釋才被保留;另外程序中的空行也自動被去掉。

 

2.9   簡單例子

2.9.1  簡單數據插入例子:

/***********************************************/
/* 文件名: test.sql                  */
/* 說 明:
        一個簡單的插入測試,無實際應用。*/
/* 作 者: ZRJ                     */
/* 時 間: 2017-3-22                  */
/***********************************************/
DECLARE
   v_ename   VARCHAR2(20) :=  'Bill' ;
   v_sal       NUMBER(7,2) :=1234.56;
   v_deptno   NUMBER(2) := 10;
   v_empno   NUMBER(4) := 8888;
BEGIN
   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 ;
END ;

 

2.9.2  簡單數據刪除例子:

/***********************************************/
/* 文件名: test_deletedata.sql      */
/* 說 明:
        簡單的刪除例子,不是實際應用。 */
/* 作 者: ZRJ                     */
/* 時 間: 2017-3-22                  */
/***********************************************/
DECLARE
   v_ename   VARCHAR2(20) :=  'Bill' ;
   v_sal       NUMBER(7,2) :=1234.56;
   v_deptno   NUMBER(2) := 10;
   v_empno   NUMBER(4) := 8888;
BEGIN
   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 ;
END ;
  DECLARE
   v_empno   number(4) := 8888;
BEGIN
   DELETE  FROM  emp  WHERE  empno=v_empno;
   COMMIT ;
END ;

 

************************************************

---------PL/SQL流程控制語句------------

介紹PL/SQL的流程控制語句, 包括如下三類:

             控制語句: IF 語句

l  循環語句: LOOP語句, EXIT語句

l  順序語句: GOTO語句, NULL語句

3.1  條件語句

        IF <布爾表達式> THEN

          PL/SQL 和 SQL語句

        END IF;

        -----------------------

        IF <布爾表達式> THEN

          PL/SQL 和 SQL語句

        ELSE

          其它語句

        END IF;

        -----------------------

        IF <布爾表達式> THEN

          PL/SQL 和 SQL語句

        ELSIF < 其它布爾表達式> THEN

          其它語句

        ELSIF < 其它布爾表達式> THEN

          其它語句

        ELSE

          其它語句

        END IF;

提示: ELSIF 不能寫成 ELSEIF

 

        例1:

        DECLARE

            v_empno  employees.employee_id%TYPE :=&empno;

            V_salary employees.salary%TYPE;

            V_comment VARCHAR2(35);

        BEGIN

           SELECT salary INTO v_salary FROM employees 

           WHERE employee_id = v_empno;

           IF v_salary < 1500 THEN

               V_comment:= '太少了,加點吧~!';

           ELSIF v_salary <3000 THEN

              V_comment:= '多了點,少點吧~!';

           ELSE

              V_comment:= '沒有薪水~!';

           END IF;

           DBMS_OUTPUT.PUT_LINE(V_comment);

           exception

             when no_data_found then

                DBMS_OUTPUT.PUT_LINE('沒有數據~!');

             when others then

                DBMS_OUTPUT.PUT_LINE(sqlcode || '---' || sqlerrm);        

        END;

 

        例2:

        DECLARE

           v_first_name  VARCHAR2(20);

           v_salary NUMBER(7,2);

        BEGIN

           SELECT first_name, salary INTO v_first_name, v_salary FROM employees

           WHERE employee_id = &emp_id;

           DBMS_OUTPUT.PUT_LINE(v_first_name||'雇員的工資是'||v_salary);

           IF v_salary < 10000 THEN

              DBMS_OUTPUT.PUT_LINE('工資低於10000');

           ELSE

              IF 10000 <= v_salary AND v_salary < 20000 THEN

                 DBMS_OUTPUT.PUT_LINE('工資在10000到20000之間');

              ELSE

                 DBMS_OUTPUT.PUT_LINE('工資高於20000');

              END IF;

           END IF;

        END;

 

3.2  CASE 表達式

        ---------格式一---------

        CASE 條件表達式

          WHEN 條件表達式結果1 THEN 

             語句段1

          WHEN 條件表達式結果2 THEN

             語句段2

          ......

          WHEN 條件表達式結果n THEN

             語句段n

          [ELSE 條件表達式結果]

        END;

        ---------格式二---------

        CASE 

          WHEN 條件表達式1 THEN

             語句段1

          WHEN 條件表達式2 THEN

             語句段2

          ......

          WHEN 條件表達式n THEN 

             語句段n

          [ELSE 語句段]

        END;

 

    例子:

    DECLARE

       v_first_name employees.first_name%TYPE;

       v_job_id employees.job_id%TYPE;

       v_salary employees.salary%TYPE;

       v_sal_raise NUMBER(3,2);

    BEGIN

       SELECT first_name,   job_id,   salary INTO

              v_first_name, v_job_id, v_salary

       FROM employees WHERE employee_id = &emp_id;

       CASE

          WHEN v_job_id = 'PU_CLERK' THEN

             IF v_salary < 3000 THEN v_sal_raise := .08;

             ELSE v_sal_raise := .07;

             END IF;

          WHEN v_job_id = 'SH_CLERK' THEN

             IF v_salary < 4000 THEN v_sal_raise := .06;

             ELSE v_sal_raise := .05;

             END IF;

          WHEN v_job_id = 'ST_CLERK' THEN

             IF v_salary < 3500 THEN v_sal_raise := .04;

             ELSE v_sal_raise := .03;

             END IF;

          ELSE

             DBMS_OUTPUT.PUT_LINE('該崗位不漲工資: '||v_job_id);

       END CASE;

       DBMS_OUTPUT.PUT_LINE(v_first_name||'的崗位是'||v_job_id

                                        ||'、的工資是'||v_salary

                                        ||'、工資漲幅是'||v_sal_raise);

    END;

 

3.3  循環

 1.  簡單循環

  LOOP
      要執行的語句;
      EXIT WHEN <條件語句> --條件滿足,退出循環語句
  END LOOP;

    例子:

    DECLARE

        int NUMBER(2) :=0;

    BEGIN

       LOOP

          int := int + 1;

          DBMS_OUTPUT.PUT_LINE('int 的當前值為:'||int);

          EXIT WHEN int =10;

       END LOOP;

    END;

 

2.  WHILE 循環

    WHILE <布爾表達式> LOOP
    要執行的語句;
  END LOOP;

  例子:

  DECLARE 
    x NUMBER :=1;
  BEGIN
     WHILE x<=10 LOOP
        DBMS_OUTPUT.PUT_LINE('X的當前值為:'||x);
         x:= x+1;
     END LOOP;
  END;

 

3.  數字式循環

[<<循環標簽>>]
FOR 循環計數器 IN [ REVERSE ] 下限 .. 上限 LOOP
  要執行的語句;
END LOOP [循環標簽];

       每循環一次,循環變量自動加1;使用關鍵字REVERSE,循環變量自動減1。跟在IN REVERSE 后面的數字必須是從小到大的順序,而且必須是整數,不能是變量或表達式。可以使用EXIT 退出循環。

例子1:

BEGIN
   FOR int  in 1..10 LOOP
       DBMS_OUTPUT.PUT_LINE('int 的當前值為: '||int);
   END LOOP;
END;

 

例子2:

 

DECLARE

   TYPE jobids_varray IS VARRAY(12) OF VARCHAR2(10); --定義一個VARRAY數據類型

   v_jobids JOBIDS_VARRAY; --聲明一個具有JOBIDS_VARRAY數據類型的變量

   v_howmany NUMBER; --聲明一個變量來保存雇員的數量

 

BEGIN

   --用某些job_id值初始化數組

   v_jobids := jobids_varray('FI_ACCOUNT', 'FI_MGR', 'ST_CLERK', 'ST_MAN');

 

   --用FOR...LOOP...END LOOP循環使用每個數組成員的值

   FOR i IN v_jobids.FIRST..v_jobids.LAST LOOP

 

   --針對數組中的每個崗位,決定該崗位的雇員的數量

      SELECT count(*) INTO v_howmany FROM employees WHERE job_id = v_jobids(i);

      DBMS_OUTPUT.PUT_LINE ( '崗位'||v_jobids(i)||

                       '總共有'|| TO_CHAR(v_howmany) || '個雇員');

   END LOOP;

END;

 

例:在While循環中嵌套loop循環

/*求100至110之間的素數*/

DECLARE

   v_m NUMBER := 101;

   v_i NUMBER;

   v_n NUMBER := 0;

BEGIN

   WHILE v_m < 110 LOOP

      v_i := 2;

      LOOP

         IF mod(v_m, v_i) = 0 THEN

            v_i := 0;

            EXIT;

         END IF;

    

         v_i := v_i + 1;

         EXIT WHEN v_i > v_m - 1; 

      END LOOP;

      

      IF v_i > 0 THEN

         v_n := v_n + 1;

         DBMS_OUTPUT.PUT_LINE('第'|| v_n || '個素數是' || v_m);

      END IF;

 

      v_m := v_m + 2;

   END LOOP;

END;

 

3.4  標號和GOTO 

PL/SQL中GOTO語句是無條件跳轉到指定的標號去的意思。語法如下:

GOTO label;
......
<<label>> /*標號是用<< >>括起來的標識符 */

 

注意,在以下地方使用是不合法的,編譯時會出錯誤。

u 跳轉到非執行語句前面。

u 跳轉到子塊中。

u 跳轉到循環語句中。

u 跳轉到條件語句中。

u 從異常處理部分跳轉到執行。

u 從條件語句的一部分跳轉到另一部分。

例子:

DECLARE
   v_i NUMBER := 0;
   v_s NUMBER := 0;
BEGIN
   <<label_1>>
   v_i := v_i + 1;
   IF v_i <= 1000 THEN
      v_s := v_s + v_i;
      GOTO label_1;
   END IF;
   DBMS_OUTPUT.PUT_LINE(v_s);
END;

 

3.5  NULL 語句 

      在PL/SQL 程序中,NULL語句是一個可執行語句,可以用 null 語句來說明“不用做任何事情”的意思,相當於一個占位符或不執行任何操作的空語句,可以使某些語句變得有意義,提高程序的可讀性,保證其他語句結構的完整性和正確性。如:

例子:

DECLARE
   v_emp_id employees.employee_id%TYPE;
   v_first_name employees.first_name%TYPE;
   v_salary employees.salary%TYPE;
   v_sal_raise NUMBER(3,2);
BEGIN
   v_emp_id := &emp_id;
   SELECT first_name, salary INTO v_first_name, v_salary
   FROM employees WHERE employee_id = v_emp_id;
   IF v_salary <= 3000 THEN
      v_sal_raise := .10;
      DBMS_OUTPUT.PUT_LINE(v_first_name||'的工資是'||v_salary
                                       ||'、工資漲幅是'||v_sal_raise);
   ELSE
      NULL;
   END IF;
END;

 

************************************************

----------把游標說透-----------

在 PL/SQL 程序中,對於處理多行記錄的事務經常使用游標來實現。

 

4.1 游標概念

 

在PL/SQL塊中執行SELECT、INSERT、DELETE和UPDATE語句時,ORACLE會在內存中為其分配上下文區(Context Area),即緩沖區。游標是指向該區的一個指針,或是命名一個工作區(Work Area),或是一種結構化數據類型。它為應用等量齊觀提供了一種對具有多行數據查詢結果集中的每一行數據分別進行單獨處理的方法,是設計嵌入式SQL語句的應用程序的常用編程方式。

 在每個用戶會話中,可以同時打開多個游標,其數量由數據庫初始化參數文件中的OPEN_CURSORS參數定義。

對於不同的SQL語句,游標的使用情況不同:

 

SQL語句

游標

非查詢語句

        隱式的

結果是單行的查詢語句

        隱式的或顯示的

結果是多行的查詢語句

        顯示的

 

 

4.1.1  處理顯示游標

1 . 顯式游標處理

顯式游標處理需四個 PL/SQL步驟:

l 定義/聲明游標:就是定義一個游標名,以及與其相對應的SELECT 語句。

格式:

    CURSOR cursor_name[(parameter[, parameter]…)] 
           [RETURN datatype]
    IS 
        select_statement;

 

游標參數只能為輸入參數,其格式為: 

parameter_name [IN] datatype [{:= | DEFAULT} expression]

在指定數據類型時,不能使用長度約束。如NUMBER(4),CHAR(10) 等都是錯誤的。

[RETURN datatype]是可選的,表示游標返回數據的數據。如果選擇,則應該嚴格與select_statement中的選擇列表在次序和數據類型上匹配。一般是記錄數據類型或帶“%ROWTYPE”的數據

l 打開游標:就是執行游標所對應的SELECT 語句,將其查詢結果放入工作區,並且指針指向工作區的首部,標識游標結果集合。如果游標查詢語句中帶有FOR UPDATE選項,OPEN 語句還將鎖定數據庫表中游標結果集合對應的數據行。

格式:

OPEN cursor_name[([parameter =>] value[, [parameter =>] value]…)]; 

在向游標傳遞參數時,可以使用與函數參數相同的傳值方法,即位置表示法和名稱表示法。PL/SQL 程序不能用OPEN 語句重復打開一個游標。

l 提取游標數據:就是檢索結果集合中的數據行,放入指定的輸出變量中。 

格式: 

FETCH cursor_name INTO {variable_list | record_variable };

執行FETCH語句時,每次返回一個數據行,然后自動將游標移動指向下一個數據行。當檢索到最后一行數據時,如果再次執行FETCH語句,將操作失敗,並將游標屬性%NOTFOUND置為TRUE。所以每次執行完FETCH語句后,檢查游標屬性%NOTFOUND就可以判斷FETCH語句是否執行成功並返回一個數據行,以便確定是否給對應的變量賦了值。

l 對該記錄進行處理;

l 繼續處理,直到活動集合中沒有記錄;

l 關閉游標:當提取和處理完游標結果集合數據后,應及時關閉游標,以釋放該游標所占用的系統資源,並使該游標的工作區變成無效,不能再使用FETCH 語句取其中數據。關閉后的游標可以使用OPEN 語句重新打開。

格式:

      CLOSE cursor_name; 

注:定義的游標不能有INTO 子句。

例子. 查詢前10名員工的信息。

DECLARE
   CURSOR c_cursor 
   IS SELECT first_name || last_name, Salary 
   FROM EMPLOYEES 
   WHERE rownum<11;   
   v_ename  EMPLOYEES.first_name%TYPE;
   v_sal    EMPLOYEES.Salary%TYPE;   
BEGIN
  OPEN c_cursor;
  FETCH c_cursor INTO v_ename, v_sal;
  WHILE c_cursor%FOUND LOOP
     DBMS_OUTPUT.PUT_LINE(v_ename||'---'||to_char(v_sal) );
     FETCH c_cursor INTO v_ename, v_sal;
  END LOOP;
  CLOSE c_cursor;
END;

 

2.游標屬性

 Cursor_name%FOUND     布爾型屬性,當最近一次提取游標操作FETCH成功則為 TRUE,否則為FALSE;

 Cursor_name%NOTFOUND   布爾型屬性,與%FOUND相反;

 Cursor_name%ISOPEN     布爾型屬性,當游標已打開時返回 TRUE;

 Cursor_name%ROWCOUNT   數字型屬性,返回已從游標中讀取的記錄數。

 

例1:沒有參數且沒有返回值的游標。

DECLARE
   v_f_name employees.first_name%TYPE;
   v_j_id   employees.job_id%TYPE;
   CURSOR c1       --聲明游標,沒有參數沒有返回值
   IS
      SELECT first_name, job_id FROM employees 
      WHERE department_id = 20;
BEGIN
   OPEN c1;        --打開游標
   LOOP
      FETCH c1 INTO v_f_name, v_j_id;    --提取游標
      IF c1%FOUND THEN
         DBMS_OUTPUT.PUT_LINE(v_f_name||'的崗位是'||v_j_id);
      ELSE
         DBMS_OUTPUT.PUT_LINE('已經處理完結果集了');
         EXIT;
      END IF;
   END LOOP;
   CLOSE c1;   --關閉游標
END;

 

例2:有參數且沒有返回值的游標。

DECLARE
   v_f_name employees.first_name%TYPE;
   v_h_date employees.hire_date%TYPE;
   CURSOR c2(dept_id NUMBER, j_id VARCHAR2) --聲明游標,有參數沒有返回值
   IS
      SELECT first_name, hire_date FROM employees
      WHERE department_id = dept_id AND job_id = j_id;
BEGIN
   OPEN c2(90, 'AD_VP');  --打開游標,傳遞參數值
   LOOP
      FETCH c2 INTO v_f_name, v_h_date;    --提取游標
      IF c2%FOUND THEN
         DBMS_OUTPUT.PUT_LINE(v_f_name||'的雇佣日期是'||v_h_date);
      ELSE
         DBMS_OUTPUT.PUT_LINE('已經處理完結果集了');
         EXIT;
      END IF;
   END LOOP;
   CLOSE c2;   --關閉游標
END;

 

例3:有參數且有返回值的游標。

DECLARE
   TYPE emp_record_type IS RECORD(
        f_name   employees.first_name%TYPE,
        h_date   employees.hire_date%TYPE);
   v_emp_record EMP_RECORD_TYPE;

   CURSOR c3(dept_id NUMBER, j_id VARCHAR2) --聲明游標,有參數有返回值
          RETURN EMP_RECORD_TYPE
   IS
      SELECT first_name, hire_date FROM employees
      WHERE department_id = dept_id AND job_id = j_id;
BEGIN
   OPEN c3(j_id => 'AD_VP', dept_id => 90);  --打開游標,傳遞參數值
   LOOP
      FETCH c3 INTO v_emp_record;    --提取游標
      IF c3%FOUND THEN
         DBMS_OUTPUT.PUT_LINE(v_emp_record.f_name||'的雇佣日期是'
                            ||v_emp_record.h_date);
      ELSE
         DBMS_OUTPUT.PUT_LINE('已經處理完結果集了');
         EXIT;
      END IF;
   END LOOP;
   CLOSE c3;   --關閉游標
END;

 

3. 游標的FOR循環

    PL/SQL語言提供了游標FOR循環語句,自動執行游標的OPEN、FETCH、CLOSE語句和循環語句的功能;當進入循環時,游標FOR循環語句自動打開游標,並提取第一行游標數據,當程序處理完當前所提取的數據而進入下一次循環時,游標FOR循環語句自動提取下一行數據供程序處理,當提取完結果集合中的所有數據行后結束循環,並自動關閉游標。

格式:

  FOR index_variable IN cursor_name[(value[, value]…)] LOOP
    -- 游標數據處理代碼
  END LOOP;

其中:

index_variable為游標FOR 循環語句隱含聲明的索引變量,該變量為記錄變量,其結構與游標查詢語句返回的結構集合的結構相同。在程序中可以通過引用該索引記錄變量元素來讀取所提取的游標數據,index_variable中各元素的名稱與游標查詢語句選擇列表中所制定的列名相同。如果在游標查詢語句的選擇列表中存在計算列,則必須為這些計算列指定別名后才能通過游標FOR 循環語句中的索引變量來訪問這些列數據。

注:不要在程序中對游標進行人工操作;不要在程序中定義用於控制FOR循環的記錄。

 

例子:當所聲明的游標帶有參數時,通過游標FOR 循環語句為游標傳遞參數。

DECLARE
  CURSOR c_cursor(dept_no NUMBER DEFAULT 10) 
  IS
    SELECT department_name, location_id FROM departments WHERE department_id <= dept_no;
BEGIN
    DBMS_OUTPUT.PUT_LINE('當dept_no參數值為30:');
    FOR c1_rec IN c_cursor(30) LOOP        DBMS_OUTPUT.PUT_LINE(c1_rec.department_name||'---'||c1_rec.location_id);
    END LOOP;
    DBMS_OUTPUT.PUT_LINE(CHR(10)||'使用默認的dept_no參數值10:');
    FOR c1_rec IN c_cursor LOOP        DBMS_OUTPUT.PUT_LINE(c1_rec.department_name||'---'||c1_rec.location_id);
    END LOOP;
END;

 

4.1.2 處理隱式游標

顯式游標主要是用於對查詢語句的處理,尤其是在查詢結果為多條記錄的情況下;而對於非查詢語句,如修改、刪除操作,則由ORACLE 系統自動地為這些操作設置游標並創建其工作區,這些由系統隱含創建的游標稱為隱式游標,隱式游標的名字為SQL,這是由ORACLE 系統定義的。對於隱式游標的操作,如定義、打開、取值及關閉操作,都由ORACLE 系統自動地完成,無需用戶進行處理。用戶只能通過隱式游標的相關屬性,來完成相應的操作。在隱式游標的工作區中,所存放的數據是與用戶自定義的顯示游標無關的、最新處理的一條SQL 語句所包含的數據。

格式調用為: SQL%

注:INSERT, UPDATE, DELETE, SELECT 語句中不必明確定義游標。

隱式游標屬性

屬性

SELECT

INSERT

UPDATE

DELETE

SQL%ISOPEN

 

FALSE

FALSE

FALSE

FALSE

SQL%FOUND

TRUE

有結果

 

成功

成功

SQL%FOUND

FALSE

沒結果

 

失敗

失敗

SQL%NOTFUOND

TRUE

沒結果

 

失敗

失敗

SQL%NOTFOUND

FALSE

有結果

 

成功

失敗

SQL%ROWCOUNT

 

返回行數,只為1

插入的行數

修改的行數

刪除的行數

 

4.1.3  關於 NO_DATA_FOUND 和 %NOTFOUND 的區別

SELECT … INTO 語句觸發 NO_DATA_FOUND;

當一個顯式游標的WHERE子句未找到時觸發%NOTFOUND;

當UPDATE或DELETE 語句的WHERE 子句未找到時觸發 SQL%NOTFOUND;在提取循環中要用 %NOTFOUND 或%FOUND 來確定循環的退出條件,不要用 NO_DATA_FOUND

 

4.1.4  使用游標更新和刪除數據

       游標修改和刪除操作是指在游標定位下,修改或刪除表中指定的數據行。這時,要求游標查詢語句中必須使用FOR UPDATE選項,以便在打開游標時鎖定游標結果集合在表中對應數據行的所有列和部分列。

為了對正在處理(查詢)的行不被另外的用戶改動,ORACLE 提供一個 FOR UPDATE 子句來對所選擇的行進行鎖住。該需求迫使ORACLE鎖定游標結果集合的行,可以防止其他事務處理更新或刪除相同的行,直到您的事務處理提交或回退為止。

語法:

SELECT column_list FROM table_list FOR UPDATE [OF column[, column]…] [NOWAIT]

如果另一個會話已對活動集中的行加了鎖,那么SELECT FOR UPDATE操作一直等待到其它的會話釋放這些鎖后才繼續自己的操作,對於這種情況,當加上NOWAIT子句時,如果這些行真的被另一個會話鎖定,則OPEN立即返回並給出:

ORA-0054 :resource busy  and  acquire with nowait specified.

如果使用 FOR UPDATE 聲明游標,則可在DELETE和UPDATE 語句中使用

WHERE CURRENT OF cursor_name子句,修改或刪除游標結果集合當前行對應的數據庫表中的數據行。

 

4.2 游標變量

       與游標一樣,游標變量也是一個指向多行查詢結果集合中當前數據行的指針。但與游標不同的是,游標變量是動態的,而游標是靜態的。游標只能與指定的查詢相連,即固定指向一個查詢的內存處理區域,而游標變量則可與不同的查詢語句相連,它可以指向不同查詢語句的內存處理區域(但不能同時指向多個內存處理區域,在某一時刻只能與一個查詢語句相連),只要這些查詢語句的返回類型兼容即可。

 

4.2.1  聲明游標變量

游標變量為一個指針,它屬於參照類型,所以在聲明游標變量類型之前必須先定義游標變量類型。在PL/SQL中,可以在塊、子程序和包的聲明區域內定義游標變量類型。

語法格式為:

TYPE ref_type_name IS REF CURSOR
 [ RETURN return_type];

其中:ref_type_name為新定義的游標變量類型名稱;

  return_type 為游標變量的返回值類型,它必須為記錄變量。

在定義游標變量類型時,可以采用強類型定義和弱類型定義兩種。強類型定義必須指定游標變量的返回值類型,而弱類型定義則不說明返回值類型。

聲明一個游標變量的兩個步驟:

步驟一:定義一個REF CURSOU數據類型,如:

    TYPE ref_cursor_type IS REF CURSOR;

步驟二:聲明一個該數據類型的游標變量,如:

    cv_ref REF_CURSOR_TYPE;

 

 

謝謝各位博友的支持!

未經博主同意不得隨意轉載。

如需轉載請注明出處:http://www.cnblogs.com/ZRJ-boke/p/6602465.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM