PL/SQL 編程(一)

1、PL/SQL概念java

  1. PL/SQL:Procedural language/sql,是oracle在標準sql語句上的擴展。PL/SQL不只容許嵌入sql語言,還能夠定義變量和常量,容許使用條件語句和循環語句,容許使用例外處理各類錯誤。sql


    存儲過程、函數、觸發器都是存放在oracle數據庫當中,比較複雜的任務通常能夠用存儲過程來實現,而這些都是用PL/SQL來編寫的。存儲過程、函數能夠用Java程序來調用。數據庫

    PL/SQL的優勢:數組

    1. 提升應用程序的運行性能:安全

      傳統操做數據庫方法是:SQL語句是寫在Java程序中,而後Java程序獲得一個connection,數據庫拿到SQL語句                                        後須要編譯(時間),將語句翻譯成可識別的語言。而後返回結果。網絡

      存儲過程:減小Java程序中的SQL語言,並且存儲過程已經在數據庫中編譯好。oracle

    2. 模塊化的設計思想:直接傳參數調用過程ide

    3. 減小網絡傳輸量:與傳整個sql語句相比,調用存儲過程只須要穿過程名,參數便可。模塊化

    4. 提升安全性:存儲過程避免了重要信息的暴露,由於直接在數據庫中。函數

    PL/SQL缺點:

    1. 移植性很差:若是數據庫變換,則這些存儲過程全都要推翻掉。

  2. PL/SQL的編寫工具:Sql plus 或 PL/SQL developer。後者是用於開發pl/sql塊的集成開發環境(IDE),這個一個獨立的產品,而不是oracle的附帶品。

  3. 一個簡單案例:編寫一個存儲過程,向某表添加記錄。

  4. --建立一個表
    create table mytest(
    name varchar2(20),
    password varchar2(20)
    );
    
    --建立存儲過程
    create or replace procedure pro_insertMytest is
    begin
      --執行部分
      insert into mytest values ('SMITH', 'm123');
    end;
    
    --最後輸入/表示執行存儲過程
    --若是有錯誤,SQL> show error; 顯示錯誤信息
    
    --如何調用該存儲過程
    --方式一: exec procedureName(param1, param2...);
    --方式二: call procedureName(param1, param2...);

