PL/SQL筆記

1、基礎知識

PLSQL是一種類Pascal語言,每一段程序都是由Block(代碼塊)組成

declare
     變量定
begin
    sql語句
    pl語句
exception
    異常處理
end;

PLSQL塊分為三種:

  • 匿名塊 (Anonymous)
  • 存儲過程 (Procedure)
  • 函數 (function)

上面舉得例子就是匿名塊,因為沒有名字,所以叫匿名塊。存儲過程和函數都是有名字的,函數有返回值。

2、PLSQL變量

PLSQL變量主要有以下四種:

  • 系統內置的常規簡單變量類型
  • 用戶自定義復雜類型變量
  • 引用類型變量(保存了一個指針值)
  • 大對象類型(LOB)

變量類型舉例:

  • 布爾類型
  • 日期類型
  • BFILE的二進制文件類型
  • 日期類型
  • BLOB類型
  • long類型,長字符串
  • 字符串類型

變量聲明舉例:

DECLARE v_hiredateDATE;
v_deptno NUMBER(2) NOT NULL := 10;
v_location VARCHAR2(13) := 'Atlanta';
c_comm CONSTANT NUMBER := 1400;
  • 變量聲明后再沒有賦值之前其值為NULL
  • 同一個塊中,應該避免多個變量使用相同的名字

常規變量的申明:

DECLARE
    v_job VARCHAR2(9);
    v_count BINARY_INTEGER := 0;
    v_total_sal NUMBER(9,2) := 0;
    v_orderdate DATE := SYSDATE + 7;
    c_tax_rate CONSTANT NUMBER(3,2) := 8.25;
    v_valid BOOLEAN NOT NULL := TRUE;
    ... 
-- 特殊申明變量方式:
    v_name employees.last_name%TYPE;
    v_min_balance v_balance%TYPE := 10;
begin
   null;
end;

PLSQL中的游標

游標概論

游標是一個私有的SQL工作區域,Oracle數據庫中有兩種游標,分別是隱式游標和顯式游標,隱式游標不易被用戶和程序員察覺和意識到,實際上Oracle服務器使用隱式游標來解析和執行我們提交的SQL語句; 而顯式游標是程序員在程序中顯式聲明的;通常我們說的游標均指顯式游標。

顯示游標

對于返回多行結果的SQL語句的返回結果,可使用顯式游標獨立的處理器中每一行的數據。

此處圖片缺失

顯式游標控制的一般過程:

DECLARE
    v_empno employees.employee_id%TYPE;
    v_ename employees.last_name%TYPE;
    CURSOR emp_cursor IS --1.創建游標
      SELECT employee_id, last_name FROM employees;
BEGIN
    OPEN emp_cursor; --2.打開游標
    LOOP
      FETCH emp_cursor
        INTO v_empno, v_ename; --3.提取變量  fetch cursor_name into value1,value2;

      EXIT WHEN emp_cursor%ROWCOUNT > 10 OR emp_cursor%NOTFOUND; --emp_cursor%NOTFOUND 4.用來測試是否有數據
      DBMS_OUTPUT.PUT_LINE(TO_CHAR(v_empno) || ' ' || v_ename);
    END LOOP;
    CLOSE emp_cursor; --5.關閉游標
END;

for循環控制游標

舉例1:直接使用for+sql創建并適用游標

   --一般格式:
   --for record_name in cursor_name loop ... end loop;
   BEGIN
   FOR emp_record IN (SELECT last_name, department_id
   FROM employees) LOOP
   -- implicit open and implicit fetch occur
   IF emp_record.department_id = 80 THEN
   ...
   END LOOP; -- implicit close occurs
   END;

舉例2:先創建游標emp_cursor,再用for調用

--這種情況適用于多次調用游標的情況
 DECLARE
   CURSOR emp_cursor IS --先創建游標
   SELECT last_name, department_id
   FROM employees;
BEGIN
   FOR emp_record IN emp_cursor LOOP --再調用游標訪問
   -- implicit open and implicit fetch occur
   IF emp_record.department_id = 80 THEN
   ...
   END LOOP; -- implicit close occurs
END;

游標帶參數

直接返回和參數相關的查詢結果

 DECLARE
    CURSOR emp_cursor
       (p_deptno NUMBER, p_job VARCHAR2) IS
        SELECT employee_id, last_name
        FROM employees
        WHERE department_id = p_deptno
        AND job_id = p_job;
BEGIN
    OPEN emp_cursor (80, 'SA_REP');
    . . .
    CLOSE emp_cursor;
    OPEN emp_cursor (60, 'IT_PROG');
    . . .
END;

在游標中使用FOR UPDATE NOWAIT

有的時候我們打開一個游標是為了更新或者刪除一些記錄,這種情況下我們希望
在打開游標的時候即鎖定相關記錄,應該使用for update nowait語句,倘若鎖定失敗我們就停止不再繼續,以免出現長時間等待資源的死鎖情況。

