PL/SQL Step By Step(三)

1.什麼是存儲過程    sql

    這篇博客主要介紹存儲過程(Stored Procedure),簡稱過程。存儲過程是Oracle PL/SQL中的一種程序單元。存儲過程能夠經過給一個PL/SQL語句塊命名從而將這個語句塊存儲在數據庫中,以便未來能夠被反覆的調用。數據庫

    存儲過程與通常的匿名PL/SQL塊的一個主要區別是有無肯定的名稱。此外,對於匿名塊來講,每次提到到數據庫進行執行時,PL/SQL解析程序都會對其進行一次解析,而後再運行;然而對於存儲過程來講,PL/SQL的解析程序只在其建立時對其進行一次解析,後續的調用就不須要再次解析了。網絡

    存儲過程能夠在其餘耳朵可執行語句中被調用,好比說另一個匿名塊或者另外一個存儲過程等,而且,存儲工程還能夠帶參數。下面咱們先看一段存儲過程的示例代碼:ui

PROCEDURE PROC_UPDATE_CRUISE_STATUS IS
  v_today DATE
BEGIN
  SELECT TRUNC(SYSDATE)
  INTO v_today
  FROM DUAL;
  
  UPDATE CRUISES
    SET STATUS = 'DISABLED'
  WHERE TRUNC(START_DATE) < v_today
    AND TRUNC(END_DATE) < v_today
    AND STATUS <> 'CANCELED';
    
  UPDATE CRUISES
    SET STATUS = 'COMPLETE';
  WHERE TRUNC(END_DATE) < v_today
    AND STATUS <> 'CANCELED';
    
  COMMIT;
END;
/

    這段代碼會根據表CRUISES中的START_DATE和END_DATE與當前系統時間的關係來更新CRUISES表。對於這個存儲過程來講,它是沒有一個明確的返回值的,可是,執行該存儲過程以後,整個數據庫都能體現出表中每一行數據的更新後的狀態了。code

    還有一點須要特別的指出來。當你給別的用戶賦予執行某個存儲過程的權限的時候,即便這個用戶對於存儲過程所操做的表的訪問權限,該用戶也是能夠執行存儲過程的,這一點與普通的PL/SQL塊也是不同的。存儲過程存放最多的位置仍是數據庫中,這種狀況下,任何可以訪問該數據庫而且具備執行這個存儲過程權限的應用程序都可以調用它。除此以外,存儲過程也能夠存在於客戶端應用程序中,好比說基於Oracle Form Builder構建的程序,可是,這樣的程序中的存儲過程是不能被網絡上的其餘用戶調用的。orm

2.建立、更改、刪除存儲過程對象

    建立一個存儲過程能夠採用CREATE PROCEDURE proc_name語句。完整的語法能夠參考Oracle的官方文檔,我這裏只貼一張截圖:ip

    光看這個圖會有一種一下被shock到的感受。其實實際中我們寫的存儲過程不太可能完整的用到上面的語法圖表示的內容。所以,咱們這裏只講解最主要的部分。先說建立存儲過程,仍是看例子吧:文檔

CREATE OR REPLACE PROCEDURE proc_clear_log IS
BEGIN
  DELETE FROM ERRORS;
  COMMIT;
END;
/

    這是一個再簡單不過的建立存儲過程的例子,其它部分和前面講到的塊沒什麼區別,只是須要遵照格式的要求:上面的例子中OR REPLACE是可選的,意思就是字面的意思:當這個存儲過程名稱已經有了就把原來的替換掉。關鍵字IS也能夠用AS代替,固然會有不一樣,咱們後面講到再說。咱們第一個例子還不涉及到帶參數的存儲過程,參數部分咱們接下來會專門講。博客

    當提交一個CREATE PROCEDURE命令到數據庫(好比說利用SQL*PLUS),會發生下面的過程:

  • 代碼被存儲在數據字典中
  • 代碼語法解析,而且最終被斷定爲VALID或者INVALID
  • 若是是VALID,那麼數據庫會返回"Procedure created"提示信息,而且該存儲過程隨時能夠被調用        
  • 若是是INVALID,那麼數據庫會返回相應的錯誤信息,好比說相似於ORA-12222的錯誤號,該存儲過程不能被調用    

    須要注意的是,不管解析與否,經過與否,存儲過程的代碼都會保存在數據字典中。做爲一個好的習慣來講,你還能夠給存儲過程的END加上標籤,也就是過程的名稱,以下:

