天天看點

Oracle資料庫之第四篇

版權聲明:本文為部落客原創文章,遵循 CC 4.0 BY-SA 版權協定,轉載請附上原文出處連結和本聲明。

本文連結:https://blog.csdn.net/zhao1299002788/article/details/101758110

/*
	  授權指令 grant 權限 to 使用者
		  授權可以授予多個權限
		  grant  connect,resource to baidu   
	  收回權限  revoke  權限 from  使用者
		  revoke dba from baidu
		  
	  建立使用者 配置設定表空間--指定使用者的預設表空間
		 create  table p(..) 建表存在預設表空間
		  --建表時候指定表空間 
		 create table p(...) talebspace 表空間名
	*/
	/*
	 PlSql程式設計語言  procedure language 過程語言
		  是在sql語句中加入一些處理過程的語句
		  常見的條件表達式 if else  還有循環結構
	 基本結構
		declare
		   --聲明部分 了解為定義
			  --聲明使用的變量
		begin
		   --處理邏輯的代碼塊
		end;
	*/
	--psSql簡單示例
	declare
	   v_n number := 1; --聲明數值變量 指派使用符号:=
	   v_s varchar2(4) :='s'; --聲明字元類型變量
	   emp_ename emp.ename%type ;-- 引用類型變量
	   emp_row   emp%rowtype ;-- 記錄類型變量
	begin
	   
	   dbms_output.put_line('v_n====='||v_n); --輸出語句相當于sys out 
	   dbms_output.put_line('v_s====='||v_s);
	   
	   select ename into emp_ename from emp where empno=7499; --使用into關鍵字指派
	   dbms_output.put_line('emp_ename====='||emp_ename);
	   
	   select  * into  emp_row    from emp where empno = 7499; --指派記錄類型變量
	   dbms_output.put_line('員工編号=='||emp_row.empno||'員工姓名'||emp_row.ename);
	end;
	/*
	  plsql 的條件表達式判斷
	  if .. 處理語句 else if ..處理語句
	  -------------------------
	  if .. then  
	  elsif .. then  
	  else  
	  end if;   
	*/
	---使用條件表達式判斷員工的工資 使用漢字輸出
	declare
	  
	   emp_row   emp%rowtype ;-- 記錄類型變量
	begin
	  
	   select  * into  emp_row    from emp where empno = 7499; --指派記錄類型變量
	   --使用表達式判斷工資
	   if emp_row.sal > 3000 then
		  dbms_output.put_line('員工工資大于3000=='||emp_row.sal);
	   elsif   emp_row.sal < 1000  then
		  dbms_output.put_line('員工工資小于1000=='||emp_row.sal);
	   else
		   dbms_output.put_line('員工工資位于1000到3000之間=='||emp_row.sal);
	   end if;
	   
	end;

	/*
	  循環結構
	第一種-----
	  loop
		exit when 條件
	  end loop;
	第二種 ---
	  while 條件 loop
		
	  end loop;
	第三種 ---
	  for 變量  in  範圍 loop
		
	  end loop;
	*/
	-------使用循環輸出數字 1-----10
	/*
	第一種
	  loop
		exit when 條件
	  end loop;
	*/
	declare
	   v_n number :=1;
	begin
	  
	  loop 
		--隻是用來判斷退出使用的,并不是相當于if()else{}
		exit when v_n>10 ;   --退出條件
		dbms_output.put_line(v_n);
		v_n:=v_n+1;   --自增
	  end loop;
	end;
	/*
	第二種
	  while 條件  loop
		
	  end loop;
	*/
	declare
	   v_n number :=1;
	begin
	  
	  while v_n<11  loop
		dbms_output.put_line(v_n);
		v_n:=v_n+1;   --自增
	  end loop;
	end;
	/*
	  第三種
	  for 變量 in 範圍 loop  變量的聲明和範圍的控制是由for循環自動執行
		
	  end loop;
	*/

	declare
	begin
	  
	  for i in 1..10  loop
		dbms_output.put_line(i);
	  end loop;
	end;
	/*
	  遊标 光标  是用于接收查詢的記錄結果集 ResultSet 提示記錄使用.next()
	  遊标的使用步驟
		 聲明遊标 cursor 遊标名 is select 語句 指定遊标的記錄結果集
		 打開遊标  open  遊标名
		 提取遊标  fetch 遊标名 into 記錄類型變量 
		 關閉遊标  close cursor
	  遊标的兩個屬性  遊标名%found     : 判斷它有找到
					  遊标名%notfound  : 判斷它沒有找到
		if  emp_cursor%found then
		   dbms_output.put_line('found');
		 elsif  emp_cursor%notfound then
		   dbms_output.put_line('notfound');
		 elsif emp_cursor%notfound is null then
			dbms_output.put_line('null');
		 end if;
	*/
	--使用while循環結構示範遊标
	declare
		 --聲明遊标
		 cursor emp_cursor is select * from emp;
		 --聲明記錄類型變量 用于接收遊标提取的記錄
		 emp_row  emp%rowtype; 
	begin
		 --打開遊标
		 open emp_cursor; 
		 --提取遊标(判斷下一個是否有值)
		 fetch emp_cursor into emp_row ;
		 --有值就執行while循環
		 while emp_cursor%found  loop
			dbms_output.put_line(emp_row.empno||'員工姓名'||emp_row.ename);
			--繼續提取遊标(并判斷下一個是否有值)
			fetch emp_cursor into emp_row ;
		 end loop;
		 close emp_cursor;
	end;
	/*
	 loop  
		exit when 遊标提取不到
	 end loop
	*/
	declare
		 --聲明遊标
		 cursor emp_cursor is select * from emp;
		 --聲明記錄類型變量 用于接收遊标提取的記錄
		 emp_row  emp%rowtype; 
	begin
		 --打開遊标
		 open emp_cursor; 
		 loop
		   fetch emp_cursor into emp_row;
		   exit when emp_cursor%notfound;
			 dbms_output.put_line(emp_row.empno||'員工姓名'||emp_row.ename);
		 end loop;
		 close emp_cursor;
	end;
	--使用遊标提取某個部門的員工資訊
	--聲明帶參數的遊标資訊
	declare
		 --聲明遊标
		 cursor emp_cursor(dno number) is select * from emp where deptno = dno ;
		 --聲明記錄類型變量 用于接收遊标提取的記錄
		 emp_row  emp%rowtype; 
	begin
		 --打開遊标 時候傳入參數
		 open emp_cursor(10); 
		 loop
		   fetch emp_cursor into emp_row;
		   exit when emp_cursor%notfound;
			 dbms_output.put_line(emp_row.empno||'員工姓名'||emp_row.ename);
		 end loop;
		 close emp_cursor;
	end;
	/*
	  錯誤資訊開發中的異常
	  資料庫中叫做 例外
			異常的分類 1.系統異常 系統定義好的異常
						   
					   2.自定義的異常
						 new 自定義類繼承Exception 自定義傳值(錯誤代碼,提示資訊)
						 使用場景
							不滿足某些特定業務場景,抛出自定義異常
			異常的處理
			   java  try{}catche(IndexOutOfBoundException e){}catche(Exception e){}
			   java  try{}catche(Exception e){} catche(IndexOutOfBoundException e){}--報錯
			   資料庫可以捕捉處理異常
					 exception   關鍵字捕捉異常
					   when 異常類型 then 處理語句   判斷異常類型 處理異常
							
	*/
	--異常的簡單示例
	/*
	  --除0的異常 除數為0
	  --指派錯誤
	  
	*/

	declare
	  v_n number :=0;
	  v_m number :=1;
	  
	begin
	  v_m:='s';  --将字元串指派給數值變量
	  v_m:= v_m/v_n;
	exception
	  
	  when zero_divide then 
		dbms_output.put_line('除數不能為0');
	  when value_error then
		dbms_output.put_line('指派有誤');

	end;
	---處理太多記錄數異常
	declare
	  
	   emp_row   emp%rowtype ;-- 記錄類型變量
	begin
	  
	   select  * into  emp_row  from emp ; --指派記錄類型
	   
	exception
	  
	   when too_many_rows then
		  dbms_output.put_line('太多記錄數'); 
		when others then    --others是最大範圍的異常 相當于java 的 Exception
		 dbms_output.put_line('其他異常');
	end;
	/*
	   需求 :使用遊标查詢部門下的員工資訊
			  如果部門下沒有員工 報錯提示
	   需要自定義異常 
	   變量名  exception     --聲明自定義異常
			  
	*/
	declare
	  
	   cursor emp_cursor is select * from emp where deptno= 40;  --遊标結果集不存在
		emp_row   emp%rowtype ;-- 記錄類型變量
	   no_dept_emp  exception ;  --聲明自定義異常
	begin
	   open emp_cursor;  --打開遊标
		
		   fetch emp_cursor into emp_row;
		   if emp_cursor%notfound then
			 --沒有員工  抛出錯誤異常
			 raise no_dept_emp;
		   end if;
	   close emp_cursor;
	exception
	   when no_dept_emp then 
		 dbms_output.put_line('部門下面沒人,快招人吧');
	end;

	/*
	 
	  存儲過程 是一段封裝好的代碼塊,過程是編譯好放在伺服器提供開發人員調用
	  
			  封裝的代碼塊意義:  提升開發效率  可以複用 誰用直接調用
								  提升運作效率 一調用直接運作
	   文法:create [or repalce]   procedure 過程名稱(參數名 out|in  參數類型) 
			 as|is
				 --聲明變量的部分 
			 begin
				 --處理過程語句代碼塊
			 end;
	   調用存儲過程
		  在begin和end之間使用 過程名傳參調用
	*/
	--存儲過程的簡單示例  使用存儲過程給某個員工增加工資100
	create or replace procedure add_sal(eno in number )
	as
	  emp_sal number :=0;
	begin
	  select sal into emp_sal from emp where empno = eno ;
	  dbms_output.put_line('漲工資之前是===='||emp_sal);
	  update emp set sal=sal+100 where empno = eno;
	  select sal into emp_sal from emp where empno = eno ;
	  dbms_output.put_line('漲工資之後是===='||emp_sal);
	  commit;
	end;
	--------調用存儲過程
	declare
	begin
	  add_sal(7499);
	end;
	/*
	  使用存儲過程統計某個員工的年薪,年薪需要傳回輸出列印
	  in 類型輸入參數可以 省略 預設就是輸入參數
	*/
	create or replace procedure count_sal(eno number,year_sal out number)
	as
	begin
	  
		select sal*12+nvl(comm,0) into year_sal  from emp where empno=eno; --使用into指派給輸出參數

	end;
	----調用存儲過程計算年薪
	declare
	  v_emp_sal number :=0;  
	begin
	  count_sal(7499,v_emp_sal);
	  dbms_output.put_line('年薪為=='||v_emp_sal);
	end;
	/*
	  使用存儲過程 查詢出某個部門的員工資訊
	  某個部門應該接受一個in類型的輸入參數
	  查詢到的部門員工多條記錄傳回應該使用結果集 
	  聲明遊标 cursor 遊标名 is select 語句指定結果集
	  系統引用遊标
	   sys_refcursor 
	   聲明系統引用遊标  變量名 sys_refcursor;  --不需要指定結果集
	   打開遊标      open 系統引用遊标 for select 語句  --使用for關鍵字裝入資料
	*/
	create or replace procedure dept_emp(dno number,cusor_emp out sys_refcursor)
	as
	begin
	  --根據傳進來的部門編号給遊标裝入結果集資料
	  open cusor_emp for select * from emp  where deptno = dno;   
	end;
	----調用存儲過程查詢部門下的員工
	declare
	   cursor_emp sys_refcursor;  --聲明系統引用遊标傳參使用
	   emp_row emp%rowtype ;--記錄類型變量
	begin
	  dept_emp(10,cursor_emp);
	  --提取遊标中的資料
	  loop
		fetch cursor_emp into emp_row;
		exit when cursor_emp%notfound;
		dbms_output.put_line('編号'||emp_row.empno||'姓名'||emp_row.ename);
	  end loop;
	  close cursor_emp; 
	end;
	/*
	  存儲函數 是一段封裝好的代碼塊,是編譯好放在伺服器提供開發人員調用
	  
			  封裝的代碼塊意義:  提升開發效率  可以複用 誰用直接調用
								  提升運作效率 一調用直接運作
								  
	   文法:create [or repalce]   function 函數名稱(參數名 out|in  參數類型) return 資料類型
													in 代表傳入參數,out 代表傳出參數						
			 as|is
				 --聲明變量的部分 
			 begin
				 --處理過程語句代碼塊
				 --return 變量
			 end;
	   調用存儲函數
		  在begin和end之間使用 函數名傳參調用 函數必須使用變量接收 傳回值
	  
	*/
	--使用存儲函數統計某個員工的年薪
	create or replace function count_emp_sal(eno number,year_sal out number) return number
	as
	  v_sal number :=0;
	begin
	  
		select sal*12+nvl(comm,0) into year_sal  from emp where empno=eno; --使用into指派給輸出參數
		return v_sal;
	end;
	--不帶out類型輸出參數統計年薪
	create or replace function count_sal_noout(eno number) return number
	as
	  v_sal number :=0;
	begin
	  
		select sal*12+nvl(comm,0) into v_sal  from emp where empno=eno; --使用into指派給輸出參數
		return v_sal;
	end;
	--調用函數統計年薪
	declare
	  emp_sal number:=0;
	  total_sal number :=0;
	begin
	   --total_sal := count_emp_sal(7499,emp_sal);
	   total_sal := count_sal_noout(7499);
	   dbms_output.put_line(emp_sal);--0
	   dbms_output.put_line(total_sal); --統計後年薪
	end;
	/*
	   存儲函數和過程的差別
	   
		   1.建立的關鍵字  procedure   funciton
		   2.建立函數 必須使用return 聲明函數的傳回變量資料類型
		   3.在函數的方法體内 必須使用return 傳回一個變量
		   4.函數的調用 必須有變量接收傳回值
		   5.函數可以用在select 查詢語句中  select emp.*,count_sal_noout(empno) from emp;
	   
	   存儲函數和過程使用場景
		   開發規範 java代碼待用過程 過程是用來處理業務邏輯代碼
		   如果邏輯中需要用到一些功能性的封裝,可以調用函數
		   90%情況下 函數和過程通用 過程可以調用函數,函數同樣可以調用過程
	   
	*/

	/*
	  觸發器 是一個螢幕,監視對表中資料的操作
		   如果對資料的操作滿足觸發器的執行條件,
		   觸發器會自動運作
	  觸發器文法:
		  create or repalce trigger 觸發器名稱
		  after|before   --觸發器執行時機
		  insert|update|delete  --監視的動作
		  on 表名       --表級觸發器
		  declare
		  begin          
		  end;
	   行級觸發器                    insert        update        delete
		   :new   動作之後的記錄   要插入的記錄   修改後的記錄     空
		   :old   動作之前的記錄      空          原始的記錄     原始的記錄
		   
	*/
	--建立觸發器監視表,如果表中有資料插入,輸出一個歡迎語句
	create or replace trigger insert_trigger
	after
	insert
	on p
	declare
	   
	begin
	   dbms_output.put_line('歡迎加入!');
	end;
	----插入資料測試效果
	insert into p values(1,'zs');
	commit;
	--插入資料不能在休息日插入資料
	--休息日 周六和周日
	/*
	 raise_application_error(v1,v2) v1錯誤代碼  v2是提示語句
									  -20000 -20999 
	*/
	create or replace trigger insert_no_work
	before
	insert
	on p
	declare
	   v_day varchar2(10) ;
	begin
	  --擷取到目前星期
	  select to_char(sysdate,'day') into v_day from dual;
	  --判斷星期是否在休息日
	  if trim(v_day)  in ('saturday','sunday') then
	  --如果休息 錯誤提示
		 raise_application_error(-20001,'不能休息日插入資料');
	  end if;
	end;
	----插入資料測試效果
	insert into p values(1,'zs');
	commit;

	--使用觸發器監視表中資料修改,不能做降低工資的操作
	create or replace trigger can_not_low
	before
	update
	on emp
	for each row --行級觸發器
	declare
	  
	begin
	  --擷取到原始記錄的工資  --擷取修改後的工資
	  if :old.sal > :new.sal then
	  --談錯誤框提示
	  raise_application_error(-20002,'不能降低工資');
	  end if;
	end;
	--修改員工的工資測試觸發器
	update emp set sal=sal-1 where empno=7499;

	/*
	  觸發器實際應用 
		  使用觸發器實作 插入資料的id 自增長 面試題
	**/

	create or replace trigger auto_increment_id
	before    
	insert
	on test_trigger
	for each row 
	declare 
	begin
	  --補全将要插入記錄的id
	  --補全的id 是自增長的數值 如果沒有提前建立序列,需要提前建立序列   --建立序列  create sequence order_sequence 
	  select order_sequence.nextval into :new.pid from dual;
	end;

	insert into test_trigger(pname,phone) values('zs','1234566');
	commit;

	package baidu;

