1. 程式人生 > >Oracle教程第五篇

Oracle教程第五篇

一、遊標

1.簡介

oracle 讀取資料的工具,類似java集合。

2.示例

DECLART 
	-- 獲取班級集合
	CURSOR  EMPLIST  IS SELECT * FROM EMP ;
	-- 定義emp 表的物件 進行解析
	V_EMPOBJ  EMP%ROWTYPE;
BEGIN
	-- 解析遊標(集合)
	-- 1. 開啟遊標。
	OPEN  EMPLIST;
	LOOP
		EXIT WHEN EMPLIST%NOTFOUND
		FETCH EMPLIST  INTO V_EMPOBJ;
		dbms_output.put_line(v_empobj.empno||v_empobj.ename);
	END LOOP;
	-- 2. 關閉遊標
	CLOSE  EMPLIST;
END 

3.簡化版

DECLARE 
	CURSOR  GLIST  IS SELECT * FROM GRADE;
BEGIN
	FOR g IN GLIST  LOOP
	dbms_output.put_line(g.gid);
END  LOOP;
END

二、事務

1.一個程式是否健壯,主要看異常處理機制

2.異常

  1. oracle 一共定義有2萬個錯誤。
  2. 預定義錯誤 彈窗 終止程式執行。
  3. 處理異常。
EXCEPTION  
WHEN
	TOO_MANY_ROWS
THEN 
	dbms_output.put_line('返回條數過多,不能賦值');
WHEN
	NO_DATA_FOUND
THEN
	dbms_output.put_line('要查詢的資料不存在');
  1. 自定義異常。
-- 如果有員工工資小於1000 丟擲異常提示。
DECLARE 
	CURSOR  emplist  IS SELECT  *  FROM  EMP ;
	-- 1. 定義異常
	SAL_TOO_LOW   EXCEPTION;
BEGIN
	FOR  e  IN  emplist  LOOP
		IF   e.sal  <  1000  THEN
			-- 2. 引發異常
			RAISE   SAL_TOO_LOW;
		END  IF;
	END LOOP;
-- 3. 接收並處理異常
	EXCEPTION
	WHEN  SAL_TOO_LOW  THEN
		dbms_output.put_line('有員工工資太低');
		raise_application_error(-20001,'工資過低,請及時漲工資');
		//錯誤資訊標號 必須小於 -20000
END
  1. 其他異常
    所有未攔截的異常都應該被others接收
WHEN  OTHERS   THEN
	dbms_output.put_line('所有未攔截的異常');

空白R攔截順序

先是預定義    再是自定義     最後是others

三、儲存過程

儲存過程是一個有名字的 plsql 塊

1. 示例

//-->根據員工編號給指定員工漲工資,並列印相關資訊。

create  or  replace  procedure  updateempbyempno(
	eno    emp.empno%type  //沒有;
) as 
	--宣告變數的地方
	a_sal     emp.sal%type ;  --漲工資之前
	b_sal     emp.sal%type;--漲工資之後
begin
	select sal  into a_sal  from emp  where empno = eno;
	dbms_output.put_line('漲工資之前為'||a_sal);
	--漲工資
	update emp  set sal=sal+100  where empno = eno;
	commit;
	--查詢漲了之後的工資
	select sal into b_sal from emp where empno = eno;
	dbms_output.put_line('漲工資之後為' || b_sal);
end;

	-- 呼叫儲存過程
begin
	updateempbyempno(7521);
end;

四、儲存函式

1.示例

根據員工編號查詢員工的年薪。
create  or  replace  function FINDSALBYEMPNO(eno  EMP.SAL%TYPE)
	RETURN    NUMBER      // eno不能和欄位名一致
AS
	v_nianxin   number(11,2);
BEGIN
	-- 查詢年薪
	select  sal*12+nvl(comm,0) into  v_nianxin from  emp  where empno=eno;
	return v_nianxin;
EXCEPTION
	WHEN  NO_DATA_FOUND  THEN
		dbms_output.put_line('沒有找到資料');
	WHEN  TOO_MANY_ROWS  THEN 
		dbms_output.put_line('輸出資料過多');
