1. 程式人生 > >Oracle學習10:PLSQL學習

Oracle學習10:PLSQL學習

1. PLSQL概述

PLSQL是Oracle內部的一種程式語言
PLSQL是一門語言。叫做過程化SQL語言(Procedural Language SQL)
PLSQL是一種過程化語言,屬於第三代語言,它與C、C++、Java等語言一樣關注於處理細節,可以用來實現比較複雜的業務邏輯。
PL/SQL是對結構化查詢語言(SQL)的過程語言擴充套件。

PL/SQL的基本單位叫做一個區段,由三個部分組成:一個宣告部分,一個可執行部分,和排除-構建部分。
PL/SQL區段只被編譯一次並且以可執行的形式儲存,以降低響應時間

實際工作中,PLSQL多用於寫觸發器、儲存過程、函式等

2. PLSQL基本語法

PL/SQL的基本單位叫做一個區段,由三個部分組成:一個宣告部分,一個可執行部分,和排除-構建部分。
這裡宣告只有執行部分是必須的,其餘均是可選,如下:

declare --(可選,宣告變數用)
begin   --(must)
    null;
exception --(可選)
end;    --(must)

2.1 簡單的PSQL語句塊

2.1.1 null語句塊

如下我們建立一個最簡單的PLSQL語句塊

begin
    null;
end;
/

在命令列執行,結果如下:
這裡寫圖片描述

需要指出的是,這裡的 null 不可以省略,PLSQL語句塊中必須包含一條語句

2.1.2 Hello World語句

下面我們寫一個最簡單並且列印Hello World的PLSQL。

set serveroutput on;    --用於開啟控制檯輸出服務,預設是off,則不列印
begin
    dbms_output.put_line('HelloWorld');  --類似於java的System.out.print
end;
/

在命令列執行,結果:
這裡寫圖片描述

2.2 含有宣告(declare)語句的PLSQL

declare用於宣告變數。
變數名稱與變數型別不可省略,預設值通過 := 賦值,預設值可以省略。
格式如下:

declare
v_variable1 variable_type [ := default_value]; v_variable2 variable_type [ := default_value];

2.2.1 PLSQL變數型別

2.2.1.1 變數命名

變數宣告
規則:

  • 變數不能使用保留字。如from select;
  • 第一個字元必須是字母,一般以 v_ 開頭;
  • 變數名最多包含30個字元;
  • 不要與資料庫的表或者列同名;
  • 每一行只能宣告一個變數。

2.2.1.2 基本變數型別7個

PLSQL的基本變數型別有7個。如下:

  • binary_integer:整數,主要用來計數而不是用來表示欄位型別。
  • number:數字型別
  • char:定長字串
  • varchar2:變長字串
  • date:日期
  • long:長字串,最長2GB
  • boolean:布林型別,可以取值true、false和null

根據以上型別,我們寫一個含有變數宣告的PLSQL。

declare
    v_temp number(1);
    v_count binary_integer := 0;
    v_sal number(7,2) :=4000.00;
    v_date date := sysdate;
    v_pi constant number(3,2) := 3.14;  --類似於Java的final關鍵字,表示常量
    v_valid boolean := false;
    v_name varchar2(20) not null := 'MyName';
begin
    dbms_output.put_line('v_temp value: '||v_temp);  
    dbms_output.put_line('v_count value: '||v_count);
    dbms_output.put_line('v_sal value: '||v_sal);
    dbms_output.put_line('v_date value: '||v_date);
    dbms_output.put_line('v_pi value: '||v_pi);
    -- dbms_output.put_line('v_valid value: '||v_valid); --不能列印boolean值
    dbms_output.put_line('v_name value: '||v_name);
end;

命令列結果如下:
這裡寫圖片描述

這裡需要指出的是,PLSQL的dbms_output.put_line()函式不能直接列印boolean型別

2.2.1.3 關聯變數型別(關聯別的欄位、變數的變數型別)

在工作中,變數的作用更多的是儲存某個欄位中的值,因而保證變數資料型別與欄位資料型別一致是很有必要的。
PLSQL中,在變數宣告時,可以通過%type獲得指定欄位(變數)的資料型別。這種方式可以確保當源表的欄位型別改變時,變數的型別自動改變。
如下:

