1. 程式人生 > >PL/SQL 編程(一)基礎,變量,分支,循環,異常

PL/SQL 編程(一)基礎,變量,分支,循環,異常

變量類型 set art blog replace 過程 value ase ng-

SQL和PL/SQL:

SQL 結構化查詢語言(Structural Query Language),是用來訪問和操作關系型數據庫的一種標準通用語言,屬於第四代語言(4GL)。可以方便的調用相應語句來去的結果,特點是非過程化,使用的時候不用指明執行的具體方法,不用關註實現的細節,但是某些情況下滿足不了復雜業務流程的需求。

PL/SQL是 Procedure Language & Structured Query Language 的縮寫。屬於第三代語言(3GL),是一種過程化語言。PL/SQL是對SQL語言存儲過程語言的擴展,是一種高級數據庫程序設計語言,該語言專門用於在各種環境下對Oracle數據庫進行訪問。除此之外,可以在Oracle數據庫的某些客戶端工具中,使用PL/SQL語言也是該語言的一個特點。PL/SQL可以向Java一樣實現邏輯判斷。條件循環和異常處理等。

同傳統的SQL相比PL/SQL有以下優點:

1.可以提高程序的運行性能。

2.可以使程序模塊化。

3.可以采用邏輯控制語句來控制程序結構。

4.利用處理運行時的錯誤信息。

5.良好的可移植性。

PL/SQL塊

pl/sql的基本單位是塊。分為三部分,聲明部分,執行部分,異常處理部分。其中執行部分時必須存在的,聲明和異常處理可以沒有。

--PL/SQL塊的結構如下:
 DECLARE  
    --聲明部分: 在此聲明PL/SQL用到的變量,類型及遊標,以及局部的存儲過程和函數
  BEGIN
    -- 執行部分:  過程及SQL 語句  , 即程序的主要部分
  EXCEPTION
    
-- 執行異常部分: 錯誤處理 END;

變量 常量

變量表示的值是可以變化的,常量初始化後,其值不可改變。

需要註意:pl/sql是一種強類型語言。

如果表示常量,必須用CONSTANT關鍵字。

標量類型變量:

最簡單類型的變量,它本身是單一的值,不包含任何的類型組合,標量類型主要包含數值類型,字符類型,布爾類型,日期類型。還有一種特殊的聲明變量類型的方式: %type

引用型變量:

使用%TYPE,利用已存在的數據類型定義新變量的數據類型。最常見的就是把表中字段類型作為變量或常量的數據類型。

使用%TYPE特性的優點在於:

l 所引用的數據庫列的數據類型可以不必知道;

l 所引用的數據庫列的數據類型可以實時改變,容易保持一致,也不用修改PL/SQL程序。

DECLARE
   -- 用%TYPE 類型定義與表相配的字段
   TYPE T_Record IS RECORD(
        T_no emp.empno%TYPE,
        T_name emp.ename%TYPE,
        T_sal emp.sal%TYPE );
   -- 聲明接收數據的變量
   v_emp T_Record;
BEGIN
   SELECT empno, ename, sal INTO v_emp FROM emp WHERE empno=7788;
   DBMS_OUTPUT.PUT_LINE
    (TO_CHAR(v_emp.t_no)|| ||v_emp.t_name||   || TO_CHAR(v_emp.t_sal));
END;
DECLARE
   v_empno emp.empno%TYPE :=&no;
   Type t_record is record (
        v_name   emp.ename%TYPE,
        v_sal    emp.sal%TYPE,
        v_date   emp.hiredate%TYPE);
   Rec t_record;
BEGIN
   SELECT ename, sal, hiredate INTO Rec FROM emp WHERE empno=v_empno;
   DBMS_OUTPUT.PUT_LINE(Rec.v_name||---||Rec.v_sal||--||Rec.v_date);
END;

記錄型變量:

