1. 程式人生 > >Oracle Database-PL/SQL

Oracle Database-PL/SQL

rom tput ati EDA mit inpu 工具 inf fin

Oracle Database-PL/SQL

技術分享圖片

PL/SQL基礎

PL/SQL(Procedure Language/SQL) PL/SQL是Oracle對SQL語言的過程化擴展,指在SQL命令語言中增加了過程處理語(如分支、循環等),使SQL語言具有過程處理能力。把SQL語言的數據操縱能力與過程語言的數據處理能力結合起來,使得 PLSQL面向過程但比過程語言簡單、高效、靈活和實用。
因為是過程化擴展,所以
  • PL/SQL程序內支持編寫SQL語言
    • SQL語言的數據操縱能力與過程語言的數據處理能力結合
  • PL/SQL是一門面向過程的語言

PL/SQL的官方文檔

技術分享圖片 技術分享圖片 如下,可以查到DBMS_OUTPUT程序包的內容 技術分享圖片
也可以在命令行中使用desc關鍵字直接查看程序包的結構 如下,這種方法相當於簡化的文檔 技術分享圖片

PL/SQL的語法

技術分享圖片

*Java的語法

PL/SQL可以完成以下Java代碼(偽)完成的功能 技術分享圖片 技術分享圖片 下面是PL/SQL實現這個功能 技術分享圖片 而PL/SQL相對JDBC實現這個更能更有效率 開發模式:SpringMVC+存儲過程(PL/SQL) Hello World 技術分享圖片 技術分享圖片 這個dbms_output是一個程序包(類似API)

declare-變量和常量的說明

技術分享圖片

註意:若沒有需要說明的變量或常量,可以沒有declare關鍵字
說明基本數據類型變量時要說明三個部分
  • 變量名
  • 數據類型
  • 數據大小
基本類型變量
與表的列的數據類型對應(意味著我們可以把表中的數據存到這些類型的變量中)
  • char
  • varchar2
  • date
  • number
  • boolean
  • long
*PL/SQL中的":="相當於Java中的"=","="相當於Java中的"=="
引用型變量
引用表中列的數據類型作為該變量的數據類型 技術分享圖片 示例: 技術分享圖片 技術分享圖片 賦值有兩種方法,":="和"into"關鍵字
記錄型變量
記錄型變量引用表中的一行作為變量(s)的數據類型,換句話說,記錄型變量可以存放表中的一行數據,可以理解成一個數組(或者集合),數組中的每一個元素代表這一行的記錄每一列 技術分享圖片 示例: 技術分享圖片 技術分享圖片

IF語句

語法
技術分享圖片 註意elsif這個寫法與Java else if不一樣 示例: 技術分享圖片
-- 判斷用戶從鍵盤輸入的數字並打印相應語句
--
接收鍵盤輸入 -- accept addr prompt ‘xxx‘ -- addr 是一個地址值,在該地址上保持了輸入的值 accept num prompt 請輸入一個數字; declare pnum number := # -- 註意:取出存在在這個地址值的值要使用‘&‘符號相當於c的指針 begin if pnum = 0 then dbms_output.put_line(you input 0); else if pnum = 1 then dbms_output.put_line(you input 1); else then dbms_output.put_line(you input other number); end if; end;
註意,這段正確的代碼無法在PL/SQL Developer和命令行中運行,但可以在Oracle SQL Developer(自行下載)中運行

循環語句

語法
技術分享圖片 示例:輸入數字1到10 技術分享圖片 技術分享圖片

Cursor-光標(遊標)

在Java語言中有集合的概念,在PL/SQL語言中也會用到多條記錄作為整體,此時我們需要用到遊標,遊標可以存儲查詢返回的多條數據 簡言之,光標/遊標類似Java中的ResultSet

基本語法

說明光標
CURSOR 光標名 [(參數名數據類型[,參數名數據類型]...)] IS SELECT 語句; 如: 技術分享圖片
使用步驟
技術分享圖片 fetch關鍵字的作用是將光標的一個值賦到變量中並將指針往後移動一位(遊標的指針默認在第一個值上) 示例: 技術分享圖片 技術分享圖片

