Oracle 第5章 使用PL/SQL

 


Oracle 第4章 同義詞、序列、視圖、索引

Oracle 第6章 遊標

Oracle 第5章 使用PL/SQL


1、技術目標

  • 理解 PL/SQL 功能和特點
  • 瞭解數據類型及其用法
  • 理解邏輯比較
  • 理解控制結構
  • 掌握錯誤處理


2、什麼是PL/SQL?

  • PL/SQL是過程語言(Procedural Language)與結構化查詢語言(SQL)結合而成的編程語言
  • PL/SQL是對SQL的擴展
  • 支持多種數據類型,如大對象和集合類型,可使用條件和循環等控制結構
  • 可用於創建存儲過程、觸發器和程序包,給SQL語句的執行添加程序邏輯
  • 與 Oracle服務器和Oracle 工具緊密集成,具備可移植性、靈活性和安全性


3、PL/SQL特點

  • 支持 SQL,在 PL/SQL 中可以使用:
           數據操縱命令
           事務控制命令
           遊標控制
  • 支持動態SQL,可以在程序運行過程中動態構造和運行各種SQL命令
  • SQL函數和SQL運算符
  • 支持面向對象編程 (OOP)
  • 可移植性,可運行在任何操作系統和平臺上的Oralce 數據庫
  • 更佳的性能,PL/SQL 經過編譯執行
  • 與 SQL 緊密集成,簡化數據處理,如下:
           支持所有SQL數據類型
           支持NULL值
           支持%TYPE和%ROWTYPE屬性類型
  • 安全性,可以通過存儲過程限制用戶對數據的訪問


4、PL/SQL體系結構

PL/SQL引擎用來編譯和執行PL/SQL塊或子程序,該引擎駐留在Oracle
服務器中,引擎僅執行過程語句,對於SQL語句則發送給Oracle服務器
上的SQL語句執行器執行,如圖:




5、什麼是PL/SQL塊?

  • PL/SQL是一種塊結構語言,將一組語句放在一個塊中
  • 構成PL/SQL程序的基本單元是邏輯塊(過程、函數或匿名塊),邏輯塊可以包含任何數量的嵌套子塊,每個邏輯塊對應要解決的問題或子問題,匿名塊是未在數據庫中命名的PL/SQL塊,運行時傳遞到PL/SQL引擎執行
    (本文只涉及匿名塊,命名塊見後續文章)
  • 將邏輯上相關的聲明和語句組合在一起


PL/SQL分爲三個部分:

  • 聲明部分,聲明塊中使用的變量、遊標和自定義異常,其作用域僅限所在的塊,另外,局部子程序也可在聲明部分中定義
  • 可執行部分,執行命令並操作聲明部分的變量、遊標,可嵌套子塊,該部分是必選
  • 異常處理部分,處理執行塊時引發的異常,該部分也可嵌套子塊


定義PL/SQL塊,語法如下:

   [DECLARE
    declarations]
    BEGIN
    executable statements
    [EXCEPTION
    handlers]
    END;

語法說明:

declarations,爲聲明部分(可不要)
executable statements,爲可執行部分
exception handlers,爲錯誤處理代碼塊(可不要)

使用: 定義PL/SQL語句塊
--聲明部分
DECLARE
  --定義變量
  qty_on_hand number(5);
--可執行部分
BEGIN
  --PL/SQL語句
  SELECT quantity INTO qty_on_hand
  FROM Products
  WHERE product = '芭比娃娃'
  FOR UPDATE OF quantity;
  IF qty_on_hand > 0 THEN
    UPDATE Products SET quantity = quantity + 1
    WHERE product = '芭比娃娃';
    INSERT INTO purchase_record
    VALUES ('已購買芭比娃娃', SYSDATE);
  END IF;
  COMMIT;
--異常處理部分
EXCEPTION  /* 異常處理語句 */
  WHEN OTHERS THEN
    DBMS_OUTPUT.PUT_LINE('出錯:'|| SQLERRM); 
END;
/

