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的基本語法已經學習完成,有關存儲過程、遊標等的介紹,將會另起一文,本文僅對基本語法等做簡單學習總結。

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