Oracle學習筆記之PL/SQL編程

流氓前言:無論是Oracle DBA還是 數據庫開發人員,都需要和PLSQL打交道,那麼我就針對PLSQL做一個全面的總結,來幫助想學習PLSQL的童鞋們快速認識它並愛上它,在我看來要想讓一個人發自內心的愛上一門技術,無非有兩種情況:
相信大多數人都不是天生對技術感興趣的,顯然我也是~~ 哈哈。不過這都不是問題,只要我們用另一種方式去拿下她,我們照樣可以愛她它甚至從此對她產生強烈的興趣,那就是搞懂它!

###第一講:PL/SQL 概覽
                                             1.1  發展歷史

PL/SQL 在20世紀80年代由Oracle公司開發,作用當然就是爲了更方便高效的操作數據庫和處理數據嘍!最初,功能還很有限;
Oracle 8 Database 時 向數據庫引入了對象類型,使得Oracle 由一種純關係模型演變爲了對象關係(或擴展關係)模型;
Oracle 9i R2 時候已經演變爲了一種過程化的面向對象的編程語言;
Oracle 11g 時候 由有解釋型語言演變爲了編譯語言;
                                           1.2 體系結構
PL/SQL運行時 ,引擎作爲SQL*PLUS環境中的資源而存在;用戶連接進數據庫後,會建立一個會話,可以在該會話中直接運行SQL或PL/SQL語句;
PLSQL程序單元可以運行SQL語句或外部單元,SQL也可以調用PL/SQL函數和過程,SQL語句是最終實現直接與數據交互的;
下面讓大家看一下數據庫處理體系結構如下圖:

                                       1.3 基本的塊結構
PL/SQL是塊狀編程語言,分爲命名塊和匿名塊,通俗說也就是有名字的塊和沒名字的塊~
匿名塊使用場景:
在爲種子(seed)數據構建腳本時候;
執行一次性的處理活動時候;
在一個PL/SQL塊的執行會話中嵌入另一個處理活動時候;
基本的匿名塊結構必須包含執行會話,然後聲明和異常處理是可選的,如下模型:

[DECLARE]
   declaration_statements
BEGIN
    execution_statements
[EXCEPTION]
    exception_handling_statements
END;
/      

聲明塊由DECLARE保留字開始,以BEGIN保留字結束;
遊標的作用類似函數,有名稱、簽名和返回類型—select語句的輸出列;
執行塊用於處理數據,可包含變量賦值、比較、條件運算和迭代,同時他也是訪問遊標和其他命名程序的地方;
函數、過程和一些對象類型都是命名程序單元;
一個執行塊中必須至少有一條語句,如下就是一個最簡單的只包含一條NULL語句的匿名塊:
BEGIN
     NULL;
END;
/
如上這個除了使編譯階段無錯的完成,不做任何工作。任何語言的編譯都包括語法分析,塊中缺少語句會報錯,我會在後續講到;

異常處理塊由EXCEPTION保留字開始,以END保留字結束;用於捕獲和管理異常。

命名塊用於定義數據類型、結構體和變量;命名塊的結構稍有不同,因爲他們存儲在數據庫中。
結構體是混合變量,如集合、記錄機構體或系統引用遊標;結構體也可以是局部命名的函數、過程或遊標;
命名塊也有聲明部分,也叫做頭(header);由頭部分定義名稱、形參列表、和PL/SQL命名塊的任何返回類型,名稱和形參列表就是子例程的簽名;
頭和執行塊之間的區域就是作爲命名塊的聲明塊;下面舉一個命名塊函數的原型實例:


函數相當於值傳遞(只使用IN模式定義形參)或引用傳遞(使用IN和out或只有out模式定義形參)的子例程;
函數可以使用SELECT 查詢數據,但不能執行DML語句,例如UPDATE、DELETE、INSERT;
定義形參的函數或是使用PL/SQL數據類型的返回類型不能從SQL命令行調用,不過可從SQL命令行調用使用SQL數據類型的函數;
如果函數依賴於會話級變量的狀態,那麼應避免使用DETERMINISTIC子句,該子句最適合於基於函數的索引和物化視圖;
對於計劃從肯那個使用並行查詢能力的SQL語句調用的函數,應啓用PARALLEL_ENABLE子句,應進一步瞭解該子句在數據倉庫技術中的應用;
當函數返回集合(如嵌套表或VARRAY)時,PIPELINED子句提供了改進的性嫩,在返回系統引用遊標時,也能改進性能。
RESULT_CACHE子句表明函數只在SGA中緩存一次並跨會話可用,11g新特性,跨會話函數只使用IN模式形參;


下面展示一個命名塊過程原型:


AUTHID 默認值是DEFINER,這被稱爲定義者權限(definer right),意味着有權執行該過程的任何人和定義該過程的用戶擁有相同的權限,
CURRENT_USER被稱爲調用者權限,意味着只允許擁有執行權限的人調用過程並只運行自己用戶/模式下的數據;