光標的屬性

  • %isopen:光標是否已打開
  • %rowcount:影響的行數(即已取出的行數)
  • %found:當前指針的位置是否仍然有記錄
  • %notfound:當前指針位置是否沒有記錄

帶參數的光標

語法
技術分享圖片 示例: 技術分享圖片 技術分享圖片

例外

例外例外是程序設計語言提供的一種功能,用來增強程序的健壯性和容錯性 相當於Java中的異常

例外的分類

  • 系統定義的例外
    • No_data_found(沒有找到數據)
    • Too_many_rows(select …into語句匹配多個行)
    • Zero_Divide(被零除)
    • Value_error(算術或轉換錯誤)
    • Timeout_on_resource(在等待資源時發生超時)(分布式數據庫相關)
  • 用戶定義的例外

定義及處理例外

  • 在declare節中定義例外
    • out_of exception;
  • 在可行語句中引起例外
    • raise out_of;(類似Java中的throw)
  • 在exception節處理例外
    • when out_of then …;(類似Java中的try-catch)
技術分享圖片 示例: 技術分享圖片

*PL/SQL的應用

實例

技術分享圖片 技術分享圖片 技術分享圖片 技術分享圖片 技術分享圖片 技術分享圖片
declare
  --部門
  cursor cdept is select deptno from dept;
  pdeptno dept.deptno%type;
  
  --部門中員工的薪水
  cursor cemp(dno number) is select sal from emp where deptno=dno;
  psal emp.sal%type;
  
  --每個段的人數
  count1 number; count2 number; count3 number;
  --部門的工資總額
  salTotal number := 0;
begin
  --部門
  open cdept;
  loop
       --取一個部門
       fetch cdept into pdeptno;
       exit when cdept%notfound;
       
       --初始化
       count1:=0; count2:=0; count3:=0;
       --得到部門的工資總額
       select sum(sal) into salTotal  from emp where deptno=pdeptno;
       
       --取部門的中員工薪水
       open cemp(pdeptno);
       loop
            --取一個員工的薪水
            fetch cemp into psal;
            exit when cemp%notfound;
            
            --判斷
            if psal < 3000 then count1:=count1+1;
               elsif psal >=3000 and psal<6000 then count2:=count2+1;
               else count3:=count3+1;
            end if;
       end loop;
       close cemp;

       --保存結果
       insert into msg values(pdeptno,count1,count2,count3,nvl(saltotal,0));

  end loop;
  close cdept;
  
  commit;
  dbms_output.put_line(完成);
  
end;

編寫PL/SQL程序的一般步驟
  • 需求分析
    • SQL語句:即涉及操作數據庫的邏輯
      • 獲取數據庫中的數據
        • 使用什麽類型的變量來存放獲取到的數據
        • 使用什麽方式來從變量中提取數據(如循環,判斷等)
      • 修改數據庫中的數據
    • 變量
      • 變量的初始值
      • 變量的獲取方式
        • 滿足什麽條件就改變變量的值

PL/SQL進階內容

存儲過程和存儲函數

存儲過程

存儲過程(Stored Procedure)是在大型數據庫系統中,一組為了完成特定功能SQL語句集編譯後存儲在數據庫中,用戶通過指定存儲過程的名字給出參數(如果該存儲過程帶有參數)來執行它。 存儲過程是數據庫中的一個重要對象,任何一個設計良好的數據庫應用程序都應該用到存儲過程。

存儲函數

函數(Function)為一命名的存儲程序,可帶參數,並返回一計算值。函數和過程的結構類似,但必須有一個RETURN子句,用於返回函數值。函數說明要指定函數名、結果值的類型,以及參數類型等。

*

簡言之,存儲在數據庫中供所有用戶程序調用的由PL/SQL編寫的子程序被稱為存儲過程或存儲函數。 在Java程序中,不能直接調用PL/SQL編寫好的程序,但是可以調用存儲過程或存儲函數。
當然,在存儲過程或存儲函數中也可以調用存儲過程或存儲函數。