import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;

import org.junit.Test;

import oracle.jdbc.OracleCallableStatement;
import oracle.jdbc.OracleTypes;

public class TestJdbc {

	 String driverClass = "oracle.jdbc.driver.OracleDriver";
	 String url ="jdbc:oracle:thin:@192.168.17.128:1521:orcl";
	 String user= "baidu_03";
	 String password = "baidu_03";
	 /*
	  *測試jdbc連接配接資料庫
	  *
	  * */
	 @Test
	 public void querEmp(){
		 try{
		 //加載驅動
		 Class.forName(driverClass);
		 //擷取連結
		 Connection con = DriverManager.getConnection(url, user,password);
		 //擷取預編譯的statement
		 PreparedStatement pst= con.prepareStatement("select * from emp");
		 //執行查詢
		 ResultSet rs =  pst.executeQuery();
		 //處理結果
		 while(rs.next()){
			 System.out.println(rs.getInt(1)+"員工姓名"+rs.getString("ename"));
		 }
		 rs.close();
		 con.close();
		 //關閉連接配接
		 }catch(Exception e){
			 e.printStackTrace();
		 }
		 
	 }
	 /*存儲過程的調用
	  * {call <procedure-name>[(<arg1>,<arg2>, ...)]}
		add_sal(eno number,addsal number)
	  * */
	 @Test
	 public void callAddSal(){
		 try{
		 //加載驅動
		 Class.forName(driverClass);
		 //擷取連結
		 Connection con = DriverManager.getConnection(url, user,password);
		 //擷取預編譯的statement
		 CallableStatement pst= con.prepareCall("{call add_sal(?,?)}");
		 pst.setInt(1, 7499);
		 pst.setInt(2, 1000);
		 //執行查詢
		 pst.execute();
		 con.close();
		 //關閉連接配接
		 }catch(Exception e){
			 e.printStackTrace();
		 }
		 
	 }
	 