過程在很多方面類似函數,但不返回數據類型,意味着不能將他們用右操作數;
和函數不同的是,必須通過PL/SQL塊調用過程;
過程可以查詢和操縱數據,也是將值傳入或傳出外部語言的基本子例程;

                          

10g R1 中支持用另一個引用符號取代單引號,如果字符串中有大量撇號,需要用另一個單引號,這時就有用如下:
SELECT ’ it’ ‘s  a girl , big eye,no money, so it can’ ‘t go school ! ’ as  phrase  FROM  dual;
可以用如下語句取代:
SELECT q’(it’s  a girl , big eye,no money, so it can’t go school !) ’ as phrase FROM  dual;

—————————————————————————————————————————————————————————————————————————————
上面對PL/SQL匿名塊、函數、過程有了一個簡單的介紹了,下面我就分別做一些小實驗並貼出來供大家共同學習和理解其應用場景和作用

匿名塊之小實驗:
(一)、在塊中操作變量 : 塊中可以聲明變量也可以不聲明變量,現在就來說一下生明變量時候的一些操作
語法爲:identifier [CONSTANT] datatype [NOT NULL] [:= | default expr]
1.聲明變量必須指定數據類型,或者用 %type 屬性指定變量和一個列數據類型相同或者和另一個變量數據類型相同;
2.聲明變量的同時可以賦初始值、默認值(default)、或者不賦值,且其變量值可以在begin語句中被修改;
3.當變量指定了 NOT NULL 屬性 和 constant(標量) 屬性時候,必須要賦初始值,但標量值不可在begin語句中修改;
如下:
SQL>declare
  v1 int;                                  - -定義不賦值的變量
  v2 varchar2(10) :=’Aa’;           - - 定義賦初始值的變量
  v3 varchar2(10) default ‘Zz’;    - - 定義具有默認值的變量 類似於:= 賦值
  v4 constant number :=5;        - -  定義標量變量,且必須賦一個固定值
  begin
  v1:=1; v2:=’Bb’; v3:=’Cc’;     - - 執行體中重新賦值
  null;
  end;