存儲過程和存儲函數的區別

一般來講,過程和函數的區別在於函數可以有一個返回值:而過程沒有返回值。但過程和函數都可以通過out指定一個或多個輸出參數。我們可以利用out參數,在過程和函數中實現返回多個值。 通常除去return(返回)值的不同,可以認為兩者相同。

使用存儲過程的語法

技術分享圖片
創建存儲過程
技術分享圖片
調用存儲過程
示例: 技術分享圖片 技術分享圖片 示例: 技術分享圖片

使用存儲函數的語法

創建存儲函數
技術分享圖片
調用存儲函數
示例 技術分享圖片 技術分享圖片

存儲過程和存儲函數中的in(輸入參數)out(輸出參數)

一般來講,過程和函數的區別在於函數可以有一個返回值,而過程沒有返回值。但過程和函數都可以通過out指定一個或多個輸出參數,我們可以利用out參數,在過程和函數中實現返回多個值。
使用out參數來返回值
技術分享圖片
在out參數中返回遊標
技術分享圖片

使用存儲過程/函數的註意事項

如果只有一個返回值,用存儲函數,否則,就用存儲過程 盡量不在存儲過程或存儲函數中操作事務,因為子程序是交由其他人調用的,不應該操作事務

*Java調用存儲過程/存儲函數

創建測試項目,導包 包的位置位於安裝Oracle的機器下的這個目錄 技術分享圖片 導入項目如下 技術分享圖片 編寫工具類
package demo;

import java.sql.*;

public class JDBCUtils {
    private static String driver = "oracle.jdbc.OracleDriver";
    private static String url = "jdbc:oracle:thin:@169.254.35.157:1521/orcl";
    private static String user = "scott";
    private static String password = "tiger";

    static{
        // 註冊驅動
        try {
            Class.forName(driver);
        } catch (ClassNotFoundException e) {
            throw new ExceptionInInitializerError(e);
        }
    }