END;

-- 呼叫儲存函式
DECLARE
	--定義方法返回值的接受變數
	v_sal  number(11,2);
BEGIN
	v_sal   :=   FINDSALBYEMPNO(7521);
	dbms_output.put_line('年薪為'|| v_sal);
END

五、多返回值的儲存過程

1.示例1

根據編號返回員工名稱和年薪
create  or  replace procedure   findempinfobyempno(
	eno  in  emp.empno%type, [in 輸入模式]
	empname out  emp.ename%type,[out  輸出模式]
	nianxin  out number [不是表中的型別,不需要帶長度]
)
as

begin
	select  ename,sal*12+nvl(comm,0) into  empname,nianxin
	from emp where empno = eno;
end;

--呼叫帶out的儲存過程
DECLARE
	ename  emp.ename%type;
	nianxin   number(11,2)
BEGIN
	findempinfobyempno(7521,ename,nianxin);
	dbms_output.put_line(ename||'的年薪是:'||nianxin);
END;

2.示例2

獲取所有人的年薪。//引數為遊標型別
create   or   replace  procedure  
	findALlSal(allsal  out  sys_refcursor)as
begin
	open allsal for select sal*12+nvl(comm.0) from emp;
end;

	-- 呼叫儲存過程
   DECLARE
              --儲存遊標型別所有的年薪
              ALLSAL SYS_REFCURSOR;
              SAL EMP.SAL%TYPE;
    BEGIN
            --呼叫
              FINDALLSAL(ALLSAL);
              --迴圈
              LOOP
                  --出口
                  EXIT WHEN ALLSAL%NOTFOUND;
                  --列印
                  DBMS_OUTPUT.PUT_LINE(SAL);
                  --注入
                  FETCH ALLSAL INTO SAL;
              END LOOP;
              --結束迴圈
              CLOSE ALLSAL;
              --關閉遊標
      END;

3.總結

當儲存過程的引數型別是遊標型別時,開啟遊標時設定查詢結果進去。
因為在定義儲存過程時將遊標打開了,所以呼叫儲存過程時不能用for迴圈
而且記得關閉遊標。

六、java呼叫儲存過程

1.驅動包

2.工具類

  1. 四個屬性
    driver:oracle.jdbc.OracleDriver
    url:jdbc:oracle:thin:@192.168.56.130:1521:orcl
    user:test_302
    password:ORCL
  2. 呼叫儲存過程
//需求:根據某個人的id 獲取他的年薪
	@Test
	public void testGetSomeOneYearNew() throws Exception{
		
		//獲取連結
		Connection conn = JdbcUtil.getConn();
		//sql語句 呼叫儲存過程 用{call 儲存過程名} ?佔位符的索引從1開始
		String sql = "{call FINDALLINFOBYNO(?,?,?)}";
		//獲取欲執行語句
		CallableStatement statement = conn.prepareCall(sql);
		statement.setInt(1, 7521);
		//傳出模式  引數得註冊
		statement.registerOutParameter(2,OracleTypes.VARCHAR);
		statement.registerOutParameter(3,OracleTypes.NUMBER);
		//執行
		statement.execute();
		//處理結果集
		//獲取佔位符的值
		System.out.println(statement.getString(2)+"的年薪為:"+statement.getDouble(3));
		//釋放連結
		JdbcUtil.closeResource(conn, statement, null);
	}

七、綜合示例

1.分頁獲取學生資料,包含學生以及班級資訊

  1. 儲存過程
CREATE   OR   REPLACE    PROCEDURE   FINDSTUDENTSBYPAGE(
	PI IN NUMBER,PS IN NUMBER,STUDENTLIST OUT SYS_REFCURSOR)
	AS
BEGIN
	OPEN STUDENTLIST FOR 
	SELECT * FROM (
		SELECT A.*,ROWNUM RN FROM 
			(SELECT S.*,G.G_NAME,G.G_DESC FROM STUDENT S INNER JOIN GRADE G ON S.G_ID = G.G_ID ORDER BY S.G_ID ASC,S.S_ID ASC) A) B WHERE B.RN BETWEEN (PI-1)*PS+1 AND PI*PS;