注意:默認情況下,SQL*Plus工具不會顯示DBMS_OUTPUT程序包
的輸出,要啓用DBMS_OUTPUT,須執行命令set serveroutput on
語句結束後的"/",表示執行程序塊


6、PL/SQL語言特性

PL/SQL對大小寫不敏感

PL/SQL中的複合符號:

    :=         賦值操作符
    ||         連接操作符
    --        單行註釋
    /* */        多行註釋
    <<, >>        標籤分隔符
    ..        範圍操作符
    **        求冥操作符

可執行部分所使用的變量和常量,必須在聲明部分先聲明

聲明變量時必須指定變量的數據類型,可以在聲明變量時初始化,如下:

    語法: 變量名 數據類型[(範圍)] [:= 初始值];
    使用:
        DECLARE
            vcode varchar2(5);
            vname varchar2(25) := 'rose'
        BEGIN
            ...
        END;
        注意:一條語句只能聲明一個變量

變量賦值時可使用select ... into語句將查詢出的數據賦值給變量,

    使用:
        DECLARE
          icode varchar2(6);
          pCatg varchar2(20);
          pRate number;
          --定義常量
          cRate CONSTANT number := 0.10;
        BEGIN
          ...
          icode := 'i205'; --給變量icode賦值
          select pCategory, itemRate * cRate
          into  pCatg, pRate --給變量pCatg、pRate賦值
          from ItemFile where itemCode = icode;
          ...
        END;

聲明常量的語法爲: 常量名 CONSTANT 數據類型 := 常量值

注意:初始化變量和常量時,可用保留字"DEFAULT"替換賦值操作符":=",例如,
flag boolean DEFAULT true; --聲明布爾型變量flag,初始值爲true
score number DEFAULT 50; --聲明數值型常量score,值爲50


7、PL/SQL 數據類型

PL/SQL所使用的數據類型如下圖:



數值類型:



字符類型:




日期時間類型

  • DATE
  • TIMESTAMP


布爾數據類型,BOOLEAN:

  • 用於存儲邏輯值(TRUE、FALSE和NULL)
  • 不能向數據庫中插入BOOLEAN數據
  • 不能將列值保存到BOOLEAN變量中
  • 只能對BOOLEAN變量執行邏輯操作


LOB數據類型:

  • 用於存儲大文本、圖像、視頻剪輯和聲音剪輯等非結構化數據。
  • LOB 數據類型可存儲最大 4GB的數據。
  • LOB 類型包括:

        BLOB   將大型二進制對象存儲在數據庫中
        CLOB   將大型字符數據存儲在數據庫中,
        使用:

            SET SERVEROUTPUT ON
            DECLARE
              clob_var   CLOB;
              amount     INTEGER;
              offset     INTEGER;
              output_var VARCHAR2(100);
            BEGIN
              --從表中選擇CLOB字段值到clob_var變量中
              SELECT chapter_text INTO clob_var
              FROM my_book_text
              WHERE chapter_id=5;
              amount := 24;  -- 要讀取的字符數
              offset := 1;   -- 起始位置
              --從clob數據中讀取24個字符存到output_var變量中
              DBMS_LOB.READ(clob_var, amount, offset, output_var);
              --顯示讀取的信息
              DBMS_OUTPUT.PUT_LINE(output_var);
            END;
            /
        NCLOB 存儲大型UNICODE字符數據
        BFILE   將大型二進制對象存儲在操作系統文件中,
        使用:
            insert into myBook values ('Oracle第5章', 

                  BFILENAME('oracle_book', 'oracle05.txt'));
            oracle_book爲目錄別名(目錄可以使用create directory語句創建),
            oracle05.txt爲目錄下的文件名

屬性類型:

    用於引用數據庫表中列的數據類型,以及表示表中一行的記錄
    類型,有兩種:

  • %TYPE,引用變量和數據庫列的數據類型
  • %ROWTYPE,提供表示表中一行的記錄類型


    使用1: 聲明變量icode,其類型爲表ItemFile中的列itemCode的類型,
    icode ItemFile.itemCode%TYPE;

    使用2: 聲明變量empRec,可存儲表Emp表中的一行記錄,
    empRec Emp%ROWTYPE;

    屬性類型的優點:

  • 不需要知道被引用的表列的具體類型
  • 如果被引用對象的數據類型發生改變,PL/SQL 變量的數據類型也隨之改變


