ORACLE PL/SQL編程總結(一)

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

1.1   SQL與PL/SQL

1.1.1   什麼是PL/SQL?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   有利於客戶/服務器環境應用的運行express

      對於客戶/服務器環境來講,真正的瓶頸是網絡上。不管網絡多快,只要客戶端與服務器進行大量的數據交換。應用運行的效率天然就回受到影響。若是使用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

相關文章
相關標籤/搜索