declare
    v_empno number(4);
    v_empno2 emp.empno%type; --v_empno2的型別是表emp的empno欄位的型別
    v_empno3 v_empno2%type;  --v_empno3的型別是變數v_empno2的型別

2.2.1.4 複合變數

PLSQL提供了兩種複合變數。

  • Table 類似於Java的陣列Array
  • record 類似於Java的類Class

如下宣告一個Table變數

declare
    type type_table_emp_empno is table of emp.empno%type index by binary_integer;   --宣告一個數組型別
        v_empnos type_table_emp_empno;  --利用型別宣告變數
begin
    v_empno(0) := 7369;
    v_empno(2) := 7839;
    v_empno(-1) := 9999;    --Table型別的下標可以是負數
    dbms_output.put_line(v_empno(-1));
end;

宣告一個Record型別

--RECORD型別
declare 
    type type_record_dept is record
        (
            deptno dept.deptno%type,
            dname dept.dname%type,
            loc dept.loc%type
        );
        v_temp type_record_dept;
begin
    v_temp.deptno := 50;
    v_temp.dname := 'aaa';
    v_temp.loc := 'bj';
    dbms_output.put_line(v_temp.deptno||v_temp.dname||v_temp.loc);
end;

這裡如果表的結構發生了變化,我們如何保證宣告的複合變數和表中的變數相同呢,可以通過rowtype實現。

%rowtype:定義一個表示表中一行記錄的變數
如下:

declare 
    v_temp dept%rowtype;
begin
    v_temp.deptno := 50;
    v_temp.dname := 'aaa';
    v_temp.loc := 'bj';
    dbms_output.put_line(v_temp.deptno||v_temp.dname||v_temp.loc);
end;

這裡關於 %type%rowtype 做一個簡單的對比說明:
如下:
%TYPE
為了使一個變數的資料型別與另一個已經定義了的變數(尤其是表的某一列)的資料型別相一致,當被參照的那個變數的資料型別改變了之後,這個新定義的變數的資料型別會自動跟隨其改變,無需修改PL/SQL程式。當不能確切地知道被參照的那個變數的資料型別時,就只能採用這種方法定義變數的資料型別。

%ROWTYPE
如果一個表有較多的列,使用%ROWTYPE來定義一個表示表中一行記錄的變數,比分別使用%TYPE來定義表示表中各個列的變數要簡潔得多,並且不容易遺漏、出錯。這樣會增加程式的可維護性。當表的某些列的資料型別改變了之後,這個新定義的變數的資料型別會自動跟隨其改變,無需修改PL/SQL程式。當不能確切地知道被參照的那個表的結構及其資料型別時,就只能採用這種方法定義變數的資料型別。

2.3 含有異常處理的PLSQL

異常塊通過exception關鍵字宣告,如下:

declare
  v_num number := 0;
begin
  v_num := 2/v_num;
  dbms_output.put_line(v_num);
exception
  when others then
    dbms_output.put_line('error');
end;
/

3.PLSQL中的SQL語法

3.1 DML語句(資料操作語言,select,update,insert,delete from)

PLSQL中可以寫SQL語句,和平時的SQL語法基本一致,需要注意的是以下幾點:

  • select語句必須返回一條記錄,並且只能返回一條記錄;
  • select必須和into一塊用(除非使用遊標);
  • 操作的資料可以是變數。
  • insert、update、delete等語句與普通SQL完成一致,只是可以傳入引數而已。

這裡需要解釋的是,select語句不返回值則無意義,返回許多則變數裝不了。

下面我們有一張測試表,並且根據這張表,實驗理解PLSQL的SQL語句。
這裡寫圖片描述

set serveroutput on;    --該服務開啟一次即可,如果之前開啟過無需再次開啟。
declare
    v_dep ljb_test.dep%type;    --type
    v_name ljb_test.name%type;  --type
    v_test ljb_test%rowtype;    --rowtype

    --測試insert&update的資料
    v_new_dep ljb_test.dep%type := 10;
    v_new_name ljb_test.name%type := '小明';
    v_new_salary ljb_test.salary%type := 5000;

    v_anoter_name ljb_test.name%type := '大明';