它把邏輯相關的、分離的、基本數據類型的變量組成一個整體存儲起來,它必須包括至少一個標量型或RECORD 數據類型的成員,稱作PL/SQL RECORD 的域(FIELD),其作用是存放互不相同但邏輯相關的信息。在使用記錄數據類型變量時,需要先在聲明部分先定義記錄的組成、記錄的變量,然後在執行部分引用該記錄變量本身或其中的成員。

該類型可以包含一個或多個成員,每個成員類型可以不同。成員可以是標量類型,也可以是引用類型。記錄類型適合處理查詢語句中有多個列的情況,比如調用某個表的一行記錄時用記錄類型變量存儲這行記錄。

--可以用 SELECT語句對記錄變量進行賦值,只要保證記錄字段與查詢結果列表中的字段相配即可。
 DECLARE 
   TYPE test_rec IS RECORD(
         Name VARCHAR2(30) NOT NULL := 胡勇,
         Info VARCHAR2(100));
   rec_book test_rec;
BEGIN
   rec_book.Name :=胡勇;
   rec_book.Info :=談PL/SQL編程;;
   DBMS_OUTPUT.PUT_LINE(rec_book.Name||   ||rec_book.Info);
END; 
--一個記錄類型的變量只能保存從數據庫中查詢出的一行記錄,若查詢出了多行記錄,就會出現錯誤。 
DECLARE
--定義與hr.employees表中的這幾個列相同的記錄數據類型
   TYPE RECORD_TYPE_EMPLOYEES IS RECORD(
        f_name   hr.employees.first_name%TYPE,
        h_date   hr.employees.hire_date%TYPE,
        j_id     hr.employees.job_id%TYPE);
--聲明一個該記錄數據類型的記錄變量
   v_emp_record RECORD_TYPE_EMPLOYEES;

BEGIN
   SELECT first_name, hire_date, job_id INTO v_emp_record
   FROM employees
   WHERE employee_id = &emp_id;

   DBMS_OUTPUT.PUT_LINE(雇員名稱:||v_emp_record.f_name
             ||  雇傭日期:||v_emp_record.h_date
             ||  崗位:||v_emp_record.j_id);
END;

使用%ROWTYPE聲明記錄類型數據

這種聲明方式可以直接引用表中的行作為變量類型,同 %type 相似。

使用%ROWTYPE特性的優點在於:

l 所引用的數據庫中列的個數和數據類型可以不必知道;

l 所引用的數據庫中列的個數和數據類型可以實時改變,容易保持一致,也不用修改PL/SQL程序。

DECLARE
    v_empno emp.empno%TYPE :=&no;
    rec emp%ROWTYPE;
BEGIN
    SELECT * INTO rec FROM emp WHERE empno=v_empno;
    DBMS_OUTPUT.PUT_LINE(姓名:||rec.ename||工資:||rec.sal||工作時間:||rec.hiredate); 
END;

數組類型

是具有相同數據類型的一組成員的集合。每個成員都有一個唯一的下標,它取決於成員在數組中的位置。在PL/SQL中,數組數據類型是VARRAY。

DECLARE
--定義一個最多保存5個VARCHAR(25)數據類型成員的VARRAY數據類型
   TYPE reg_varray_type IS VARRAY(5) OF VARCHAR(25);
--聲明一個該VARRAY數據類型的變量
   v_reg_varray REG_VARRAY_TYPE;

BEGIN
--用構造函數語法賦予初值
   v_reg_varray := reg_varray_type
         (中國, 美國, 英國, 日本, 法國);

   DBMS_OUTPUT.PUT_LINE(地區名稱:||v_reg_varray(1)||
                                    ||v_reg_varray(2)||
                                    ||v_reg_varray(3)||
                                    ||v_reg_varray(4));
   DBMS_OUTPUT.PUT_LINE(賦予初值NULL的第5個成員的值:||v_reg_varray(5));
--用構造函數語法賦予初值後就可以這樣對成員賦值
   v_reg_varray(5) := 法國;
   DBMS_OUTPUT.PUT_LINE(第5個成員的值:||v_reg_varray(5));
END;

賦值:賦值要用 := 。

結構控制:

if條件控制:

和Java基本相同。

declare
  a varchar(10);
  b number(10);
  c number(10);
begin
  a := ;
  dbms_output.put_line(a);

  b := 2;
  c := 3;
  --分支
  if b > c then
    dbms_output.put_line(b大於c);
  elsif b < c then
    dbms_output.put_line(b小於c);
  else
    dbms_output.put_line(b等於c);
  end if;

end;
DECLARE
    v_empno  employees.employee_id%TYPE :=&empno;
    V_salary employees.salary%TYPE;
    V_comment VARCHAR2(35);
BEGIN
   SELECT salary INTO v_salary FROM employees 
   WHERE employee_id = v_empno;
   IF v_salary < 1500 THEN
       V_comment:= 太少了,加點吧~!;
   ELSIF v_salary <3000 THEN
      V_comment:= 多了點,少點吧~!;
   ELSE
      V_comment:= 沒有薪水~!;
   END IF;
   DBMS_OUTPUT.PUT_LINE(V_comment);
   exception
     when no_data_found then
        DBMS_OUTPUT.PUT_LINE(沒有數據~!);
     when others then
        DBMS_OUTPUT.PUT_LINE(sqlcode || --- || sqlerrm);        
END;
DECLARE
   v_first_name  VARCHAR2(20);
   v_salary NUMBER(7,2);
BEGIN
   SELECT first_name, salary INTO v_first_name, v_salary FROM employees
   WHERE employee_id = &emp_id;
   DBMS_OUTPUT.PUT_LINE(v_first_name||雇員的工資是||v_salary);
   IF v_salary < 10000 THEN
      DBMS_OUTPUT.PUT_LINE(工資低於10000);
   ELSE
      IF 10000 <= v_salary AND v_salary < 20000 THEN
         DBMS_OUTPUT.PUT_LINE(工資在10000到20000之間);
      ELSE
         DBMS_OUTPUT.PUT_LINE(工資高於20000);
      END IF;
   END IF;
END;
DECLARE
   v_first_name  VARCHAR2(20);
   v_hire_date DATE;
   v_bonus NUMBER(6,2);
BEGIN
   SELECT first_name, hire_date INTO v_first_name, v_hire_date FROM employees
   WHERE employee_id = &emp_id;
   IF v_hire_date > TO_DATE(01-1月-90) THEN
      v_bonus := 800;
   ELSIF v_hire_date > TO_DATE(01-1月-88) THEN
      v_bonus := 1600;
   ELSE
      v_bonus := 2400;
   END IF;
   DBMS_OUTPUT.PUT_LINE(v_first_name||雇員的雇傭日期是||v_hire_date
                                    ||、獎金是||v_bonus);
END;

case語句

DECLARE
  V_grade char(1) := UPPER(&p_grade);
  V_appraisal VARCHAR2(20);
BEGIN
  V_appraisal :=
  CASE v_grade
    WHEN A THEN Excellent
    WHEN B THEN Very Good
    WHEN C THEN Good
    ELSE No such grade
  END;
  DBMS_OUTPUT.PUT_LINE(Grade:||v_grade||  Appraisal: || v_appraisal);
END;
DECLARE
   v_first_name employees.first_name%TYPE;
   v_job_id employees.job_id%TYPE;
   v_salary employees.salary%TYPE;
   v_sal_raise NUMBER(3,2);
BEGIN
   SELECT first_name,   job_id,   salary INTO
          v_first_name, v_job_id, v_salary
   FROM employees WHERE employee_id = &emp_id;
   CASE
      WHEN v_job_id = PU_CLERK THEN
         IF v_salary < 3000 THEN v_sal_raise := .08;
         ELSE v_sal_raise := .07;
         END IF;
      WHEN v_job_id = SH_CLERK THEN
         IF v_salary < 4000 THEN v_sal_raise := .06;
         ELSE v_sal_raise := .05;
         END IF;
      WHEN v_job_id = ST_CLERK THEN
         IF v_salary < 3500 THEN v_sal_raise := .04;
         ELSE v_sal_raise := .03;
         END IF;
      ELSE
         DBMS_OUTPUT.PUT_LINE(該崗位不漲工資: ||v_job_id);
   END CASE;
   DBMS_OUTPUT.PUT_LINE(v_first_name||的崗位是||v_job_id
                                    ||、的工資是||v_salary
                                    ||、工資漲幅是||v_sal_raise);