CREATE OR REPLACE PROCEDURE proc_clear_log IS
BEGIN
  DELETE FROM ERRORS;
  COMMIT;
END proc_clear_log;

   下面咱們再說說修改存儲過程,修改存儲過程會有兩種狀況,所以也有兩種不一樣的對應方法。若是說你的處理邏輯須要變化,換句話說,存儲過程自己須要調整,那麼能夠採用上文提到的OR REPLACE選項來完整的替換掉以前的那個存儲過程。第二種狀況是,當存儲過程內部所引用的數據庫對象發生了變化,這時,數據庫會強制將引用該對象的存儲過程設置爲INVALID狀態。這時須要使用ALTER PROCEDURE語法。這句話可能很差理解,咱們舉例說明:

    對於上面的例子,存儲過程proc_clear_log使用到了一個表ERRORS,如今假設咱們修改表的結構以下:

ALTER TABLE ERRORS ADD ERROR_SOURCE VARCHAR2(30);

    這時,數據庫會自動將proc_clear_log標記爲INVALID,也就不能被執行了。可是咱們知道表的更改實際上不影響存儲過程proc_clear_log正確運行,那麼要使存儲過程從新變回VALID狀態,可使用下面的語句:

ALTER PROCEDURE proc_clear_log COMPILE;

    這個語句會從新觸發Orale PL/SQL的解析程序去從新編譯存儲過程proc_clear_log,若是確實表的更改實際上不會影響proc_clear_log,那麼,它會將proc_clear_log狀態設置回VALID。

    刪除存儲過程就很是簡單,相信讀者大概都猜到了:

DROP PROCEDURE proc_clear_log;

3.如何調用存儲過程?

    調用存儲過程有兩種方法:在PL/SQL塊中調用和使用SQL*PLUS命令調用。下面先講講第一種方法:

    能夠在任何一個PL/SQL塊中的執行語句中調用已有的存儲過程。直接用存儲過程的名稱便可調用。直接看代碼:

BEGIN
    proc_clear_log;
END;

    這樣的一個匿名塊中能夠調用多個不一樣的存儲過程,而且能夠和普通的SQL語句混合使用。上面這個匿名代碼塊自己就能夠定義爲一個存儲過程。換句話說,存儲過程當中也是能夠調用其餘存儲過程的。

    另外一種調用存儲過程的辦法是在SQL*PLUS中使用其特有的命令來執行:

--下面的兩種是同樣的
EXECUTE proc_clear_log;
EXEC proc_clear_log;

4.存儲過程參數

    存儲過程的參數定義在存儲過程頂部,集中在一對小括號中。每個參數的定義都包括如下幾個方面:

  • 參數名    
  • 參數的類型,IN或者OUT或者IN OUT類型。默認是IN類型    
  • 數據類型:只能給出類型,不能給出精度、長短等。好比你能夠定義類型爲varchar2,可是不能定義爲varchar2(30)  
  • 默認值(可選):經過使用DEFAULT關鍵字給某個參數指定默認值。

    此外,參數之間使用逗號隔開。下面咱們看一個帶有參數的存儲過程的定義:

CREATE OR REPLACE PROCEDURE proc_example( 
       p_start_date IN DATE DEFAULT SYSDATE
      ,p_days       IN NUMBER
      ,p_name       IN VARCHAR2 DEFAULT 'TOM'
)
IS
......

    參數能夠接受任何PL/SQL變量能接受的類型。可是不容許提供長度、精度信息。此外,參數類型能夠接受%TYPE。參數能夠提供默認值,這一點是可選的。可是隻能給IN類型的參數提供默認值。除了上面的提供默認值的方法,還能夠不寫DEFAULT,而經過:=來提供默認值。

    對於提供了默認值的存儲過程參數來講,在調用的時候能夠不提供這些參數的值,而只提供沒有默認值的參數提供值。可是,若是最後面的參數有默認值,而前面的參數沒有,那還好說,後面的參數不提供值就好。可是若是反過來呢,這就有點麻煩。咱們後面會講解決這個問題的辦法。

