Oracle學習10:PLSQL學習

1. PLSQL概述

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

PL/SQL的基本單位叫作一個區段,由三個部分組成:一個聲明部分,一個可運行部分,和排除-構建部分。
PL/SQL區段只被編譯一次而且以可運行的形式儲存,以下降響應時間web

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

2. PLSQL基本語法

PL/SQL的基本單位叫作一個區段,由三個部分組成:一個聲明部分,一個可運行部分,和排除-構建部分。
這裏聲明只有運行部分是必須的,其他均是可選,以下:數據庫

declare --(可選,聲明變量用)
begin --(must) null;
exception --(可選)
end;    --(must)

2.1 簡單的PSQL語句塊

2.1.1 null語句塊

以下咱們建立一個最簡單的PLSQL語句塊編程

begin null;
end;
/

在命令行執行,結果以下:
這裏寫圖片描述數組

須要指出的是,這裏的 null 不能夠省略,PLSQL語句塊中必須包含一條語句bash

2.1.2 Hello World語句

下面咱們寫一個最簡單而且打印Hello World的PLSQL。編程語言

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

在命令行執行,結果:
這裏寫圖片描述svg

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