    public static Connection getConnection() {
        try {
            return DriverManager.getConnection(url, user, password);
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return null;
    }

    public static void release(Connection conn, Statement st, ResultSet rs) {
        if (rs != null) {
            try {
                rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
            finally{
                rs = null;
                // 置空作為垃圾回收
            }
        }
        if (st != null) {
            try {
                st.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
            finally{
                st = null;
            }
        }
        if (conn != null) {
            try {
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
            finally{
                conn = null;
            }
        }
    }
}
Statement接口的子接口CallableStatement就是專門用於執行SQL存儲過程的接口 技術分享圖片 其中紅框內是調用存儲過程的標準方式 編寫測試類分別測試存儲過程和存儲函數

存儲過程

存儲過程如下 技術分享圖片 測試代碼如下
 /*
    procedure queryEmpInformation(eno in number,
                                  pename out varchar2,
                                  psal   out number,
                                  pjob   out varchar2)
                                  */
    @Test
    public void testProcedure() {
        String sql = "{call queryEmpInformation(?,?,?,?)}";
        Connection conn = null;
        CallableStatement call = null;
        try {
            conn = JDBCUtils.getConnection();
            call = conn.prepareCall(sql);

            // 對於in參數,需要對其賦值
            call.setInt(1, 7839);

            // 對於out參數,需要對其聲明
            call.registerOutParameter(2, OracleTypes.VARCHAR);
            call.registerOutParameter(3, OracleTypes.NUMBER);
            call.registerOutParameter(4, OracleTypes.VARCHAR);

            // 執行調用存儲過程
            call.execute();

            // 從CallableStatement對象中取出輸出out參數
            String name = call.getString(2);
            Double sal = call.getDouble(3);
            String job = call.getString(4);

            System.out.println("name:" + name + " sal:" + sal + " job:" + job);

        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            JDBCUtils.release(conn, call, null);
        }

    }
輸出如下 技術分享圖片

存儲函數

存儲過程如下 技術分享圖片 測試代碼如下
    /*
    function queryEmpIncome(eno in number)
    return number
    */
    @Test
    public void testFunction() {
        String sql = "{?= call queryEmpIncome(?)}";
        Connection conn = null;
        CallableStatement call = null;
        try {
            conn = JDBCUtils.getConnection();
            call = conn.prepareCall(sql);

            // 存儲函數的輸出參數即返回值,是第一個問號,對其註冊
            call.registerOutParameter(1, OracleTypes.NUMBER);

            // 賦值輸入參數
            call.setInt(2, 7839);

            call.execute();

            double annIncome = call.getDouble(1);

            System.out.println("annual income:" + annIncome);

        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            JDBCUtils.release(conn, call, null);
        }
    }

輸出如下

技術分享圖片

調用使用光標作為out參數的存儲過程

問題:查詢某個部門所有員工的所有信息 在編寫PL/SQL時申明包結構 創建包 在包中可以自定義一個數據類型(下面的empcursor),引用原有的數據類型(下面引用了cursor光標類型)作為這個類型 技術分享圖片 創建包體 在包體中需要實現包頭中定義的所有存儲過程和存儲函數 技術分享圖片
在Java中訪問
使用ResultSet來接受光標參數 技術分享圖片 具體測試代碼如下
    @Test
    public void testCursor() {
        String sql = "{call mypackage.queryemplist(?,?)}";
        Connection conn = null;
        CallableStatement call = null;
        try {
            conn = JDBCUtils.getConnection();
            call = conn.prepareCall(sql);

            call.setInt(1, 10);

            call.registerOutParameter(2, OracleTypes.CURSOR);

            call.execute();

            // CallableStatement是一個通用的接口,獲取時獲得Oracle適用的實現類對象,可以將其強制轉換成Oracle適用的接口
            ResultSet cursor = ((OracleCallableStatement) call).getCursor(2);

            while (cursor.next()) {
                System.out.println("name:"+cursor.getString("ename"));
                System.out.println("hiredate:"+cursor.getDate("hiredate"));
            }


        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            JDBCUtils.release(conn, call, cursor);
            // 此處關閉ResultSet時同時關閉了這個光標
        }
    }

觸發器

數據庫觸發器是一個與表相關聯的存儲的PL/SQL程序。每當一個特定的數據操作語句(Insert,update和delete不包括select)在指定的表上發出時,Oracle自動地執行觸發器中定義的語句序列。

觸發器的作用

  • 數據確認
    • 如:員工漲工資後的工資不能少於漲工資後的工資
  • 實施復雜的安全性檢查
    • 如:禁止在非工作時間插入新員工
  • 審計,跟蹤表上的數據操作(日誌)
    • *Oracle中的審計有五種
      • 強制審計
      • 標準審計(配置)
      • 基於值的審計(觸發器審計屬於這一類
      • 細粒度審計
      • 管理員審計
  • 數據的備份和同步

觸發器的類型

  • 語句級觸發器
    • 指定的操作語句操作之前或之後執行一次,不管這條語句影響了多少行
      • 如:一次插入多條數據,觸發器只執行一次
    • 針對的是表
  • 行級觸發器
    • 觸發語句作用的每一條記錄都被觸發
    • 在行級觸發器中使用:old:new偽記錄變量,識別值的狀態
    • 針對的是行

創建觸發器

語法
技術分享圖片 示例: 限制非工作時間向數據庫插入數據 技術分享圖片 當非工作時間插入員工執行時會報錯 技術分享圖片 示例2: 確認數據(檢查emp表中sal 的修改值不低於原值) 技術分享圖片 運行效果 技術分享圖片
查詢觸發器、過程及函數
  • select * from user_triggers;
  • select * from user_source;

行級觸發器中觸發語句與偽記錄變量代表的值

技術分享圖片

觸發器的實際應用

技術分享圖片 技術分享圖片 技術分享圖片S

*使用PL/SQL Developer編寫PL/SQL程序

Hello World

技術分享圖片 技術分享圖片

Oracle Database-PL/SQL