	 /*存儲過程的調用
	  * {call <procedure-name>[(<arg1>,<arg2>, ...)]}
		count_yearsal(eno number,total_year_sal out number)
	  * */
	 @Test
	 public void callCountSal(){
		 try{
		 //加載驅動
		 Class.forName(driverClass);
		 //擷取連結
		 Connection con = DriverManager.getConnection(url, user,password);
		 //擷取預編譯的statement
		 CallableStatement pst= con.prepareCall("{call count_yearsal(?,?)}");
		 pst.setInt(1, 7499);
		 //注冊輸出參數
		 pst.registerOutParameter(2, OracleTypes.NUMBER);
		 //執行查詢
		 pst.execute();
		 int total =  pst.getInt(2);
		 System.out.println(total);
		 con.close();
		 //關閉連接配接
		 }catch(Exception e){
			 e.printStackTrace();
		 }
		 
	 }
	 /*
	  * pro_dept_emp(dno number,dept_emp out sys_refcursor)
	  * */
	 @Test
	 public void callProEmp(){
		 try{
		 //加載驅動
		 Class.forName(driverClass);
		 //擷取連結
		 Connection con = DriverManager.getConnection(url, user,password);
		 //擷取預編譯的statement
		 CallableStatement pst= con.prepareCall("{call pro_dept_emp(?,?)}");
		 pst.setInt(1, 10);
		 //注冊輸出參數
		 pst.registerOutParameter(2, OracleTypes.CURSOR);
		 //執行查詢
		 pst.execute();
		 OracleCallableStatement ocs = (OracleCallableStatement)pst;
		 ResultSet rs =  ocs.getCursor(2);
		 while(rs.next()){
			 System.out.println(rs.getInt(1)+"員工姓名"+rs.getString("ename"));
		 }
		 rs.close();
		 ocs.close();
		 pst.close();
		 con.close();
		 //關閉連接配接
		 }catch(Exception e){
			 e.printStackTrace();
		 }
		 
	 }
}

public void show4(){
		 try {
			 
			 Class.forName(driverClass);
			 Connection con = DriverManager.getConnection(url, user,password);
			 CallableStatement pst= con.prepareCall("{?= call count_sal_noout(?)}");

			 //給第二個參數指派
			 pst.setLong(2, 7499);
			// stat2.setLong(2, empno);

			 //聲明第一個參數的類型
			 pst.registerOutParameter(1, OracleTypes.NUMBER);
			 pst.execute();
			 OracleCallableStatement ocs = (OracleCallableStatement)pst;
			 NUMBER num = ocs.getNUMBER(1);
			 System.out.println(num);
//			 long i = pst.getLong(1);
//			 System.out.println(i);
		 
			 con.close();
			 
		 } catch (Exception e) {
			 e.printStackTrace();
		 }
	 }
/*           

複制