舉例:

DECLARE
    CURSOR emp_cursor IS
        SELECT employee_id, last_name, department_name
        FROM employees,departments
        WHERE employees.department_id =
              departments.department_id
        AND employees.department_id = 80
        FOR UPDATE OF salary NOWAIT; 

PLSQL中的異常

PLSQL中一般有兩種異常,一種是由Oracle內部錯誤拋出的異常,分為預定義和非預定義兩種;另外一種是由程序員顯式的拋出。

常見的異常:

異常代碼 異常類型
NO_DATA_FOUND 沒有發現數據
INVALID_CURSOR 游標在被打開以前,引用%NOTFOUND屬性會產生此異常
TOO_MANY_ROWS 返回數據過多
ZERO_DIVIDE 除數為零,出現異常
DUP_VAL_ON_INDEX 唯一索引上有重復值

對othres的處理

others表明我們未能預計的錯誤,所以全部歸到othres中去,單發生這種情況時,我們希望了解錯誤號和相關信息,可以使用Oracle內置的函數SQLCODE和SQLERRM來返回錯誤號和錯誤描述,舉例見下:

DECLARE
    v_error_code NUMBER;
    v_error_message VARCHAR2(255);
BEGIN
    ...
EXCEPTION
    ...
WHEN OTHERS THEN
    ROLLBACK;
    v_error_code := SQLCODE ;
    v_error_message := SQLERRM ;
    INSERT INTO errors
    VALUES(v_error_code, v_error_message);
END;

處理用戶自定義異常

此處圖片缺失

舉例:

DECLARE
    e_invalid_department EXCEPTION;
BEGIN
    UPDATE departments
    SET department_name = &p_department_desc
    WHERE department_id = &p_department_number;
    IF SQL%NOTFOUND THEN
        RAISE e_invalid_department;
    END IF;
    COMMIT;
EXCEPTION
    WHEN e_invalid_department THEN
        DBMS_OUTPUT.PUT_LINE('No such department id.');
END;

傳遞異常到外層代碼塊

DECLARE
    . . .
    e_no_rows exception;
    e_integrity exception;
    PRAGMA EXCEPTION_INIT (e_integrity, -2292);
BEGIN
    FOR c_record IN emp_cursor LOOP
      --內層代碼塊
      BEGIN
        SELECT ...
        UPDATE ...
        IF SQL%NOTFOUND THEN
            RAISE e_no_rows; --將異常扔到外層代碼塊中,使用關鍵字:RAISE
        END IF;
      END;

    END LOOP;
EXCEPTION
    WHEN e_integrity THEN ...
    WHEN e_no_rows THEN ...
END;

存儲過程

存儲過程基本語法

語法:

CREATE [OR REPLACE] PROCEDURE procedure_name
    [(parameter1 [mode1] datatype1,
    parameter2 [mode2] datatype2,
    . . .)]
IS|AS
PL/SQL Block;

舉例:

CREATE OR REPLACE PROCEDURE raise_salary
    (p_id IN employees.employee_id%TYPE)
IS
BEGIN
    UPDATE employees
    SET salary = salary * 1.10
    WHERE employee_id = p_id;
END raise_salary;

存儲過程參數模式

IN OUT IN OUT
默認模式 必須顯示指定 必須顯示指定
用以把值傳給過程 用以把值從過程返回給調用環境 用以把變量傳遞給過程,并返回給調用環境
參數可以是常數、變量、表 必須是個變量 必須是個變量
必須是個變量 不能賦予默認值 不能賦予默認值

舉例:IN OUT型變量的應用

--程序功能為:將傳入的電話號碼字符串按xxx-xxx-xxxxx的形式返回
CREATE OR REPLACE PROCEDURE format_phone
    (p_phone_no IN OUT VARCHAR2)
IS
BEGIN
    p_phone_no := '(' || SUBSTR(p_phone_no,1,3) ||
    ')' || SUBSTR(p_phone_no,4,3) ||
    '-' || SUBSTR(p_phone_no,7);
END format_phone;
/

存儲過程的參數傳遞

  • 使用默認值
  • 按順序傳遞
  • 使用=>傳遞

舉例:

首先創建存儲過程add_dept,設定了兩個參數,p_namep_loc

--設定默認參數使用關鍵字:DEFAULT
CREATE OR REPLACE PROCEDURE add_dept
    (p_name IN departments.department_name%TYPE DEFAULT 'unknown',
    p_loc IN departments.location_id%TYPE DEFAULT 1700)
IS
BEGIN
    INSERT INTO departments(department_id,
                department_name, location_id)
    VALUES (departments_seq.NEXTVAL, p_name, p_loc);
END add_dept;
/

然后,使用調用add_dept,分別使用三種方式傳遞參數