2、PL/SQL 基礎知識

  1. PL/SQL能夠編寫什麼:

    1. 塊(block)是pl/sql的基本程序單元,編寫pl/sql程序實際上就是編寫pl/sql塊。要實現相對複雜的功能,可能須要在一個pl/sql塊中嵌套其餘的pl/sql塊。

    2. 簡單分類:  


    3. 編寫規範:

      1. 註釋:

        單行註釋:--     多行註釋: /* ...  */

      2. 標識符號的命名規範

        1) 定義變量時,建議使用 v_  做爲前綴  v_name

        2) 定義常量時,建議使用 c_  做爲前綴  c_rate

        3) 定義遊標時,建議使用 _cursor  做爲後綴  emp_cursor

        4) 定義例外時,建議使用 e_  做爲前綴  e_error

  2. PL/SQL 塊的結構:

    由三個部分構成:定義部分、執行部分、例外處理部分,以下:

    declare

    /* 定義部分----定義常量、變量、遊標、例外、複雜數據類型 */

    begin

    /* 執行部分----要執行的pl/sql 語句和 sql 語句 */

    exception

    /* 例外處理部分----處理運行時的各類錯誤 */

    說明:定義部分是從declare開始的,可選;執行部分從begin開始,必須;處理部分是從exception開始,可選。

  3. PL/SQL塊的實例:

  4. /*
      實例1-最簡單的塊
      說明:dbms_output 是oracle提供的包(package),相似於java的開發包,該包包含一些過程,put_line是該包的一個過程。
    */
    set serveroutput on;  --打開輸出選項    set serveroutput off; 關閉
    begin
      dbms_output.put_line('Hello World');
    end;
    
    -------------------------------華麗的分割線-------------------------------------------
    
    /*
      實例2-包含定義部分和執行部分的pl/sql塊
      說明: & 表示要接收從控制檯輸入的變量
    */
    declare
      v_ename varchar2(10);  -- 定義字符串變量
      v_sal number(7,2);
    begin
      --執行部分
      select ename, sal into v_ename, v_sal from emp where empno=&EmployeeNo;
      --在控制檯顯示用戶名
      dbms_output.put_line('Employee Name:' || v_ename || '   Salary:' || v_sal);
    end;
    
    -------------------------------華麗的分割線-------------------------------------------
    
    /*
      實例3-包含定義部分、執行部分和例外處理的部分的pl/sql塊
      說明: 上面的實例2,若是沒有找到對應的empno,則須要進行異常處理
    */
    declare
      v_ename varchar2(10);  -- 定義字符串變量
      v_sal number(7,2);
    begin
      --執行部分
      select ename, sal into v_ename, v_sal from emp where empno=&EmployeeNo;
      --在控制檯顯示用戶名
      dbms_output.put_line('Employee Name:' || v_ename || '   Salary:' || v_sal);
    exception
      when no_data_found then dbms_output.put_line('Employee number not exists!');
    end;
  5. 存儲過程:用於執行特定的操做。創建存儲過程時,能夠指定輸入參數(in)和指定輸出參數(out)。使用輸入參數,能夠將數據傳遞到執行部分,而使用輸出參數能夠將執行部分的數據傳遞到應用環境。

    實例:編寫一個存儲過程,輸入員工名、新工資去修改該員工的工資。以及用java調用存儲過程。

  6. /*
      實例4-編寫一個存儲過程,輸入員工名、新工資去修改該員工的工資
      說明: 執行帶參數的存儲過程:  exec pro_updateEmp('SMITH', 4678);
    */
    create or replace procedure pro_updateEmp(empName varchar2, newSal number) is
    --此處能夠定義變量
    begin
      --執行部分
      update emp set sal = newSal where ename = empName;
    end;                                                                                                                                                                                           -------------------------------華麗的分割線-------------------------------------------          //Java 程序調用存儲過程                                                                              public class TestOracleProcedure {
    	    public static void main(String[] args) {
    		    try {
    			//加載驅動
    			Class.forName("oracle.jdbc.driver.OracleDriver");
    			
    			//獲取鏈接
    			Connection conn = DriverManager.getConnection("jdbc:oracle:thin:@127.0.0.1:1521:ORCL", "dog", "dog");
    			
    			//建立CallableStatement
    			//注:procedure 名大小寫要保持和數據庫一致
    			CallableStatement cs = conn.prepareCall("{call PRO_UPDATEEMP(?, ?)}");
    			cs.setString(1, "SMITH");
    			cs.setInt(2, 100);
    						
    			//執行
    			cs.execute();
    
    			//關閉資源
    			cs.close();
    			conn.close();
    			
    		    } catch (Exception e) {
    			e.printStackTrace();
    		    }
    	    }
        }
  7. 函數:用於返回特定的數據,當創建函數時,在函數頭部必須包含return子句,而在函數體內必須包含return語句返回的數據。

  8. --函數案例
    --輸入員工名,返回該員工的年薪
    create or replace function fun_YearSal(EmpName varchar2) return number is YearSal number(7,2);
    begin
      --執行部分
      select (sal+nvl(comm, 0))*12 into YearSal from emp where ename = EmpName;
      return YearSal;
    end;
    
    --如何調用function
    --定義變量abc接收返回結果
    var abc number;
    call fun_YearSal('SMITH') into:abc;
  9. 包:用於在邏輯上組合存儲過程和函數,它由包規範和包體兩部分組成。包的規範只包含了存儲過程和函數的說明,可是沒有存儲過程和函數的實現代碼。包體用於實現包規範中的存儲過程和函數。

  10. --包 案例
    --建立包 pak_test,並聲明該包有 存儲過程 PRO_UPDATEEMP 和函數 FUN_YEARSAL
    create or replace package pak_test is
      procedure PRO_UPDATEEMP(empName varchar2, newSal number);
      function FUN_YEARSAL(EmpName varchar2) return number;
    end;
    
    --接下來要實現包體
    create or replace package body pak_test is
      --實現包中的存儲過程
      procedure pro_updateEmp(empName varchar2, newSal number) is
      begin
        update emp set sal = newSal where ename = empName;
      end;
      --實現包中的函數
      function fun_YearSal(EmpName varchar2) return number is YearSal number(7,2);
      begin
        select (sal+nvl(comm, 0))*12 into YearSal from emp where ename = EmpName;
        return YearSal;
      end;
    end;
    
    --如何調用包的存儲過程和函數:當調用包的存儲過程和函數時,在過程和函數前須要帶有包名。若是要訪問其餘方案的包,還須要在包名前加方案名。
    --包是pl/sql種很是重要的部分
    call pak_test.updateEmp('SMITH', '2000');
  11. 觸發器:指隱含的執行的存儲過程,通常不會主動調用。當定義觸發器時,必需要指定觸發的事件和操做。經常使用的觸發時間包括insert, update, delete 語句,而觸發操做實際就是一個pl/sql塊。

    觸發器是很是有用的,能夠維護數據庫的安全和一致性。(PL/SQL第二篇中講述)

  12. 定義並使用變量:

    編寫pl/sql程序時,能夠定義變量和常量;在pl/sql程序中包括有:標量類型(scalar)、複合類型(composite)、參照類型(reference)、lob (large object)

    a. 標量類型(scalar)

        ---經常使用類型

        identifier [constant] datatype [not null] [:=| default expr]

        identifier:名稱   constant:常量。須要指定初始值,並且值不能改變。  datatype:數據類型  not null:不能爲       空    :=:給變量或者常量指定初始值   default:用於指定初始值  expr:指定初始值的pl/sql表達式,能夠是文本         值、其餘變量、函數等。

        案例:--定義一個變長字符串  v_ename varchar2(20)

                  --定義一個小數  範圍-9999.99~9999.99  v_sal number(6,2)

                  --定義一個小數並給一個初始值5.4 (:= 是pl/sql的賦值號 )   v_sal2 number(6,2):=5.4

                  --定義一個日期類型的數據  v_date date; 

                  --定義一個布爾變量, 不能爲空,初始值爲false     v_valid boolean not null default false;

         ---%type 類型的標量,對於上面的經常使用標量,若是實際的變量長度超過了定義的長度,則會報錯。爲了下降pl/sql             程序的維護工做量,可使用%type屬性定義變量,這樣它會按照數據庫列來肯定定義的變量的類型和長度。

              標識符名 表名.列名%type

  13. /* 案例:輸入員工號,顯示員工姓名、工資、我的所得稅(稅率0.03), 說明變量的使用*/
    declare
       --定義標量
       c_tax_rate number(3,2):=0.03;
       v_ename varchar2(6);
       v_sal number(7,2);
       v_tax number(7,2);
    begin
      --執行
      select ename, sal into v_ename, v_sal from emp where empno=&EmployeeNum;
      --計算所得稅
      v_tax:=v_sal*c_tax_rate;
      --輸出
      dbms_output.put_line('Ename: '||v_ename||'  Salary: '||v_sal||'  Tax: '||v_tax);
    exception
      when no_data_found then dbms_output.put_line('Employee number not exists!'); 
    end;
    
    --上述案例使用 %type 來定義變量,使用較多
    declare
       --定義標量
       c_tax_rate number(3,2):=0.03;
       v_ename emp.ename%type;
       v_sal emp.sal%type;
       v_tax number(7,2);
    begin
      --執行
      select ename, sal into v_ename, v_sal from emp where empno=&EmployeeNum;
      --計算所得稅
      v_tax:=v_sal*c_tax_rate;
      --輸出
      dbms_output.put_line('Ename: '||v_ename||'  Salary: '||v_sal||'  Tax: '||v_tax);
    exception
      when no_data_found then dbms_output.put_line('Employee number not exists!'); 
    end;

    b. 複合類型(composite):存放多個值的變量,主要包括:pl/sql 記錄、pl/sql 表、嵌套表、varray 四種,下面將介     紹前兩種比較經常使用的。

  14. /*
    複合類型--pl/sql記錄
    相似高級語言中的結構體,注意:當引用pl/sql記錄成員時,必需要加記錄變量做爲前綴(記錄變量名.記錄成員)
    */
    declare
      --定義一個pl/sql記錄類型,名爲 emp_record_type,包含 ename,salary,title 三個數據
      type emp_record_type is record(
           ename emp.ename%type,
           salary emp.sal%type,
           title emp.job%type);
       --定義一個emp_record_type類型的變量,emp_record
       emp_record emp_record_type;
    begin
      select ename, sal, job into emp_record from emp where empno = 7369;
      dbms_output.put_line('Ename: '||emp_record.ename||'  Salary: '||emp_record.salary||'  Title: '||emp_record.title);
    end;
    
    /*
    複合類型--pl/sql表
    相似高級語言中的數組,不一樣的是,pl/sql表中的元素下標能夠爲負,而且下標沒有限制
    */
    declare
      --定義一個pl/sql表類型,名爲emp_table_type,用於存放 emp.name%type 類型的數據
      -- index by binary_integer 表示下標是整數
      type emp_table_type is table of emp.ename%type index by binary_integer;
      --定義一個emp_table_type 類型的變量 emp_table
      emp_table emp_table_type;
    begin
      select ename into emp_table(-1) from emp where empno = 7369;
      dbms_output.put_line('Ename: '|| emp_table(-1));
    end;
    
    --問題:若是返回多行數據,該怎麼接收呢?

    c. 參照類型(reference):參照變量是指用於存放數值指針的變量。經過使用參照變量,能夠是應用程序共享相同對       象,從而下降佔用空間。在寫pl/sql程序時,可使用遊標變量(ref cursor)和對象類型變量(ref obj_type)兩種     參照變量類型。

  15. /*
    參照變量--ref cursor遊標變量
    定義遊標時,不須要指定select語句,但使用遊標時(open) 須要指定select 語句,這樣遊標就和select語句結合
    */
    --案例:輸入部門號,顯示該部門全部員工的姓名和工資
    declare
      --定義遊標類型
      type emp_cursor_type is ref cursor;
      --定義遊標變量
      emp_cursor emp_cursor_type;
      --定義兩個變量去接收結果
      v_ename emp.ename%type;
      v_sal emp.sal%type;
    begin
      --執行
      --將遊標emp_cursor 和 select語句結合,讓遊標指向結果集
      open emp_cursor for select ename, sal from emp where deptno = &DeptNum;
      --循環取出
      loop
        --取出遊標
        fetch emp_cursor into v_ename, v_sal;
        --循環結束條件,emp_cursor爲空
        exit when emp_cursor%notfound;
        dbms_output.put_line('Ename: '||v_ename||'  Salary: '||v_sal);
      end loop;
    end;
  16. pl/sql 控制結構

    a. 條件分支語句

  17. /*
    案例 
    實例11-條件分支語句:
          1.編寫一個存儲過程,輸入員工名,若是該員工工資低於2000,就給該員工工資增長10%
          2.(二重條件分支) 若是補助不是0,則增長100,不然補助爲200
          3.(多重條件分支) 若是該員工職位是PRESIDENT,工資加1000; 若是職位是 MANAGER,工資加500;其餘狀況工資加200
    */
    create or replace procedure update_sal(EmployeeName varchar2) is
    --定義
    v_sal emp.sal%type;
    v_comm emp.comm%type;
    v_job emp.job%type;
    begin
      --執行
      select sal, comm, job into v_sal, v_comm, v_job from emp where ename = EmployeeName;
      --判斷 1
      if v_sal<2000 then
        update emp set sal=sal*1.1 where ename = EmployeeName;
      end if;
      
      --判斷 2
      if v_comm <> 0 then
        update emp set comm=comm+100 where ename = EmployeeName;
      else
        update emp set comm=comm+200 where ename = EmployeeName;
      end if;
      
      --判斷3
      if v_job = 'PRESIDENT' then
        update emp set sal=sal + 1000 where ename = EmployeeName;
      elsif v_job = 'MANAGER' then
        update emp set sal=sal + 500 where ename = EmployeeName;
      else
        update emp set sal=sal + 200 where ename = EmployeeName;
      end if;
      
      exception
        when no_data_found then dbms_output.put_line('Employee number not exists!');
    end;

    b. 循環語句:pl/sql中包含三種循環,loop、while、for

  18. /*
    案例
    實例12-循環語句: loop
           經過輸入Username 向Users表中循環添加10條數據
    */
    --create table sql
    drop table users;
    
    create table users(
    userid number(10) primary key,
    username varchar2(30) not null,
    password varchar2(30));
    
    create or replace procedure loop_add_user1(Username varchar2) is
    --定義
    v_num number:=1;
    begin
      loop
        insert into users values(v_num, Username, 'loop');
        --判斷是否要退出循環
        exit when v_num = 10;
        --num 自增
        v_num:=v_num + 1;
      end loop;
    end;
    
    /*
    案例 
    實例13-循環語句: while
           經過輸入Username 向Users表中循環添加10條數據,從11條記錄開始添加
    */
    create or replace procedure loop_add_user2(Username varchar2) is
    --定義
    v_num number:=11;
    begin
      while v_num <= 20 loop
        insert into users values(v_num, Username, 'whileLoop');
        --判斷是否要退出循環
        exit when v_num = 10;
        --num 自增
        v_num:=v_num + 1;
      end loop;
    end;
    
    /*
    案例 
    實例14-循環語句: for
           經過輸入Username 向Users表中循環添加10條數據,從21條記錄開始添加
    */
    create or replace procedure loop_add_user3(Username varchar2) is
    begin
      --從21開始循環,直到30結束.能夠看到控制變量i在隱含中不停的增長
      for i in reverse 21..30 loop
        insert into users values(i, Username, 'forLoop');
      end loop;
    end;
  19. pl/sql 順序控制語句 goto 、 null

  20. /*
    案例
    實例15-順序控制語句: goto 、 null
    說明:goto end_loop,這裏end_loop是定義的標籤  <<end_loop>>
          null語句不會執行任何操做,而且會直接將控制傳遞到下一條語句,通常只是爲了提升程序可讀性
    */
    declare
    i int := 1;
    begin
      loop
        dbms_output.put_line('Output i=' || i);
        if i=10 then
          goto end_loop;
        else
          null;
        end if;
        i := i + 1;
      end loop;
      dbms_output.put_line('Cycle Over1');
      <<end_loop>>
      dbms_output.put_line('Cycle Over2');
    end;
相關文章
相關標籤/搜索