1. 程式人生 > >Oracle PL/SQL進階

Oracle PL/SQL進階

Oracle PL/SQL進階

控制結構

在任何計算機語言(c,java,c#,c++)都有各種控制語句(條件語句,迴圈語句,順序控制結構..)在pl/sql中也存在這樣的控制結構。

條件分支語句

pl/sql中提供了三種條件分支語句

if--then

if--then--else

if--then--elsif--elsif--else

簡單的條件判斷if--then

基本語法:

if 條件表示式 then

執行語句...;

end if;


--控制結構
--條件語句
--編寫過程,輸入員工編號,如果員工工資低於2000,則給員工工資增加10%
create or replace procedure p10(v_in_empno in emp2.empno%type) is
v_sal emp2.sal%type;
begin
  select sal into v_sal from emp2 where empno = v_in_empno;
  if v_sal < 2000 then
    update emp2 set sal = sal * 1.1 where empno = v_in_empno;
  end if;
end;
/

二重條件分支if--then--else

基本語法:

if 條件表示式 then

執行語句;

else

執行語句;

end if;

--編寫過程,輸入員工編號,如果員工的補助不是0則在原來基礎上加上100,如果補助為0則設補助為200
create or replace procedure p11(v_in_empno in emp2.empno%type) is
v_comm emp2.comm%type;
begin
  select comm into v_comm from emp2 where empno = v_in_empno;
  if v_comm != 0 then
    v_comm := v_comm + 100;
  else
    v_comm := 200;
  end if;
  update emp2 set comm = v_comm where empno = v_in_empno;
end;
/

多重條件分支if--then--elsif--else

基本語法:

if 條件表示式 then

執行語句;

elsif 條件表示式 then

執行語句;

else

執行語句;

end if;


--編寫過程,輸入員工編號,如果職位是PRESIDENT則工資加1000,如果是MANAGER則加500,其他職位加200
create or replace procedure p12(v_in_empno in emp2.empno%type) is
v_sal emp2.sal%type;
v_job emp2.job%type;
begin
  select sal,job into v_sal,v_job from emp2 where empno = v_in_empno;
  if v_job = 'PRESIDENT' then
    v_sal := v_sal + 1000;
  elsif v_job = 'MANAGER' then
    v_sal := v_sal + 500;
                                                                                                          
    v_sal := v_sal + 200;
  end if;
  update emp2 set sal = v_sal where empno = v_in_empno;
end;
/


迴圈結構

loop迴圈

是pl/sql中最簡單的迴圈語句,這種迴圈語句以loop開頭,以end loop結尾,這種迴圈至少會被執行一次。

基本語法:

loop

執行語句;

exit when 條件表示式;

end loop;

--迴圈語句
--編寫過程,輸入使用者名稱和新增使用者的個數n,迴圈新增n個使用者到users表中,從1到n
create or replace procedure p13(v_in_username in users.username%type,v_in_number in number) is
v_userid number := 1;
begin
  loop
    exit when v_userid > v_in_number or v_in_number <= 0;
    insert into users values(v_userid,v_in_username);
    v_userid := v_userid + 1;
  end loop;
end;
/

while迴圈

loop基本迴圈至少要執行迴圈體一次,而對於while迴圈來說,只有條件為true時,才會執行迴圈體語句,while迴圈以while..loop開始,以end loop結束。

基本語法:

while 條件表示式 loop

執行語句;

end loop;

--編寫過程,輸入使用者名稱和新增使用者的個數n,迴圈新增n個使用者到users表中,從1到n
create or replace procedure p14(v_in_username in users.username%type,v_in_number in number) is
v_userid number := 1;
begin
  while v_userid <= v_in_number and v_in_number > 0 loop
    insert into users values(v_userid,v_in_username);
    v_userid := v_userid + 1;
  end loop;
end;
/

說明:

1、在is--begin之間只能定義變數型別同時初始化賦值,或定義變數型別後在begin內進行賦值,不能在is--begin之間定義變數型別之後再對變數賦值。

2、傳入的引數變數不能在儲存過程中再次賦值。

for迴圈

基本for迴圈的基本結構如下:

begin

for i in [reverse]起始值..終止值 loop

執行語句;

end loop;

end;


--編寫過程,輸入使用者名稱和新增使用者的個數n,迴圈新增n個使用者到users表中,從1到n
create or replace procedure p15(v_in_username in users.username%type,v_in_number in number) is
begin
  for i in 1..v_in_number loop
    insert into users values(i,v_in_username);
  end loop;
end;
/

注意:推薦使用loop迴圈或者是while迴圈,不推薦使用for迴圈。


goto,null

1、goto語句

goto語句用於跳轉到特定標號去執行語句,注意由於使用goto語句會增加程式的複雜性,並使得應用程式可讀性變差,所以一般不使用goto語句。

    基本語法如下:goto lable,其中lable是已定義好的標號名。

基本語法:goto 標號;

標號定義:<<標號>>


例:

--輸出1至12 迴圈結束。
declare
i number:=1;
begin
<<start_loop>>
	loop
		dbms_output.put_line('輸出i='||i);
		if i=12 then
			goto end_loop;
		end if;
		i:=i+1;
		if i=10 then
			goto start_loop;
		end if;
	end loop;
<<end_loop>>
	dbms_output.put_line('迴圈結束');
end;

2、null

null語句不會執行任何操作,並且會直接將控制傳遞到下一條語句。使用null語句的主要好處是可以提高pl/sql的可讀性。


例:

declare
v_sal emp.sal%type;
v_ename emp.ename%type;
begin
	select ename,sal into v_ename,v_sal from emp where empno=&no;
	if v_sal<3000 then
		update emp set comm=sal*0.1 where ename=v_ename;
	else
		null;
	end if;
end;


儲存過程實際運用——編寫分頁過程

PS:分頁過程主要運用的是過程中的返回值實現的,而返回值概念比較抽象,故一步一步瞭解返回值,從無返回值->有返回值的非列表形式->有返回值的列表形式->有多個返回值的列表非列表組合形式(實現分頁功能)


無返回值的儲存過程

例項:

--編寫過程,以輸入的員工號,顯示員工的姓名、工資、個人所得稅(稅率為0.03)
create or replace procedure p5(v_in_empno in number) is
v_ename varchar2(30);
v_sal number;
v_tax_rate number(3,2) := 0.03;
v_tax number;
begin
  select ename,sal into v_ename,v_sal from emp2 where empno = v_in_empno;
  v_tax := v_sal * v_tax_rate;
  dbms_output.put_line('員工的姓名為:' || v_ename || ' 員工的工資為:' || v_sal || ' 員工的個人所得稅為: ' || v_tax);
end;
/

有返回值的儲存過程(非列表,只有一個返回值)

建立有返回值的儲存過程基本語法:

create or replace procedure 過程名(引數名 in 型別,..,引數名 out 型別,..) is

定義變數..;

begin

執行語句..;

exception

when 錯誤提示 then

處理或提示語句;

end;


例項:

--編寫過程,輸入員工編號,返回員工姓名
--out表示輸出型別
create or replace procedure p16(v_in_empno in emp2.empno%type,v_out_ename out emp2.ename%type) is
begin
  select ename into v_out_ename from emp where empno = v_in_empno;
end;
/

/**
 * 呼叫返回非列表的值
 */
package com.oracle.db;
import java.sql.*;
public class db8 {
	public static void main(String[] args) {
		Connection ct = null;
		CallableStatement cs = null;
		ResultSet rs = null;

		try {
			// 載入驅動
			Class.forName("oracle.jdbc.driver.OracleDriver");
			// 得到連線
			ct = DriverManager.getConnection(
					"jdbc:oracle:thin:@127.0.0.1:1521:Switch", "scott",
					"123456");
			// 建立CallableStatement
			cs = ct.prepareCall("{call p16(?,?)}");
			// 給?賦值
			cs.setString(1, "7839");
			cs.registerOutParameter(2, oracle.jdbc.OracleTypes.VARCHAR);

			// 執行
			cs.execute();

			// 取出輸出值
			String ename = cs.getString(2);
			System.out.println("名字是:" + ename);
		} catch (Exception e) {
			// TODO: handle exception
			e.printStackTrace();
		}
	}
}

說明:

1、對於過程的輸入值,使用set方法,對於輸出值使用registerOutParameter來註冊接收返回值。問號的順序要對應,同時考慮型別。

2、取出過程返回值的方法是CallableStatement提供的get方法(輸出引數的位置);同時要考慮輸出的引數型別。


有返回值的儲存過程(列表[結果集])

PS:列表,結果集是用遊標來實現的,但是遊標型別是不能直接使用的,必須要定義。這時候就能將遊標定義在包裡面,然後呼叫包中的遊標來實現列表,結果集。

例項:

--編寫過程,輸入部門號,返回該部門所有員工資訊
--1.宣告包,包中定義一個遊標型別
create or replace package pack2 is
--定義一個遊標型別
type my_cursor is ref cursor;
end;
/

--2.編寫過程
create or replace procedure p17(v_in_deptno in emp2.deptno%type,v_out_result out pack2.my_cursor) is
begin
  open v_out_result for select * from emp where deptno = v_in_deptno;
end;
/


/**
 * 3.呼叫列表值(集合)
 */
package com.oracle.db;
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
public class db9 {
	public static void main(String[] args) {
		Connection ct = null;
		CallableStatement cs = null;
		ResultSet rs = null;
		try {
			// 載入驅動
			Class.forName("oracle.jdbc.driver.OracleDriver");
			// 得到連線
			ct = DriverManager.getConnection(
					"jdbc:oracle:thin:@127.0.0.1:1521:Switch", "scott",
					"123456");
			// 建立CallableStatement
			cs = ct.prepareCall("{call p17(?,?)}");
			// 給?賦值
			cs.setString(1, "10");
			// 註冊第二個?
			cs.registerOutParameter(2, oracle.jdbc.OracleTypes.CURSOR);
			// 執行
			cs.execute();

			// 取出集合
			rs = (ResultSet) cs.getObject(2);
			// 迴圈取值
			while (rs.next()) {
				String ename = rs.getString("ename");
				int empno = rs.getInt("empno");
				System.out.println("員工號是:" + empno + " 員工名是:" + ename);
			}
		} catch (Exception e) {
			// TODO: handle exception
			e.printStackTrace();
		} finally {
			try {
				// 關閉資源
				if (rs != null) {
					rs.close();
				}
				if (cs != null) {
					cs.close();
				}
				if (ct != null) {
					ct.close();
				}
			} catch (Exception e2) {
				e2.printStackTrace();
			}
		}
	}
}


--編寫過程,輸入員工編號,返回該員工的姓名、工資和崗位
--1.宣告包,定義一個遊標型別,可以參考上一個案例,這裡不在定義
--2.編寫過程
create or replace procedure p18(v_in_empno in emp2.empno%type,v_out_result out pack2.my_cursor) is
begin
  open v_out_result for select ename,sal,job from emp2 where empno = v_in_empno;
end;
/

/**
 * 3.從儲存過程中接收遊標(集合)
 */
package com.oracle.db;
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
public class d10 {

	public static void main(String[] args) {
		Connection ct = null;
		CallableStatement cs = null;
		ResultSet rs = null;
		try {
			// 1.載入驅動
			Class.forName("oracle.jdbc.driver.OracleDriver");
			// 2.獲取連線
			ct = DriverManager.getConnection(
					"jdbc:oracle:thin:@127.0.0.1:1521:Switch", "scott",
					"123456");
			// 3.建立CallableStatement
			cs = ct.prepareCall("{call p18(?,?)}");
			// 4.給?賦值
			cs.setString(1, "7839");
			// 5.註冊返回型別引數
			cs.registerOutParameter(2, oracle.jdbc.OracleTypes.CURSOR);
			// 6.執行
			cs.execute();
			// 7.從遊標中獲取集合
			rs = (ResultSet) cs.getObject(2);
			if (rs.next()) {
				String ename = rs.getString("ename");
				int sal = rs.getInt("sal");
				String job = rs.getString("job");
				System.out.println("員工姓名是:" + ename + " 員工的薪水是:" + sal
						+ " 員工的崗位是:" + job);
			}
		} catch (Exception e) {
			e.printStackTrace();
			// 丟擲執行異常
			throw new RuntimeException(e.getMessage());
		} finally {
			// 關閉資源
			try {
				if (rs != null) {
					rs.close();
				}
				if (cs != null) {
					cs.close();
				}
				if (ct != null) {
					ct.close();
				}
			} catch (Exception e2) {
				// TODO: handle exception
				e2.printStackTrace();
			}
		}
	}
}


有多個返回值的列表非列表組合形式(實現分頁功能)

把一個字串,當做sql語句執行,並把查詢得到的結果賦給某個變數,語法如下:

execute immediate v_sql into myrows;

基本語法

execute immediate 變數(sql接語句) into 出變數;

--分頁過程
--編寫過程,輸入表名、每頁顯示記錄數、當前頁、排序欄位(deptno降序)。返回總記錄數,總頁數和返回的結果集
--1.宣告一個包含遊標型別的包
create or replace package pack3 is
       type my_cursor is ref cursor;
end;
/

--2.編寫過程
create or replace procedure p19
(v_in_table in varchar2,v_in_pagesize in number,v_in_currpage in number,v_in_order in varchar2
,v_out_totalrecords out number,v_out_totalpage out number,v_out_results out pack3.my_cursor) is
--定義sql拼接塊
v_sql varchar(2000);
--頁開始
v_pagebegin number;
--頁結束
v_pageend number;

begin
  --計算頁開始數
  v_pagebegin := v_in_pagesize * (v_in_currpage - 1) + 1;
  --計算頁結束數
  v_pageend := v_in_pagesize * v_in_currpage;
  --查詢分頁結果並放入遊標
  --sql語句需要拼接
  v_sql := 'select t2.* from (select t1.*,rownum rn from (select * from ' || v_in_table ||' order by '
  || v_in_order || ') t1 where rownum <= ' || v_pageend || ') t2 where rn >= ' || v_pagebegin;
  --開啟遊標,指向結果集
  open v_out_results for v_sql;
  --查詢總記錄數
  --能力有限,並沒有實現按輸入的表名來計算總頁數,想法是通過拼接之後再執行SQL語句獲取結果。
  select count(*) into v_out_totalrecords from emp2;
  --計算總頁數
  if mod(v_out_totalrecords,v_in_pagesize) = 0 then
    v_out_totalpage := v_out_totalrecords / v_in_pagesize;
  else
    v_out_totalpage := (v_out_totalrecords / v_in_pagesize) + 1;
  end if;
end;
/

/**
 * 呼叫分頁儲存過程
 */
package com.oracle.db;
import java.sql.*;
public class db11 {
	public static void main(String[] args) {
		Connection ct = null;
		CallableStatement cs = null;
		ResultSet rs = null;
		try {
			// 1.載入驅動
			Class.forName("oracle.jdbc.driver.OracleDriver");
			// 2.獲取連線
			ct = DriverManager.getConnection(
					"jdbc:oracle:thin:@127.0.0.1:1521:Switch", "scott",
					"123456");
			// 3.建立CallableStatement
			// p19過程引數
			// v_in_table varchar2
			// v_in_pagesize number
			// v_in_currpage number
			// v_in_order varchar2
			// v_out_totalrecords number
			// v_out_totalpage number
			// v_out_results pack3.my_cursor
			cs = ct.prepareCall("{call p19(?,?,?,?,?,?,?)}");
			// 4.f給?賦值
			// 設定表名
			cs.setString(1, "emp2");
			// 設定每頁顯示記錄數
			cs.setInt(2, 6);
			// 設定當前頁
			cs.setInt(3, 3);
			// 設定排序列
			cs.setString(4, "empno");
			// 註冊返回型別引數
			// 註冊總記錄數
			cs.registerOutParameter(5, oracle.jdbc.OracleTypes.INTEGER);
			// 註冊總頁數
			cs.registerOutParameter(6, oracle.jdbc.OracleTypes.INTEGER);
			// 註冊返回結果集
			cs.registerOutParameter(7, oracle.jdbc.OracleTypes.CURSOR);
			// 5.執行
			cs.execute();
			// 6.從返回引數獲取相應值
			int totalRecords = cs.getInt(5);
			int totalPage = cs.getInt(6);
			rs = (ResultSet) cs.getObject(7);
			// 迴圈輸出
			while (rs.next()) {
				String ename = rs.getString("ename");
				int empno = rs.getInt("empno");
				System.out.println("員工的編號是:" + empno + " 員工的姓名是:" + ename);
			}
			System.out.println("總記錄數為:" + totalRecords);
			System.out.println("總頁數為:" + totalPage);
		} catch (Exception e) {
			// TODO: handle exception
			e.printStackTrace();
			// 丟擲執行異常
			throw new RuntimeException(e.getMessage());
		} finally {
			// 關閉資源
			try {
				if (rs != null) {
					rs.close();
				}
				if (cs != null) {
					cs.close();
				}
				if (ct != null) {
					ct.close();
				}
			} catch (Exception e2) {
				// TODO: handle exception
				e2.printStackTrace();
			}
		}
	}
}


DividePage.jsp

<%@ page language="java" import="java.util.*,java.sql.*" pageEncoding="UTF-8"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <base href="<%=basePath%>">
    <title>My JSP 'DividePage.jsp' starting page</title>
  </head>
  <body>
    <h2>oracle分頁案例</h2>
	<br>
	<table>
	<tr>
		<td>使用者名稱</td>
		<td>薪水</td>
	</tr>
    <%
    	//接收pageCurr
		String sPageCurr = request.getParameter("pageCurr");
		int pageCurr = 1;
		if(sPageCurr != null){
			pageCurr = Integer.parseInt(sPageCurr);
		}
    	Connection ct = null;
		CallableStatement cs = null;
		ResultSet rs = null;
		try {
			// 1.載入驅動
			Class.forName("oracle.jdbc.driver.OracleDriver");
			// 2.獲取連線
			ct = DriverManager.getConnection(
					"jdbc:oracle:thin:@127.0.0.1:1521:Switch", "scott",
					"123456");
			// 3.建立CallableStatement
			// p19過程引數
			// v_in_table varchar2
			// v_in_pagesize number
			// v_in_currpage number
			// v_in_order varchar2
			// v_out_totalrecords number
			// v_out_totalpage number
			// v_out_results pack3.my_cursor
			cs = ct.prepareCall("{call p19(?,?,?,?,?,?,?)}");
			// 4.f給?賦值
			// 設定表名
			cs.setString(1, "emp2");
			// 設定每頁顯示記錄數
			cs.setInt(2, 4);
			// 設定當前頁
			cs.setInt(3, pageCurr);
			// 設定排序列
			cs.setString(4, "empno");
			// 註冊返回型別引數
			// 註冊總記錄數
			cs.registerOutParameter(5, oracle.jdbc.OracleTypes.INTEGER);
			// 註冊總頁數
			cs.registerOutParameter(6, oracle.jdbc.OracleTypes.INTEGER);
			// 註冊返回結果集
			cs.registerOutParameter(7, oracle.jdbc.OracleTypes.CURSOR);
			// 5.執行
			cs.execute();
			// 6.從返回引數獲取相應值
			int totalRecords = cs.getInt(5);
			int totalPage = cs.getInt(6);
			rs = (ResultSet) cs.getObject(7);
			// 迴圈輸出
			while (rs.next()) {
				out.println("<tr>");
				out.println("<td>" + rs.getString("ename") + "</td>");
				out.println("<td>" + rs.getString("sal") + "</td>");
				out.println("</tr>");
			}
			out.println("</table>");
			out.println("總記錄數為:" + totalRecords);
			out.println("總頁數為:" + totalPage);
			out.println("當前頁為:" + pageCurr);
			//列印總頁數
			for(int i = 1; i <= totalPage; i++){
				out.print("<a href=DividePage.jsp?pageCurr="+i+ "> [" + i +  "] </a>");
			}
			
		} catch (Exception e) {
			// TODO: handle exception
			e.printStackTrace();
			// 丟擲執行異常
			throw new RuntimeException(e.getMessage());
		} finally {
			// 關閉資源
			try {
				if (rs != null) {
					rs.close();
				}
				if (cs != null) {
					cs.close();
				}
				if (ct != null) {
					ct.close();
				}
			} catch (Exception e2) {
				// TODO: handle exception
				e2.printStackTrace();
			}
		}
     %>
  </body>
</html>

----------參考《韓順平玩轉Oracle》