1. 程式人生 > >Oracle-4 - :超級適合初學者的入門級筆記:plsql,基本語法,記錄類型,循環,遊標,異常處理,存儲過程,存儲函數,觸發器

Oracle-4 - :超級適合初學者的入門級筆記:plsql,基本語法,記錄類型,循環,遊標,異常處理,存儲過程,存儲函數,觸發器

個人 就會 逗號 n) 循環結構 less 寫上 所有 targe

初學者可以從查詢到現在的pl/sql的內容都可以在我這裏的筆記中找到,希望能幫到大家,視頻資源在 資源,

我自己的全套筆記在 筆記

在pl/sql中可以繼續使用的sql關鍵字有:update delete insert select--into commit rollback savepoint ,在這裏需要註意的是查詢跟以前有些不一樣了

plsql由三個塊組成:聲明部分,執行部分,異常處理部分

  declare:在此聲明pl/sql用到的變量,類型及遊標,以及局部的存儲過程的和函數

  begin:執行部分:過程及sql語句,即程序的主要部分

  exception:執行異常部分,錯誤處理

  end

    其中執行部分是必要的

pl/sql之helloworld

    set SERVEROUTPUT ON --首先必須執行此語句,否則沒有輸出
    begin
    dbms_output.put_line(‘hello world‘);
    end; --下圖前三個均是沒有執行set serveroutput on 語句的執行結果,在執行完此語句才能有輸出

    其中只有begin部分,和結束end,因為此語句不需要變量聲明就省去了declare部分,和exception錯誤部分

      技術分享

 變量常量等的命名規則,下列標紅的就是建議使用的命名的開頭

      技術分享

   實例:用pl/sql查詢出tno為t001的老師的名字並輸出

      下面的select 語句是把查詢結果放到了變量v_name中然後輸出

      declare
        v_name TEACHER.TNAME%TYPE; --這裏是動態的獲取teacher表中tname字段的類型

         v_tno varchar2(10);
      begin
        --普通查詢語句 :select * from teacher where tno =‘t001‘;
        select tno,tname into v_tno,v_name from teacher where tno =‘t001‘;
        SYS.DBMS_OUTPUT.PUT_LINE(v_name||‘,‘||v_tno);
      end;

  記錄類型:是把邏輯相關的數據作為一個單元存儲起來,其作用是存放互不相同但邏輯相關的信息,類似java中一個類的概念一樣

        註意不能將select語句中的列賦值給布爾變量

      declare

          --在為一個變量賦值的時候的格式 : v_tno number(10) :=10; 自這裏 “:=”是賦值,判斷為=
        --type 自定義名1 is record
         type teacher_mas is record (
           v_name TEACHER.TNAME%TYPE, --逗號
           v_tno varchar2(10) --無標點符號
         );
         -- 定義一個記錄類型的成員變量
         --自定義2 自定義名1 在這就相當於創建了一個對象
         v_teacher_mas teacher_mas;

          --如果字段特別多的話 我們可以使用:v_teacher_mas teacher%rowtype; 表示與teacher表中的所有的類型都一直 ,下面就直接可以查詢 * into v_teacher_mas 了
      begin
        --普通查詢語句 :select * from teacher where tno =‘t001‘;
        select tno,tname into v_teacher_mas from teacher where tno =‘t001‘;
        SYS.DBMS_OUTPUT.PUT_LINE(v_teacher_mas.v_name||‘,‘||v_teacher_mas.v_tno);
      end;

  流程控制語句:

      if 語句結構: if 《條件表達式》 then ---- end if; 相當於 java中 if() {}

             if 《條件表達式》 then -- else-- end if; 相當於java中的 if(){} else {}

             if 《條件表達式》 then -- els if《條件表達式》-- endif;這裏是elsif 不是elseif 相當於java中的多重判斷了就 :if(){} elseif (){} eles{}

        實例:--查詢sno為s001的學生的c001課程的成績,如果大於60輸出及格 小於60輸出不及格 其他輸出一般

            每次的if或者elsif必須跟一個分隔符 用end if作為結束標誌,當然同一個if後可以加and
        declare
          v_score SC.SCORE%type;
        begin
          select score into v_score from sc where sc.sno=‘s001‘ and CNO=‘c001‘;
          if v_score<60 then SYS.DBMS_OUTPUT.PUT_LINE(‘不及格‘);
           elsif v_score >=60 then SYS.DBMS_OUTPUT.PUT_LINE(‘及格‘);
           else SYS.DBMS_OUTPUT.PUT_LINE(‘一般‘);
           end if;
        end;

     case語句結構:case 值

              when 表達式 then

              when 表達式 then

              else

            end;

     由於case比較惡心,sno為s001的學生的c001課程的成績為78.9,看下查詢sql,在這裏case跟Java、中的switch一樣

        case 一個值,when 後 只能跟常量,並且 then後不能賦值,輸出等,只可以返回結果

      declare
        v_score SC.SCORE%type;
        v_mas varchar2(30);
      begin
        select score into v_score from sc where sc.sno=‘s001‘ and CNO=‘c001‘;
        v_mas :=
          case v_score when 78.9 then ‘及格‘
             when 60 then ‘不及格‘
             else ‘一般‘
            end;

          SYS.DBMS_OUTPUT.PUT_LINE(v_mas);
       end;

   循環結構: 實例 輸出1--100 用循環結構

        1. loop...exit...where ....end loop   

           
          declare
            v_min number(3):=1;
          begin
             loop
             SYS.DBMS_OUTPUT.PUT_LINE(v_min);
            exit when v_min >= 100;
             v_min := v_min +1;
            end loop;
          end;     

        2. while<布爾表達式> loop 要執行的語句 end loop;

          declare
              v_i number(3):=1;
          begin
             while v_i <=100 loop
               SYS.DBMS_OUTPUT.PUT_LINE(v_i);
               v_i := v_i +1;
            end loop;
          end;

        3. for 循環計數器 in【reverse】 上限 .. 下限 loop 要執行的語句 end loop;不要忘記上限於下限中間有兩個點

            每循環一次變量自動+1,使用關鍵字reverse自動-1 ,跟在in reverse 後面的數字必須是從小到大的順序,而且必須是整數,不能是變量或者表達式,可以使用exit 退出循環

            begin
              for c in 1..100 loop
                SYS.DBMS_OUTPUT.PUT_LINE(c);
              end loop;
            end;

    標號與goto:無條件的跳到指定的標號去的意思

        實例 :打印1到100 ,當打印到50的時候,打印結束循環,然後結束整個循環

            declare
               v_i number(3):=1;
            begin
               while v_i<=100 loop
                  if v_i=50
                 then goto label;
                  end if;
                 SYS.DBMS_OUTPUT.PUT_LINE(v_i);
                 v_i := v_i+1;
               end loop;
             <<label>>
            SYS.DBMS_OUTPUT.PUT_LINE(‘結束循環‘);
            end;

    遊標的使用:類似java中的叠代器Iterator,遊標是一個指向上下文的句柄或指針,通過遊標,可以處理多行記錄

          技術分享

     1. 顯示遊標處理

        顯示遊標處理四步驟

          1. 定義遊標:cursor --is -- 在指定數據類型時,不能使用長度約束

          2. 打開遊標:open -- : 程序不能用open語句重復打開一個遊標

          3. 提取遊標:fetch--into--

          4. 關閉遊標: close--

       遊標實例:打印出80號部門的所有員工的信息
           declare
               --記錄類型
               type emp_mas is record(
                 v_empid employees.employee_id%type,
                 v_name employees.last_name%type,
                 v_sal employees.salary%type
                 );
                --記錄類型對象
               emp_mas_record emp_mas;
                 --定義遊標
                 cursor emp_ens_mas is select employee_id,last_name,salary from employees where department_id=80;
            begin
               --打開遊標
               open emp_ens_mas;
               --提取遊標
               fetch emp_ens_mas into emp_mas_record;
               --emp_ens_mas%found 相當於java中的hashNext
                 while emp_ens_mas%found loop
                   SYS.DBMS_OUTPUT.PUT_LINE(emp_mas_record.v_empid||‘,‘||emp_mas_record.v_name||‘,‘||emp_mas_record.v_sal);
                   fetch emp_ens_mas into emp_mas_record;
                 end loop;
                 --關閉遊標
                close emp_ens_mas;
              end;

      2. 遊標的for循環:pl/sql提供了遊標for循環,自動執行遊標的open,fetch,close語句和循環語句的功能,當進入循環時,遊標for循環語句自動打開遊標,並提取

                第一行遊標數據,當程序處理完當前所提取的數據而進入下一次循環時,遊標for循環語句會東子提取下一行數據供程序處理,當提取完結果

                集中的所有數據行後結束循環,並自動遊標

          格式:for 變量 in 遊標 loop -----end loop;

        與上題一樣,打印出80號部門的所有員工的信息

            declare
              --定義遊標
              cursor emp_ens_mas is select employee_id,last_name,salary from employees where department_id=80;
            begin
                for c in emp_ens_mas loop
                   SYS.DBMS_OUTPUT.PUT_LINE(c.employee_id||‘,‘||c.last_name||‘,‘||c.salary);
                end loop;
            end;

      3. 異常的捕獲與處理

        1. 預定義異常,就是已經系統定義好的一些異常,這些異常由系統自動拋出,如下

            技術分享

              技術分享

            declare
              v_i number(30);
            begin
               select salary into v_i from employees where employee_id >=100;
               SYS.DBMS_OUTPUT.PUT_LINE(v_i);
            end;

                由於employee_id >= 100 的員工的工資返回的結果不止一個 ,所以這裏就會出現 返回值太多的一場

                技術分享

            此時就可以在exception中捕獲此異常並進行處理,如果不處理的話,系統報錯並且程序整體終止

              declare
                v_i number(30);
              begin
                select salary into v_i from employees where employee_id >=100;
                 SYS.DBMS_OUTPUT.PUT_LINE(v_i);
              exception
                 when Too_many_rows then SYS.DBMS_OUTPUT.PUT_LINE(‘返回值太多了!!‘);

                 when others then SYS.DBMS_OUTPUT.PUT_LINE(‘其他錯誤!!‘);
              end;

              上面捕獲的是系統預定義異常Too_many_rows ,如果產生其他不知道的異常可以使用others 進行捕獲並處理

        2. 非預定義異常的處理

            對於非預定異常的處理,首先必須對非定義的oracle錯誤進行定義,步驟

              1. 在pl/sql塊的定義部分定義異常情況:<異常情況>exception;

              2. 將其定義好的異常情況,與標準的oracle錯誤連接起來,使用 pragma exception_init 語句:pragma exception_init(<異常情況>,<異常代碼>);

              3. 在pl/sql塊的異常情況處理部分對異常情況做出相應的處理

          我們來刪除employee_id = 100的用戶

            declare
            begin
              delete from employees where employee_id =100;
            end;

          這時候由於100號員工有子記錄,employee_id 等於 本表的manager_id,所以刪除不了

          技術分享

          上面爆出來的錯誤代碼 2292 沒有在oracle中的預定義異常,我們這時候就只能自己定義錯誤名與此錯誤號相關聯

            declare
              my_exception exception;
               pragma exception_init(my_exception,-2292);
            begin
               delete from employees where employee_id =100;
            exception
               when my_exception then SYS.DBMS_OUTPUT.PUT_LINE(‘違反約束 非預定義異常!!‘);
            end;

            技術分享

       3. 用戶自定義的異常處理

          用戶自定義異常是通過顯示使用 raise 語句來觸發的,當引發一個異常錯誤的時候,控制就轉向到exception塊異常錯誤部分

            對於這類異常情況的處理步驟如下

              1. 在pl/sql塊的定義部分定義異常情況 <異常情況>exception;

              2. raise <異常情況>

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

          實例:查詢employee_id 為100 號員工的工資,如果工資>1w則拋出異常“工資高”

            declare
               my_exception exception;
              v_i number(5) ;
            begin
               select salary into v_i from employees where employee_id =100;
                if v_i>10000 then
                  raise my_exception; --出發自定義異常
                 end if;
            exception
              when my_exception then SYS.DBMS_OUTPUT.PUT_LINE(‘工資高!!‘);
            end;

            技術分享