5.參數類型

    有必要單獨把參數類型IN、OUT、IN OUT單獨列出來講一下。

  • IN類型

    IN類型的參數必須由調用者爲其提供參數值,固然,若是定義了默認值給不給參數值均可以。一旦給了參數值,而且存儲過程開始運行,那麼,該該參數的值就不能夠改變,換句話說,IN參數是隻讀的。

    除此以外,在定義參數的時候,若是是IN類型,能夠不寫。由於IN是默認的參數類型。上面的那個例子就能夠寫成:

CREATE OR REPLACE PROCEDURE proc_example( 
       p_start_date DATE DEFAULT SYSDATE
      ,p_days       NUMBER
      ,p_name       VARCHAR2 DEFAULT 'TOM'
)
IS
......

    咱們看一個完整的定義和調用的例子:

--定義開始
PROCEDURE PROC_SHECULE_CRUISE(
          p_start_date IN DATE DEFAULT SYSDATE,
          p_days       IN NUMBER,
          p_ship_id    IN NUMBER,
          p_cruise_id  IN VARCHAR2
)
IS
  v_cruise_type_id CRUISE_TYPES.CRUISE_TYPE_ID%TYPE
BEGIN
  ...
END;
--定義結束

--調用開始
DECLARE
  v_ship_id NUMBER(4):=1;
BEGIN 
  PROC_SHECULE_CRUISE('04-JAN-2012',3,v_ship_id,'Alex');
END;
/
--調用結束

  • OUT類型

    OUT類型參數與IN類型參數則恰好相反,OUT類型的參數是不容許調用者向其提供參數值的。而且OUT類型的參數是不容許提供默認值的。除此以外,OUT類型的參數的值最終會被返回給調用者。正是所以,在涉及到OUT類型參數的存儲過程的調用就不同了。畢竟,OUT類型的參數是須要傳遞迴調用者的,那麼調用者必須有相應的參數來接受返回的OUT參數。咱們看一個例子:

--定義開始
PROCEDURE PROC_GET_EMPLOYEE_INFO(
          p_employee_id   IN   NUMBER,
          p_first_name    OUT  VARCHAR2;
          p_last_name     OUT  VARCHAR2  
)
IS
BEGIN
  SELECT FIRST_NAME,LAST_NAME
  INTO p_first_name,p_last_name
  FROM EMPLOYEES
  WHERE EMPLOYEE_ID=p_employee_id
END;
/
--定義結束

--調用開始
DECLARE
  v_first_name VARCHAR2(30),
  v_last_name VARCHAR2(30),
BEGIN
  PROC_GET_EMPLOYEE_INFO(15,v_first_name,v_last_name);

  • IN OUT參數

    有的參數能夠既具有IN類型參數的特色,又具有OUT類型參數的特色。也就是說,既能夠被調用者傳遞值進來,執行完存儲過程,修改了值以後再將參數值傳遞給調用者。可是IN OUT類型參數不容許定義默認值。

6.參數傳遞方式

    咱們以前全部涉及到的參數傳遞,都是按照參數傳遞的順序一個一個的指定參數的值。可是這種方法會遇到問題,咱們在上面也講到。假設定義四個參數,第一個給了默認值,第二個沒給,第三個第四個都給了參數值。該怎麼調用呢?

    可使用一個新的操做符=>經過名稱指定給第幾個參數賦值,代碼以下:

PROC_INVOKE(p_second=>20);

    以上基本上就是我認爲的存儲過程的主要內容,歡迎補充交流。

ps:又是一個週末,祝你們愉快! 

相關文章
相關標籤/搜索