begin
    --select語句必須返回一條記錄,並且只能返回一條記錄
    select dep, name into v_dep, v_name from ljb_test where salary = 6000;  --限定條件一定要保證返回一條記錄
    dbms_output.put_line(v_dep||v_name);

    select * into v_test from ljb_test where salary = 6000; --限定條件一定要保證返回一條記錄
    dbms_output.put_line(v_test.dep||v_test.name||v_test.salary);

    --insert,與普通SQL語句一致,只是可以插入變數
    insert into ljb_test values(v_new_dep, v_new_name, v_new_salary);
    commit;     --執行完插入語句記得提交事務

    --update,與普通SQL語句一致,只是可以插入變數
    update ljb_test t set name = v_anoter_name where t.name =  v_new_name;

    --delete,與普通SQL語句一致,只是可以插入變數
    delete from ljb_test t where t.dep = '1';  --刪除部門1的記錄

end;
/

輸出結果:
這裡寫圖片描述

同時我們看下更新後的測試表:
這裡寫圖片描述
可以看到資料已經更新。

3.2 DDL(資料定義語言)

DDL語句不能直接執行,必須使用 execute immediate '' 進行包裹;
如下,我們通過PLSQL建立一張表:

begin
    execute immediate 'create table tb(aaa varchar2(255) default ''asd'')'; --兩個單引號代表一個單引號
end;
/

需要指出的兩點:

  • DDL必須被execute immediate ''包裹,否則報錯ORA-06550;
  • 平時的語句中的單引號必須通過兩個單引號進行表示,如以上指令碼的預設值。
  • delete語句可以不使用execute immediate包裹。

我們在命令列執行,並檢視該表
這裡寫圖片描述
同樣的,truncate和drop寫法如下:

begin
    execute immediate 'truncate table tb'; --刪除記錄,釋放表空間
    execute immediate 'drop table tb';     --刪除記錄及定義,釋放表空間
end;
/

但是有個例外:就是delete。
delete既可以使用execute也可以不使用,如下兩種寫法均是正確的。

begin
    execute immediate'delete tb';   --delete刪除記錄,但不釋放表空間
    commit;

    delete tb;
    commit;
end;
/

這裡可以這樣理解,實際上delete table_name 等價於 delete from table_name,是一種DML語言,因而可以直接執行。

這裡針對drop、truncate、delete在複習下三者的區別

  • TRUNCATE TABLE:刪除內容、釋放空間但不刪除定義。
  • DELETE TABLE:刪除內容不刪除定義,不釋放空間。
  • DROP TABLE:刪除內容和定義,釋放空間。

4.PLSQL的判斷迴圈語句

判斷迴圈語句是PLSQL的重要語句。

4.1 判斷(分支)語句

判斷語句也叫做分支語句,通過if實現。如下通過一個簡單例項進行理解:
對於如下表:
這裡寫圖片描述
我們寫一個薪水等級判斷語句:
<3000 low
3000~5000 middle
其餘 high

--if語句
declare 
    v_sal ljb_test.salary%type;
begin
    select salary into v_sal from ljb_test where name = 'ri';
    if(v_sal < 3000) then 
        dbms_output.put_line('low');
    elsif(v_sal < 5000) then                --elsif
        dbms_output.put_line('middle');
    else                                    --else後面沒有then
        dbms_output.put_line('high');
    end if;
end;
/
set serveroutput on;    --也可以放在下面,但是需要在之前增加一個 /
/

和Java等語句的if語句極其相似,不同的是具體的語法,需要注意其特點。
輸出結果如下:
這裡寫圖片描述

4.2 迴圈語句

與Java中的迴圈語句類似,PLSQL也要擁有類似的三種迴圈語句。為了方便理解,我使用Java迴圈的區分方式來區分PLSQL的三種迴圈。
即:
do while迴圈
while迴圈
for迴圈
對於三種迴圈,均具有以下特點:
PLSQL的迴圈一定是以loop開頭,以end loop結束

4.2.1 do while迴圈

先列印,然後在判斷條件,如下:

--類似於do while 迴圈
set serveroutput on;
declare
    i binary_integer := 1;  --宣告一個計數變數
begin
    loop                    --迴圈總是loop開頭
        dbms_output.put_line(i);
        i := i + 1;
        exit when(i>=11);   --迴圈退出語句
    end loop;               --迴圈總是end loop結束