8、關係運算符

關係運算符    說明
-------------------------------------------------------------------
=                 比較兩個變量是否相等,如果值相當,則返回 True
<>, !=        比較兩個變量,如果不相等,則返回 True
<                比較兩個變量,檢查值 1 是否小於值 2
>                比較兩個變量,檢查值 1 是否大於 值 2
<=              比較兩個變量,檢查變量 1 是否小於等於變量 2
>=              比較兩個變量,檢查變量 1 是否大於等於變量 2

9、布爾表達式

布爾表達式的結果爲TRUE、FALSE或NULL,通常由邏輯運算符
AND、OR和NOT連接,有3中類型的布爾表達式:

表達式        說明                                          示例
------------------------------------------------------------------------------------
數字布爾型    用於比較數字,爲定量比較           num1 := 25, num2 := 68,

                                                                  num1  <= num2 爲 true

字符布爾型    比較字符串每個字節的二進制值     char1 := 'Mickey',
                                                                  char2 :=
'Michael',
                                                                  char1 > char2 爲 true

日期布爾型    按年月順序進行比較                    date1 := '15-9月-08',
                                                                  date2 := '16-10-08',
                                                                  date1 < date2 爲 true

10、PL/SQL程序控制

三種程序控制方式:

  • 分支(條件)控制
  • 循環控制
  • 順序控制


11)分支(條件)控制


    11.1)分支控制if語句
    
    if語句有3種形式:if-then、if-then-else、if-then-elsif,

    if-then語句 :先檢測條件,條件爲true時執行then部分的語句,
    語法爲,

        IF condition THEN
            語句;
        END IF

    if-then-else語句:不符合條件,執行else部分語句,
    語法爲,

        IF condition THEN
            語句;
        ELSE
            語句;
        END IF;

        使用: 從ItemFile表中查詢編號爲I188的商品價格,存變量iRate中,如果
        價格大於200,將價格減少200,價格小於200,將價格減少50,
        set serveroutput on
        DECLARE
            iCode varchar2(4);--商品編號
            iRate number;--商品價格
        BEGIN
            --查詢編號爲I188的商品,將商品編號賦給iCode,
            --將價格賦給變量iRate

            select itemCode, itemRate into iCode ,iRate
            from ItemFile
            where itemCode = 'I188';
            --根據價格範圍進行更新操作
            IF iRate > 200 THEN
                update ItemFile set itemRate = itemRate - 200
                where itemCode = iCode;
            ELSE
                update ItemFile set itemRate = itemRate - 50
                where itemCode = iCode;
            END IF;
            --輸出信息
            DBMS_OUTPUT.PUT_LINE('編號:' || iCode ||  ' 價格:' || iRate);
        END;
        /

    if-then-elsif語句:

    將附加條件語句與if-then-else語句相結合的一種形式,語法爲,

        IF condition1 THEN
            語句1;
        ELSIF condition2 THEN
            語句2;
        ELSIF condition3 THEN
            語句3;
        ...
        ELSE
            語句4;
        END IF;

    11.2)分支控制,case語句

    將單個變量或表達式與多個值進行比較,執行 CASE 語句前先計算
    選擇器(selector)的值,當selector的值與WHEN中的表達式相等時,
    執行THEN部分的語句,語法:

        CASE selector
            WHEN 表達式1 THEN 語句1;
            WHEN 表達式1 THEN 語句1;
            ...
            WHEN 表達式N THEN 語句N;
            [ELSE 語句;]--當以上條件都不滿足時執行ELSE中語句
        END CASE;

        使用: 提示用戶輸入成績值,根據成績輸出相應的等級,
        set serveroutput on
        BEGIN
            CASE '&score' --接收輸入的成績
                WHEN 'A' THEN DBMS_OUTPUT.PUT_LINE('優異');
                WHEN 'B' THEN DBMS_OUTPUT.PUT_LINE('優秀');
                WHEN 'C' THEN DBMS_OUTPUT.PUT_LINE('良好');
                WHEN 'D' THEN DBMS_OUTPUT.PUT_LINE('一般');
                WHEN 'E' THEN DBMS_OUTPUT.PUT_LINE('較差');
                ELSE DBMS_OUTPUT.PUT_LINE('沒有此成績!');
            END CASE;
        END;
        /