BEGIN
    add_dept; --1.默認參數
    add_dept ('TRAINING', 2500); --2.順序傳遞
    add_dept ( p_loc => 2400, p_name =>'EDUCATION');--3.使用=>傳遞
    add_dept ( p_loc => 1200) ;
END;
/
SELECT department_id, department_name, location_id
FROM departments; --sql查詢驗證

存儲過程處理異常

此處圖片缺失

函數

自治事務

關鍵字:

一個事務A在另一個事務B內被調用,那個事務A是自治事務,自治事務A執行過程中會脫離其session內未執行完畢的事務的影響。如果session從B事務開始——A事務開始和結束——B事務結束,上述的A事務不受沒有完成的B事務的影響,然后A事務執行完畢后再次回到B事務執行沒有完成的B事務。

創建自治事務語法:

Create or replace procedure pro_name as pragma AUTONOMOUS_TRANSACTION;
Begin
 null;
End;

下面通過一個例子來對自治事務進行說明和解釋,首先創建一個procedure—noautonomous_transaction01

Create or replace procedure noautonomous_transaction01 as
Begin
    Insert into test01 select * from test01 where rownum=1;
    Commit;
End;
--進行查詢測試
Select count(*) from test01
--結果:Count(*)|25

此時,發現test01有25條數據

declare
begin
    Insert into test01 select * from test01 where rownum=1;
    Noautonomous_transaction01;--調用事務
    Rollback;
End;

上述pl/sql程序塊內嵌套了一個dml的procedure過程。執行pl/sql程序塊結束后發現test01有27條數據,也就說明了procedure中的commit完成了上述procedure和pl/sql程序塊的提交。最后的rollback也就沒有任何效果。

結論:非自治事務內的procedure中得commit或者rollback會影響前面所有dml的影響。

現在創建一個自治事務:

--Create Procedure的autonomous_transaction01
Create or replace procedure autonomous_transaction01 as pragma autonomous_transaction01;
Begin
    Insert into test01select * from test01 where rownum=1;
    Commit;
End;
--查詢進行測試
Select count(*) from test01
--Count(*)|25

此時test01有25條數據。然后,在pl/sql程序塊中調用自治事務autonomous_transaction01

Begin
Insert into test01 select * from test01 where rownum=1;
    Autonomous_transaction01;
    Rollback;--rollback并沒有影響到自治事務內的操作
End;

自治事務內的commit只會影響自治事務內的沒有提交的dml,調用自治事務完畢后又會回到調用自治事務的事務內,此時最后的rollback也只能回滾自治事務外的dml,所以此時的test01中還是只有25條數據。

自治事務與被調用事務完全獨立,不能共享調用者事務使用的鎖和其他資源,而且自治事務內還可以調用其他自治事務。自治事務與其調用者可能會發生死鎖,oracle會檢測此類死鎖并返回錯誤信息。

文件操作

系統函數:

utl_file.fopen(三個參數:文件對象,文件名,打開方式)       打開文件
utl_file.put_line(文件對象,字符串)    輸出文件
utl_file.get_line()    讀取文件
utl_file.fclose()      關閉文件
utl_file.fflush()      強制輸出緩沖

文件打開方式

也就是fopen()的第三個參數:

r -- read text
w -- write text
a -- append text
rb -- read byte mode
wb -- write byte mode
ab -- append byte mode
--如果指定'a'或者'ab'但是文件不存在會先創建。

舉例:

CREATE OR REPLACE PROCEDURE FILE_TEMP IS
   FILEHANDLE UTL_FILE.FILE_TYPE;--定義文件對象
BEGIN
  --打開文件
  FILEHANDLE := UTL_FILE.fopen('FILENAME' ,'FILE_TEMP.TXT' ,'W' );
  --寫入文件
  UTL_FILE.put_line(FILEHANDLE,'這是一個練習和測試');
  FOR TEMP_1 IN (SELECT * FROM EMPLOYEES) LOOP
   --寫入文件
   UTL_FILE.put_line(FILEHANDLE,TEMP_1.LAST_NAME || '|' || TEMP_1.SALARY);
  END LOOP;
  UTL_FILE.FCLOSE(FILEHANDLE);--關閉文件
END;

引用游標

create or replace procedure test_ref1
  is
   type s_table is ref cursor;
   f  s_table;
   f_rec t1% ;
   stmt varchar2(100) := 'select empno,ename from emp';
begin
   insert into t1 select empno,ename,sal from emp;
   open f for select * from t1;
   loop
       fetch f into f_rec;
       dbms_output.put_line(f_rec.empno ||'  '||f_rec.ename);
       exit when f%rowcount=3 ;
   end loop;
   close f;
end;
/

上課記錄

  • begin ...end; 為一個程序塊
  • 匿名塊不能被調用,因為沒有名字,一般用于測試,可以直接執行
  • 存儲過程和函數相比,沒有返回值,兩者都需要編譯后進行調用,才可以執行。
  • 返回值和參數定義不需要指定長度,定義變量需要指定長度
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容