SQL> /
PL/SQL procedure successfully completed.
SQL> 
註釋: 塊中 := 是賦值符號,= 是等值符號, - -  是單行註釋,/*  */ 是多行註釋 (類似於Shell中的 #  和 << eof  ….eof); %type 是定義相同數據類型;
隱式遊標
 
(二)、塊中查詢表的數據:在查詢單行數據時候 一定要用 select into 結構將結果存儲在變量中,多行時候需要運用遊標(後邊會講解);
如下:
SQL> declare
  2  v1 t5.name%type;
  3  begin
  4  select name into v1 from t5 where id=1;
  5  dbms_output.put_line(‘the name is : ‘||v1);   -  -   用dbms_output.put_line 包來輸出變量值
  6  end;
  7  /

PL/SQL procedure successfully completed.     - -  因爲沒有開啓 serveroutput on ,所以沒有輸出變量值

SQL> set serveroutput on                           - -  開啓 serveroutput on ,顯示變量值
SQL> /
the name is : abc123
PL/SQL procedure successfully completed.

(三)、在塊中執行DML語句:直接執行DML語句即可
如下:
SQL> begin
  2  delete from t5;   - - update 、insert  同樣適用;
  3  end;
  4  /

PL/SQL procedure successfully completed.

SQL> select * from t5;

no rows selected

SQL> rollback;

Rollback complete.

SQL> select * from t5;


ID NAME
———- ——————–
1 abc123
2 abc456
3 abc789

(四)、塊中執行DDL語句:需要動態SQL才能執行DDL語句(同樣適合於函數和過程)

SQL> begin
  2  drop table t5;
  3  end;
  4  /
drop table t5;
*
ERROR at line 2:                            - - 直接在塊中執行DDL語句報錯;
ORA-06550: line 2, column 1:
PLS-00103: Encountered the symbol “DROP” when expecting one of the following:
( begin case declare exit for goto if loop mod null pragma
raise return select update while with <an identifier>
<a double-quoted delimited-identifier> <a bind variable> <<
continue close current delete fetch lock insert open rollback
savepoint set sql execute commit forall merge pipe purge

SQL> begin 
  2  execute immediate ‘drop table t5’;    - - 通過動態SQL 在塊中來順利執行DDL語句
  3  end;
  4  /


PL/SQL procedure successfully completed.

SQL> desc t5;
ERROR:
ORA-04043: object t5 does not exist      - -  證明表t5 已經被drop掉

當然以上動態SQL語法還可以用以下方式:
如下:
SQL> desc t4;
 Name   Null?    Type
 —————————————– ——– —————————-
 ID    NUMBER(38)
 NAME    VARCHAR2(20)


SQL> declare 
  2  v1 varchar2(30);
  3  begin
  4  v1 :=’drop table t4’;    - -  即把需要執行的DDL語句賦給一個字符變量,然後執行 這個變量
  5  execute immediate v1;
  6  end;
  7  /

PL/SQL procedure successfully completed.

SQL> desc t4;
ERROR:
ORA-04043: object t4 does not exist
———————————————————————————————————————————————————————————————————-
在剛纔的例子中我們有提到遊標的概念,那麼接下來我們就來了解一下到底什麼是遊標?
個人理解如下:
遊標:就是在PL/SQL中用來處理返回多條記錄時的一種機制,用來存儲返回的多行數據一個”臨時場所“。
分爲:
隱式遊標:不需要顯示定義遊標,所有DML語句爲隱式遊標(又叫多行隱式遊標),或者select 語句返回一條記錄時候也可以用隱式遊標(又叫單行隱式遊標)
顯示遊標:需要顯示定義遊標,當查詢結果集多於一行時候,就需要定義一個顯示遊標來存儲記錄;
遊標屬性如下:
前綴爲SQL 則表示爲隱式遊標屬性,前綴爲遊標名 ,則爲顯示遊標屬性

%rowcount :DML語句或者select  into  語句操作的行數
%found : 遊標是否找到記錄行
%notfound :遊標是否未找到記錄行
%isopen: 遊標是否打開
另外當要處理遊標的每一行時候,需要配合使用循環來處理每一行,如下:
(一)、基本的loop 循環,需要明確指出 退出條件 exit  expr 
SQL> 
  1  declare
  2  cursor c1 is select id,name from t1;  - -  定義一個顯示遊標來存儲查詢返回結果集合;
  3  v1 c1%rowtype;                           - -  定義一個變量來逐行接收遊標中結果集合;
  4  n1 number;
  5  begin
  6  open c1;
  7  if not  c1%isopen then
  8  open c1;
  9  end if;
 10  loop
 11  fetch c1 into v1;
 12  dbms_output.put_line(v1.id|| ’ ’ ||v1.name);
 13  n1:=c1%rowcount;
 14  dbms_output.put_line(‘have handle ’ || c1%rowcount || ’ lines’);
 15  exit when c1%notfound;
 16  end loop;
 17  close c1;
 18  dbms_output.put_line(‘finally  handle ’ || n1 || ’ lines’);
 19* end;
SQL> /
1 A
have handle 1 lines
2 B
have handle 2 lines
3 C
have handle 3 lines
4 D
have handle 4 lines
5 E
have handle 5 lines
5 E
have handle 5 lines

finally  handle 5 lines

PL/SQL procedure successfully completed.
(二)、while 循環
如下:
SQL>
  1 declare
  2 cursor c1 is select id,name from t1;  - -  定義 一個顯示遊標  c1 來存儲數據集合;
  3   v1   c1%rowtype;                       - -  定義一個變量來逐行接收處理遊標中的數據集合;
  4 n1 number;
  5 n2 number;  - - 相比基本loop循環多定義了一個變量用來存儲表t1的總行數,來作爲while 退出的條件
  6 begin
  7 open c1;
  8 if not  c1%isopen then
  9 open c1;
 10 end if;
 11 select count(*) into n1 from t1;
 12 while c1%rowcount < n1     - - 當不滿足while條件時立即退出循環
 13 loop
 14 fetch c1 into v1;
 15 dbms_output.put_line(v1.id|| ’ ’ ||v1.name);
 16    n2:=c1%rowcount;
 17 dbms_output.put_line(‘have handle ’ || c1%rowcount || ’ lines’);
 18    - - exit when c1%notfound;
 19 end loop;
 20 close c1;
 21 dbms_output.put_line(‘finally  handle ’ || n2 || ’ lines’);
 22*   end;
SQL> /
1 A
have handle 1 lines
2 B
have handle 2 lines
3 C
have handle 3 lines
4 D
have handle 4 lines
5 E
have handle 5 lines
finally  handle 5 lines


PL/SQL procedure successfully completed.

(三)、for 循環
如下:
  1    declare
  2  cursor c1 is select * from t1 ;
  3  n1 number;
  4  begin
  5  for v1 in c1   - - 用for循環不用顯示定義 v1 變量來接收 遊標數據集
  6  loop                                                                   
  7  dbms_output.put_line(v1.id|| ’ ‘||v1.name);
  8  n1:= c1%rowcount;
  9  dbms_output.put_line(‘have handle ‘|| n1 ||’ lines’);
 10  end loop;
 11  dbms_output.put_line(‘finally handle ‘|| n1 || ’ lines’);
 12* end;
 13  /
1 A
have handle 1 lines
2 B
have handle 2 lines
3 C
have handle 3 lines
4 D
have handle 4 lines
5 E
have handle 5 lines
finally handle 5 lines

PL/SQL procedure successfully completed.

對比三種循環結構,很顯然能看出第三種的寫法更加的簡潔,因爲 for 循環 和 遊標的配合能自動打開 遊標和自動關閉遊標,且能循環處理遊標內的每一行







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