12、循環控制

循環控制包括:loop、exit語句,exit可退出循環,
exit when語句可按條件退出循環,共三種類型:

    loop-end loop 循環
    while 循環
    for 循環

    12.1)LOOP循環(無條件循環)

    語法:

    LOOP
        語句;
    END LOOP;

    循環執行LOOP塊中的語句,爲了避免死循環,LOOP的語句中必須
    加入EXIT或EXIT WHEN語句退出循環
    使用:使用學員分數,大於60分的顯示"通過",

    set serveroutput on
    BEGIN
        LOOP
            --&score變量接收用戶的輸入
            IF &score > 60 THEN
                DBMS_OUTPUT.PUT_LINE('通過');
                EXIT;
            END IF;
        END LOOP;
    END;
    /
   
    12.2)WHILE循環(條件循環)

    語法:

    WHILE 條件表達式 LOOP
        語句;
    END LOOP;

    使用: 每日銷量循環累加,直至月銷量大於等於4000爲止,


    語法:

    set serveroutput on
    DECLARE
        monthlyValue number := 0;--聲明月銷量
        dailyValue number := 0;--聲明日銷量
    BEGIN
        WHILE monthlyValue <= 4000 LOOP
            --根據日銷量計算月銷量
            monthlyValue := dailyValue * 31;
            --日銷量累加
            dailyValue := dailyValue + 10;
            --輸出日銷量信息
            DBMS_OUTPUT.PUT_LINE('日銷量:' || dailyValue);
        END LOOP;
        --輸出月銷量信息
        DBMS_OUTPUT.PUT_LINE('月銷量:' || monthlyValue);
    END;
    /

    12.3)FOR循環(帶計數器的循環)

    語法:

    FOR 計數器變量 IN [REVERSE] 值1...值N
    LOOP
        語句;
    END LOOP;

    說明:reverse可使值由大到小執行循環
    使用: 循環25次,顯示25個偶數,

    set serveroutput on
    BEGIN
        FOR counter IN 1..25
        LOOP
            DBMS_OUTPUT.PUT_LINE(counter * 2);
        END LOOP;
    END;
    /

13、順序控制

可控制程序的執行順序,可使用如下語句,

  • GOTO語句
  • 無條件地跳到標籤指定的語句,標籤使用雙尖括號(<<標籤名>>)括起來
  •     2在語句塊內必須是唯一的名稱,標籤後跟待執行語句或PL/SQL塊,GOTO
  • 3無法跳轉到IF、CASE、LOOP語句或子快中
  • NULL語句
  • NULL是一條可執行語句,什麼也不做,用在IF或其他語句的語法要求至少
  • 需要一條可執行語句但又不需要執行任何操作的情況下


使用: 檢查itemCode爲'I188'的產品的庫存,如現有庫存小於訂購量則更新
現有庫存

    DECLARE
        inventory ItemFile.inventory%type;--庫存,類型同字段inventory
        relevel ItemFile.relevel%type;--訂購數量

    BEGIN
        --查詢商品編號爲'I188'的商品庫存和訂購量並賦值給變量
        select inventory, relevel into inventory, relevel
        from ItemFile
        where itemCode = 'I188';
        --判斷庫存是否小於訂購量
        IF inventory < relevel THEN
            --跳轉到updation標籤處
            GOTO updation;
        ELSE
            --跳轉到quit標籤處
            GOTO quit;
        END IF;
        --設置updation標籤
        <<updation>>
        update ItemFile set inventory = inventory + relevel
        where itemCode = 'I188';
        --設置quit標籤
        <<quit>>
        NULL;
    END;
    /

14、動態SQL