END;

循環:

declare
  a varchar(10);
  b number(10);
  c number(10);
  m number(10);
  sname varchar2(10);
begin
  a := ;
  dbms_output.put_line(a);

  b := 2;
  c := 3;
  --分支
  if b > c then
    dbms_output.put_line(b大於c);
  elsif b < c then
    dbms_output.put_line(b小於c);
  else
    dbms_output.put_line(b等於c);
  end if;
  --循環 1
  loop
    exit when c < 0;
    dbms_output.put_line(loop: || c);
    c := c - 1;
  end loop;
  --循環 2

  while b > 0 loop
    dbms_output.put_line(while: || b);
    b := b - 1;
  end loop;
  --循環 3 
  for n in 1 .. 3 loop
    dbms_output.put_line(for: || n);
  end loop;

end;

異常

編譯時的錯誤不能稱為異常。

有三種類型的異常錯誤:

1. 預定義 ( Predefined )錯誤

ORACLE預定義的異常情況大約有24個。對這種異常情況的處理,無需在程序中定義,由ORACLE自動將其引發。

2. 非預定義 ( Predefined )錯誤

即其他標準的ORACLE錯誤。對這種異常情況的處理,需要用戶在程序中定義,然後由ORACLE自動將其引發。

3. 用戶定義(User_define) 錯誤

預定義異常一覽:

技術分享

處理異常:

select s.name into sname from z_student s where s.id=m;
  dbms_output.put_line(查詢結果: || sname);
  exception 
    when no_data_found then 
      dbms_output.put_line(無查詢結果);

--預定義異常

DECLARE
   v_empno employees.employee_id%TYPE := &empno;
   v_sal   employees.salary%TYPE;
BEGIN
   SELECT salary INTO v_sal FROM employees WHERE employee_id = v_empno;
   IF v_sal<=1500 THEN 
        UPDATE employees SET salary = salary + 100 WHERE employee_id=v_empno; 
        DBMS_OUTPUT.PUT_LINE(編碼為||v_empno||員工工資已更新!);     
   ELSE
        DBMS_OUTPUT.PUT_LINE(編碼為||v_empno||員工工資已經超過規定值!);
   END IF;
EXCEPTION
   WHEN NO_DATA_FOUND THEN  
      DBMS_OUTPUT.PUT_LINE(數據庫中沒有編碼為||v_empno||的員工);
   WHEN TOO_MANY_ROWS THEN
      DBMS_OUTPUT.PUT_LINE(程序運行錯誤!請使用遊標);
   WHEN OTHERS THEN
      DBMS_OUTPUT.PUT_LINE(SQLCODE||---||SQLERRM);
END; 

非預定義異常

對於這類異常情況的處理,首先必須對非定義的ORACLE錯誤進行定義。步驟如下:

1. 在PL/SQL 塊的定義部分定義異常情況:

<異常情況> EXCEPTION;

2. 將其定義好的異常情況,與標準的ORACLE錯誤聯系起來,使用EXCEPTION_INIT語句:

PRAGMA EXCEPTION_INIT(<異常情況>, <錯誤代碼>);

3. 在PL/SQL 塊的異常情況處理部分對異常情況做出相應的處理。

--刪除指定部門的記錄信息,以確保該部門沒有員工。
 
INSERT INTO departments VALUES(50, FINANCE, CHICAGO);

DECLARE
   v_deptno departments.department_id%TYPE := &deptno;
   deptno_remaining EXCEPTION;
   PRAGMA EXCEPTION_INIT(deptno_remaining, -2292);
   /* -2292 是違反一致性約束的錯誤代碼 */
BEGIN
   DELETE FROM departments WHERE department_id = v_deptno;