END;
  1. 客戶端來呼叫儲存過程
DECLARE
  	 studentlist  SYS_REFCURSOR;
	 sname student.s_name%TYPE;
	 gname grade.g_name%TYPE;
BEGIN
  	 FINDSTUDENTSBYPAGE(2,10,studentlist);
	 -- 解析遊標
	 LOOP
	 EXIT WHEN studentlist%NOTFOUND;
   	dbms_output.put_line('學生姓名:'||sname||'所屬班級'||gname);
	 FETCH studentlist INTO sname,gname;
	 END LOOP;
	 CLOSE studentlist;
END;
  1. java程式碼來呼叫儲存過程
@Test
	public void testGetStudentByPage() throws Exception{
		//獲取連結
		Connection conn = JdbcUtil.getConn();
		String sql="{call FINDSTUDENTBYPAGE(?,?,?)}";
		//獲取欲執行語句
		CallableStatement st = conn.prepareCall(sql);
		//--給引數賦值
		st.setInt(1, 2);
		st.setInt(2, 10);
		st.registerOutParameter(3, OracleTypes.CURSOR);
		//執行
		st.execute();
		//處理結果集
		ResultSet rs = ((OracleCallableStatement)st).getCursor(3);
		//--遍歷取值
		while(rs.next()){
			System.out.println(
					rs.getObject(1)+"\t"+
					rs.getObject(2)+"\t"+
					rs.getObject(4));
			System.out.println();
			
		}
		//釋放資源
		JdbcUtil.closeResource(conn, st, rs);
	}

八、觸發器

1.觸發器的型別

  1. 語句級觸發器。
    語句每執行一次就會執行一次。
  2. 行級的觸發器。

2.語句級的觸發器。

  1. 示例1
// 插入班級資訊以後,列印新增成功。
create or replace trigger gradetg1
	after insert on grade
declare 

begin
	dbms_output.put_line("新增成功");
end;

// 自己插入一條資訊測試。
  1. 示例2
不能再星期四  新增班級資訊。
create or  replace  tirgger gradetg2
	before insert on grade
declare`在這裡插入程式碼片`
	v_week  varchar(32);
BEGIN
	SELECT TO_CHAR(SYSDATE,'DAY') INTO V_WEEK FROM DUAL;
	IF V_WEEK = '星期四' THEN 
		Raise_application_error(-20001,'星期四不能新增班級!');
	END IF;
END;

3.行級的觸發器

判斷員工是否真正漲工資。
怎麼獲得漲工資之前或者之後的工資   ( for each row )
create or replace trigger emptg1
	before update [ of sal ] on emp for  each  row 
declare 

begin
	if  :new.sal = :old.sal < 0  then 
		raise_application_error(-20002,'不能降工資');
end;

4.實際應用

  1. 主鍵自增
--建立序列
create  sequence  seq_userinfo   nocycle  nocache;
--行級觸發器
create  or replace trigger  usertg1
	before  insert on user_info for each row
declare
begin
	select seq_userinfo.nextval  into :new.uid  FROM dual;
end;
  1. 日誌記錄
--建立序列
create   seqence   seq_syslog  nocycle   nocache;
-- 被班級表進行日誌記錄
	create  or  replace trigger  gradetg3
	after  update  on  grade for  each  row 
DECLARE
	 v_time  varchar(128);
	 v_msg  CLOB;
BEGIN
	--賦值當前時間 
	select   to_char(SYSDATE,'yyyy-MM-dd hh24:mm:ss') into v_time  from  dual;
	--插入入職表中
	 v_msg := '更新,發生在'||V_TIME||',操作前['||:old.g_name||']操作後
		['||:new.g_name||']';
 	insert   into  sys_log   values  (seq_syslog.Nextval,v_msg);
	dbms_output.put_line('日誌記錄成功!');
END;

oracle教程第四篇傳送門