在PL/SQL程序中,可使用DML、TCL語句,但是DDL語句和會話控制
語句不能在PL/SQL中直接使用,要想使用可以通過動態SQL實現

動態SQL指在PL/SQL塊編譯時SQL語句不確定,比如可以根據用戶輸入
參數的不同而執行不同的操作,編譯程序不處理動態語句部分,在程序
運行時可動態創建語句、對語句進行語法分析並執行

動態SQL執行方式:

  • 通過本地SQL命令執行
  • 通過DBMS_SQL程序包來執行


本地動態SQL語法:
   
    EXECUTE IMMEDIATE dynamic_sql_string
    [into define_variable_list]
    [using bind_argument_list];
   
    說明:
    dynamic_sql_string,爲動態SQL語句字符串
    into子句,用於接受select語句查詢出的值
    using子句,用於綁定輸入參數變量
   
    注意:EXECUTE IMMEDIATE語句只能處理返回單行或沒有返回的SQL語句,
    要處理返回多行的動態SQL可使用REF遊標的OPEN...FOR語句(見後續文章)
   
    使用: 先執行創建表的動態SQL,再執行帶參select語句,
    DECLARE
        sqlStmt varchar2(200); --保存SQL語句的字符串
        empId number(4) := 9527; --員工編號
        empRec emp%ROWTYPE; --保存行記錄
    BEGIN
        --執行建表的動態SQL語句
        EXECUTE IMMEDIATE
            'create table TempTable (id number, amt number)';
        --查詢員工表的SQL語句,帶一個輸入參數id
        sqlStmt := 'select * from Emp where empNo = :id';
        --執行查詢員工表的動態SQL語句,同時設置輸入參數empId,
        --查詢結果(1條記錄)保存到變量empRec

        EXECUTE IMMEDIATE sqlStmt INTO empRec USING empId;
    END;
    /

15、異常

在運行程序時出現的錯誤叫做異常,發生異常後,語句將停止執行,
控制權轉移到 PL/SQL 塊的異常處理部分

異常有兩種類型:

  • 預定義異常,當PL/SQL程序違反 Oracle 規則或超越系統限制時隱式引發
  • 用戶定義異常,用戶可以在PL/SQL塊的聲明部分定義異常,自定義的異常通過RAISE語句顯式引發


16、預定義異常

  • Oracle把一些常見錯誤預先定義爲異常
  • 每個預定義異常都有一個錯誤號
  • 必須通過名字來捕獲異常,比如除數爲零會引發"ZERO_DIVIDE"異常


PL/SQL支持的預定義異常如下:

異常名                              說明
--------------------------------------------------------------------------------
ACCESS_INTO_NULL          未初始化對象時出現
CASE_NOT_FOUND            在CASE語句中的選項與輸入的數據不匹配時出現
COLLECTION_IS_NULL       給尚未初始化的表或數組賦值時出現
CURSOR_ALREADY_OPEN  試圖重新打開已打開的遊標時出現,重新打開遊標
                                         前必須先關閉
DUP_VAL_ON_INDEX        試圖將重複的值存儲在使用唯一索引的列中時出現
INVALID_CURSOR             執行非法遊標運算時出現,比如打開一個尚未打開
                                         的遊標
INVALID_NUMBER            將字符串轉換爲數字時出現
LOGIN_DENIED                輸入的用戶名或密碼無效時出現
NO_DATA_FOUND            表中不存在請求的行時出現或者引用已刪除的元素時
STORAGE_ERROR             內存損壞或PL/SQL耗盡內存時出現
TOO_MANY_ROWS            執行select into語句後返回多行時出現
VALUE_ERROR                  產生大小限制錯誤時出現
ZERO_DIVIDE                   除數爲零時出現

注意:預定義異常在STANDARD程序包中聲明

異常處理程序語法爲:
BEGIN
    ...
EXCEPTION
    WHEN 異常名 THEN
        語句;
    WHEN OTHERS THEN
        語句;
END;

說明:

  • OTHERS 處理程序確保不會遺漏任何異常,PL/SQL塊只能有一個
  • OTHERS異常處理程序,函數SQLCODE和SQLERRM可返回錯誤代碼和錯誤文本信息