存儲函數與存儲過程

    Oracle 提供可以把pl/sql程序存儲在數據庫中,並可以在任何地方來運行他,這樣就叫存儲過程或函數

      過程和函數的唯一區別是函數總向調用者返回數據,而過程不返回

  創建一個函數: 創建函數時如果重名直接覆蓋創建

      1. 建立內嵌函數

          語法: create or replace function 函數名 (id number ,name varchar2)

              return number

              is --需要使用的變量遊標等可以在這裏定義

              begin --函數體

              exception --異常接受處理

              end;

          實例,寫一個可以返回helloworld 的函數(無參函數)

            create or replace function get_helloWorld
            return varchar2
            is
            begin
              return ‘Hello World‘;
            end;

            函數創建完成調用此函數:1. select GET_HELLOWORLD from dual;

                        2. begin
                           SYS.DBMS_OUTPUT.PUT_LINE(GET_HELLOWORLD);
                         end;

            (有參函數):create or replace function get_helloWorld(name varchar2) --不需要指定長度
                    return varchar2
                    is
                    begin
                    return ‘Hello World ‘ || name;
                    end;

              調用:select GET_HELLOWORLD(‘純菜鳥‘) from dual;

       2. 關於out函數:pl/sql程序可以通過out型的參數實現有多個返回值

          in參數標記表示傳遞給函數的值在該函數執行中不改變;out標記表示一個值在函數中進行計算並通過該參數傳遞給調用語句,in out 標記標識傳遞給函數的值可以變化

            並傳遞給調用語句。若省去標記,則參數隱含為in 。return 包含返回結果的數據類型

            實例:定義一個函數,獲取給定部門的工資總和 和 該部門的員工總數(定義為out類型的參數)

                要求:部門號定義為參數,工資總額定義為返回值

     創建函數:  create or replace function get_salary(empid number,empNum out number)
            return number
            is
               v_sal number(6) :=0;
               cursor my_emp_cur is select salary from employees where department_id = empid;
            begin
              empNum :=0; --參數只能在函數體中賦值,如果不對請指正
               for c in my_emp_cur loop
                v_sal := c.salary + v_sal; --工資
                empNum := empNum+1;
              end loop;
              return v_sal;
            end;

     調用函數:declare
           v_count_people_number number(3);--存儲人數的變量
         begin
          SYS.DBMS_OUTPUT.PUT_LINE( get_salary(80,v_count_people_number));
          SYS.DBMS_OUTPUT.PUT_LINE(v_count_people_number);
          end;  

        --從調用函數這就可以看到,在上面out函數中並沒有顯示返回人數,但是在調用的時候,Oracle會帶回參數並存到自己定義的變量中,此時輸出只會輸出函數返回的結果,而輸出待會的參數

    存儲過程創建:獲取給定部門的工資總和(out) ,要求:部門號和工資總額定義為參數

            create or replace procedure get_sal(empid number,sum_sal out number)
            is
             cursor my_emp_cur is select salary from employees where department_id = empid;
            begin
            sum_sal :=0;
              for c in my_emp_cur loop
                sum_sal := sum_sal+c.salary;
              end loop;
            end;

        我們發現存儲過程的語法格式與存儲函數的語法格式只是相差 過程是 procedure 無return,,而函數是function 有return

      調用:declare
          v_count_people_number number(7);
         begin
          get_sal(80,v_count_people_number);
          sys.dbms_output.put_line(v_count_people_number);
         end;

            技術分享

    觸發器:類似過程和函數,都有聲明,執行,和異常處理過程的pl/sql塊,區別與存儲過程,存儲過程是由程序調用,而觸發器是由事件觸發調用,觸發器不能接受參數,Oracle事件指的是對表或視圖的增刪改

          可以在增刪改操作前或者操作後進行觸發,可以對每個行或語句操作上進行觸發。

        觸發器的組成:

          1. 觸發事件:增刪改

          2. 觸發時間:before after

          3. 觸發器本身 :

          4. 觸發頻率:語句級(statement)觸發器 和 行級(row)觸發器:例如更改一個表的工資,如果更改一個人的觸發一次就是行級,如果整個表更改前或後觸發就是語句級

      創建觸發器的語法

          create [or replace ] trigger 名字

          before | after

          insert | update | delete [of column]

          on table

          [for each row] --行級還是語句級的,寫上的話就是行級的,不寫就是語句級的

          where ---

        在teacher 表上的 tname 上添加觸發器:當更新update tname的時候 輸出:tname被更改

              create or replace trigger tea_tname_up
              after --事件之前被觸發
              update of tname on teacher --作用在teacher 表上的tname列中,也可以直接作用在表上,去掉行就行 直接on table

                  --不寫就是語句級的,寫for each row 就是行級的
              begin --被觸發後做的事情
              SYS.DBMS_OUTPUT.PUT_LINE(‘tname被更改‘);
              end;

          當更新:update teacher set tname =‘純菜鳥‘ where tid=1; 時

          技術分享

      :new 和 :old修飾符:比如更改表中的數據,用這兩個就可以看到更新前和更新後的數據

        修改上面的觸發器,使其tname更改後,輸出更改前的和更改後的

          create or replace trigger tea_tname_up
          after
          update of tname on teacher
          for each row --作用與每行,使用new 和old 必須加上這個
          begin
            SYS.DBMS_OUTPUT.PUT_LINE(‘修改前的:‘||:old.tname||‘ 修改後的:‘||:new.tname);
          end;

          更改tname:update teacher set tname =‘懶蛋‘ where tid=1;

            技術分享

    實例:當刪除teacher 表中的數據的時候,吧刪除的數據備份到 teacher_bak;

        teacher中的數據

            技術分享

        teacher_bak中的數據

            技術分享

        觸發器創建:create or replace trigger teacher_two_bak
              after
              delete on teacher
              for each row
              begin
                insert into teacher_bak values (:old.tid,:old.tname);
              end;

        測試:delete from teacher where tid = 1;

       執行完後,teacher 與 teacher_bak 中的數據分別是

            技術分享......技術分享


Oracle-4 - :超級適合初學者的入門級筆記:plsql,基本語法,記錄類型,循環,遊標,異常處理,存儲過程,存儲函數,觸發器