end;
/

命令列列印結果如下:
這裡寫圖片描述

4.2.2 while迴圈

先判斷,在迴圈列印

--while迴圈
declare
    j binary_integer := 1;  --宣告一個計數變數
begin
    while j<11 loop         --迴圈退出語句,迴圈總是loop開頭
        dbms_output.put_line(j);
        j := j+1;
    end loop;               --迴圈總是end loop結束
end;
/

結果如下:
這裡寫圖片描述

4.2.3 for迴圈

for迴圈無需declare塊宣告變數。
1..10表示1-10,注意是兩個點。

--for迴圈
begin                       --for迴圈無需declare宣告變數
    for k in 1..10 loop     --1..10表示1-10,注意是兩個點
        dbms_output.put_line(k);
    end loop;

    for k in reverse 1..10 loop --reverse表示逆序
        dbms_output.put_line(k);
    end loop;
end;
/

結果如下:
這裡寫圖片描述

5. PLSQL異常處理

前面說過,PLSQL是一門過程語言,所以也支援類似於Java的異常處理機制。
需要指出的是,PLSQL的異常處理實際工作中並不常用,原因很簡單,為了提高程式的可移植性(用於多個數據庫),我們通常把異常處理放在Java等語言中處理
異常處理的核心應用一般是日誌表
所以,下文我們僅作簡單的介紹。

5.1異常處理的是執行時異常,而非編譯時異常

首先,明確一點,異常處理只能捕獲執行時的異常,編譯時的異常無法捕獲,因而如果存在編譯異常,則程式無法執行,更無法呼叫異常處理機制。

begin
    insert into dual values('',''); --插入欄位數明顯不符合
    exception
        when others then
        dbms_output.put_line('ERROR!');
end;
/

這時,程式無法編譯通過,會直接報錯:
這裡寫圖片描述

5.2 常見的異常型別及處理

下面,我們就之前PLSQL中select語句返回一條記錄的要求,來寫幾個簡單的異常捕獲程式:

5.2.1 too_many_rows

set serveroutput on;
declare
    v_name ljb_test.name%type;
begin
    select name into v_name from ljb_test;

    exception
        when too_many_rows then     --too_many_rows,返回值超過一條
            dbms_output.put_line('返回值太多');
        when others then
            dbms_output.put_line('error');
end;
/

執行程式,結果如下:
這裡寫圖片描述

5.2.2 no_data_found

declare
    v_name ljb_test.name%type;
begin
    select name into v_name from ljb_test where 1 = 0;

    exception
        when no_data_found then     --no_data_found,無資料異常
            dbms_output.put_line('無資料');
        when others then
            dbms_output.put_line('error');
end;
/

結果如下:
這裡寫圖片描述

5.3 日誌表建立

異常處理最核心的應用應該就是日誌表,而日誌表多用於儲存過程等。

  • 首先建立一個日誌表
create table err_log(
    err_id number primary key,
    err_code number,
    err_msg varchar2(1024),
    err_date date
);
  • 建立sequence
create sequence seq_err_log start with 1 increment by 1;
  • 新增異常塊
declare
    v_errcode number;
    v_errmsg varchar2(1024);

    v_name ljb_test.name%type;
begin
    --insert into dual values('0','2'); --編譯時錯誤是無法通過異常捕獲處理的
    select name into v_name from ljb_test;
    --commit;
exception
    when others then
        rollback;   --回滾,取消錯誤操作的影響
            v_errcode := SQLCODE;   --SQLCODE,關鍵字,代表出錯程式碼,Oracle的錯誤程式碼全部是負數
            v_errmsg := SQLERRM;    --SQLERRM,關鍵字,代表出錯資訊
        insert into err_log values(seq_err_log.nextval,v_errcode,v_errmsg,sysdate);
    commit;         --不要忘記commit
end;
/

我們查詢下對應的日誌表內容:
這裡寫圖片描述

如果是儲存過程中的日誌表,可以新增一個pro_name欄位。
插入的時候,直接插入該儲存過程的名字即可。

6.寫在最後

至此,PLSQL的基本語法已經學習完成,有關儲存過程、遊標等的介紹,將會另起一文,本文僅對基本語法等做簡單學習總結。