使用: 檢測代碼中的預定義錯誤,
set serveroutput on
DECLARE
    orderNum varchar2(5); --訂單號
BEGIN
    --查詢訂單號
    select orderNo into orderNum from orderMaster;
    --異常處理
    EXCEPTION
        WHEN TOO_MANY_ROWS THEN
            --輸出提示信息
            DBMS_OUTPUT.PUT_LINE('返回多行');
    END;
    /
    注意:select into只允許返回一行,所以會引發TOO_MANY_ROWS預定義異常

17、用戶定義異常

用戶可自行定義代碼中使用的異常,這種處理方式與PL/SQL引擎處理錯誤的方式相同,可以作爲一種良好的編程習慣,獲得直觀而好管理的代碼,用戶定義異常在PL/SQL塊或子程序的聲明部分定義,語法如下:

DECLARE
    異常名 EXCEPTION;

自定義異常可使用RAISE語句顯示引發,語法爲:

RAISE 異常名;

使用: 用自定義異常處理錯誤,如用戶輸入'附件', '頂蓋', '備件',
屬於錯誤的類別,引發自定義異常,
set serveroutput on
DECLARE
    --自定義異常(無效類別)
    InvalidCategory EXCEPTION;
    category varchar2(10);
BEGIN
    --接收輸入的數據
    category := '&Category';
    --判斷輸入數據
    IF category NOT IN ('附件', '頂蓋', '備件') THEN
        --引發自定義異常
        RAISE InvalidCategory;
    ELSE
        --輸出信息
        DBMS_OUTPUT.PUT_LINE('你輸入的類別是:' || category);
    END IF;
--異常處理塊
EXCEPTION
    --捕獲InvalidCategory異常
    WHEN InvalidCategory THEN
        DBMS_OUTPUT.PUT_LINE('無法識別該類別');
END;
/

18、引發應用程序錯誤

使用過程RAISE_APPLICATION_ERROR創建用戶定義的錯誤信息,用戶
定義的錯誤信息可以比指定的異常描述得更詳細,語法如下:

RAISE_APPLICATION_ERROR (error_number, error_message);

說明:
error_number,是爲異常指定的編號,編號必須是-20000 ~ -20999之間
    的負整數
error_message,爲用戶異常指定的消息文本,消息長度可到2048字節,
    錯誤消息是與error_number表示關聯的文本

RAISE_APPLICATION_ERROR即可在PL/SQL程序可執行部分使用,還
可在其異常處理部分使用,調用此過程可同時顯示錯誤編號和消息

使用: 如果費率是未知的,將顯示"未指定費率",
set serveroutput on
DECLARE
    rate ItemFile.itemRate%TYPE;
    --自定義異常
    RateException EXCEPTION;
BEGIN
    --查詢編號爲'I188'的商品的費率,賦給變量rate,費率爲NULL就賦0
    select NVL(itemRate, 0) into rate from ItemFile
    where itemCode = 'I188';
    --判斷費率
    IF rate = 0 THEN
        --費率爲0,引發異常
        RAISE RateException;
    ELSE
        --費率不爲0,顯示費率
        DBMS_OUTPUT.PUT_LINE('費率爲:' || rate);
    END IF;
--異常處理塊
EXCEPTION
    WHEN RateException THEN
        --顯示指定的異常編號和異常信息
        RAISE_APPLICATION_ERROR(-20008, '該商品未指定費率');
END;
/

19、總結

  • PL/SQL 是一種可移植的高性能事務處理語言
  • PL/SQL 引擎駐留在 Oracle 服務器中
  • PL/SQL 塊由聲明部分、可執行部分和異常處理部分組成
  • PL/SQL 數據類型包括標量數據類型、LOB 數據類型和屬性類型
  • 控制結構包括條件控制、循環控制和順序控制
  • PL/SQL 支持動態 SQL
  • 運行時出現的錯誤叫做異常
  • 異常可以分爲預定義異常和用戶定義的異常



Oracle 第4章 同義詞、序列、視圖、索引

Oracle 第6章 遊標

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章