EXCEPTION
   WHEN deptno_remaining THEN 
      DBMS_OUTPUT.PUT_LINE(違反數據完整性約束!);
   WHEN OTHERS THEN
      DBMS_OUTPUT.PUT_LINE(SQLCODE||---||SQLERRM);
END;

自定義異常

步驟如下:

1. 在PL/SQL 塊的定義部分定義異常情況:

<異常情況> EXCEPTION;

2. RAISE <異常情況>;

3. 在PL/SQL 塊的異常情況處理部分對異常情況做出相應的處理。

  if m = 0 then
    raise nozero;
  end if;

exception
  when nozero then
    dbms_output.put_line(m不能為0);

--更新指定員工工資,增加100;
 
DECLARE
   v_empno employees.employee_id%TYPE :=&empno;
   no_result  EXCEPTION;
BEGIN
   UPDATE employees SET salary = salary+100 WHERE employee_id = v_empno;
   IF SQL%NOTFOUND THEN
      RAISE no_result;
   END IF;
EXCEPTION
   WHEN no_result THEN 
      DBMS_OUTPUT.PUT_LINE(你的數據更新語句失敗了!);
   WHEN OTHERS THEN
      DBMS_OUTPUT.PUT_LINE(SQLCODE||---||SQLERRM);
END;
--創建一個函數get_salary, 該函數檢索指定部門的工資總和,其中定義了-20991和-20992號錯誤,分別處理參數為空和非法部門代碼兩種錯誤:
 
CREATE TABLE errlog(
  Errcode NUMBER,
  Errtext CHAR(40));

CREATE OR REPLACE FUNCTION get_salary(p_deptno NUMBER)
RETURN NUMBER 
AS
  v_sal NUMBER;
BEGIN
  IF p_deptno IS NULL THEN
    RAISE_APPLICATION_ERROR(-20991, ’部門代碼為空’);
  ELSIF p_deptno<0 THEN
    RAISE_APPLICATION_ERROR(-20992, ’無效的部門代碼’);
  ELSE
    SELECT SUM(employees.salary) INTO v_sal FROM employees 
    WHERE employees.department_id=p_deptno;
    RETURN v_sal;
  END IF;
END;
DECLARE 
  V_salary NUMBER(7,2);
  V_sqlcode NUMBER;
  V_sqlerr VARCHAR2(512);
  Null_deptno EXCEPTION;
  Invalid_deptno EXCEPTION;
  PRAGMA EXCEPTION_INIT(null_deptno,-20991);
  PRAGMA EXCEPTION_INIT(invalid_deptno, -20992);
BEGIN
  V_salary :=get_salary(10);
  DBMS_OUTPUT.PUT_LINE(10號部門工資: || TO_CHAR(V_salary));

  BEGIN
    V_salary :=get_salary(-10);
  EXCEPTION
    WHEN invalid_deptno THEN
      V_sqlcode :=SQLCODE;
      V_sqlerr  :=SQLERRM;
      INSERT INTO errlog(errcode, errtext) 
      VALUES(v_sqlcode, v_sqlerr);
      COMMIT;
  END inner1;

  V_salary :=get_salary(20);
  DBMS_OUTPUT.PUT_LINE(部門號為20的工資為:||TO_CHAR(V_salary));

  BEGIN
    V_salary :=get_salary(NULL);
  END inner2;

  V_salary := get_salary(30);
  DBMS_OUTPUT.PUT_LINE(部門號為30的工資為:||TO_CHAR(V_salary));

  EXCEPTION
    WHEN null_deptno THEN
      V_sqlcode :=SQLCODE;
      V_sqlerr  :=SQLERRM;
      INSERT INTO errlog(errcode, errtext) VALUES(v_sqlcode, v_sqlerr);
      COMMIT;
    WHEN OTHERS THEN
         DBMS_OUTPUT.PUT_LINE(SQLCODE||---||SQLERRM);
END outer;

PL/SQL 編程(一)基礎,變量,分支,循環,異常