Oracle Database-PL/SQL

Oracle Database-PL/SQL

 

“Oracle Database logo”的图片搜索结果

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);
        }
    }

輸出以下java

調用使用光標做爲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時同時關閉了這個光標
        }
    }

觸發器sql

    數據庫觸發器是一個 與表相關聯的存儲的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

相關文章
相關標籤/搜索