1. 程式人生 > >Oracle儲存過程、遊標、函式

Oracle儲存過程、遊標、函式

SQL99是什麼
(1)是操作所有關係型資料庫的規則
(2)是第四代語言
(3)是一種結構化查詢語言
(4)只需發出合法合理的命令,就有對應的結果顯示

SQL的特點
(1)互動性強,非過程化
(2)資料庫操縱能力強,只需傳送命令,無需關注如何實現
(3)多表操作時,自動導航簡單,例如:
     select emp.empno,emp.sal,dept.dname
     from emp,dept
     where emp.deptno = dept.deptno
(4)容易除錯,錯誤提示,直接了當
(5)SQL強調結果 

PLSQL是什麼
     是專用於Oracle伺服器,在SQL基礎之上,添加了一些過程化控制語句,叫PLSQL
     過程化包括有:型別定義,判斷,迴圈,遊標,異常或例外處理。。。
     PLSQL強調過程

為什麼要用PLSQL
     因為SQL是第四代命令式語言,無法顯示處理過程化的業務,所以得用一個過程化程式設計語言來彌補SQL的不足之處,
     SQL和PLSQL不是替代關係,是彌補關係        
        
PLSQL程式的完整組成結構如下:
     [declare]
          變數宣告;
        變數宣告;
     begin
          DML
/TCL操作; DML/TCL操作; [exception] 例外處理; 例外處理; end; / 注意:在PLSQL程式中,;號表示每條語句的結束,/表示整個PLSQL程式結束 書寫PLSQL的工具有: (1)SQLPLUS工具 (2)SQLDeveloper工具 (3)第三方工具(PLSQL & 其它) PLSQL與SQL執行有什麼不同: (1)SQL是單條執行的 (2)PLSQL是整體執行的,不能單條執行,整個PLSQL結束用/,其中每條語句結束用;號 ------------------------------------------------------------------------------------PLSQL型別 寫一個PLSQL程式,輸出
"hello world"字串,語法:dbms_output.put_line('需要輸出的字串'); begin --向SQLPLUS客戶端工具輸出字串 dbms_output.put_line('hello 你好'); end; / 注意: dbms_output是oracle中的一個輸出物件 put_line是上述物件的一個方法,用於輸出一個字串自動換行 設定顯示PLSQL程式的執行結果,預設情況下,不顯示PLSQL程式的執行結果,語法:set serveroutput on/off; set serveroutput on; 使用基本型別變數,常量和註釋,求10
+100的和 declare --定義變數 mysum number(3) := 0; tip varchar2(10) := '結果是'; begin /*業務演算法*/ mysum := 10 + 100; /*輸出到控制器*/ dbms_output.put_line(tip || mysum); end; / 輸出7369號員工姓名和工資,格式如下:7369號員工的姓名是SMITH,薪水是800,語法:使用表名.欄位%type declare --定義二個變數,分別裝姓名和工資 pename emp.ename%type; psal emp.sal%type; begin --SQL語句 --select ename,sal from emp where empno = 7369; --PLSQL語句,將ename的值放入pename變數中,sal的值放入psal變數中 select ename,sal into pename,psal from emp where empno = 7369; --輸出 dbms_output.put_line('7369號員工的姓名是'||pename||',薪水是'||psal); end; / 輸出7788號員工姓名和工資,格式如下:7788號員工的姓名是SMITH,薪水是3000,語法:使用表名%rowtype declare emp_record emp%rowtype; begin select * into emp_record from emp where empno = 7788; dbms_output.put_line('7788號員工的姓名是'||emp_record.ename||',薪水是'||emp_record.sal); end; / 何時使用%type,何時使用%rowtype? 當定義變數時,該變數的型別與表中某欄位的型別相同時,可以使用%type 當定義變數時,該變數與整個表結構完全相同時,可以使用%rowtype,此時通過變數名.欄位名,可以取值變數中對應的值 專案中,常用%type ------------------------------------------------------------------------------------PLSQL判斷 使用if-else-end if顯示今天星期幾,是"工作日"還是"休息日" declare pday varchar2(10); begin select to_char(sysdate,'day') into pday from dual; dbms_output.put_line('今天是'||pday); if pday in ('星期六','星期日') then dbms_output.put_line('休息日'); else dbms_output.put_line('工作日'); end if; end; / 從鍵盤接收值,使用if-elsif-else-end if顯示"age<16","age<30","age<60","age<80" declare age number(3) := &age; begin if age < 16 then dbms_output.put_line('你未成人'); elsif age < 30 then dbms_output.put_line('你青年人'); elsif age < 60 then dbms_output.put_line('你奮鬥人'); elsif age < 80 then dbms_output.put_line('你享受人'); else dbms_output.put_line('未完再繼'); end if; end; / -------------------------------------------------------------------------------------PLSQL迴圈 使用loop迴圈顯示1-10 declare i number(2) := 1; begin loop --當i>10時,退出迴圈 exit when i>10; --輸出i的值 dbms_output.put_line(i); --變數自加 i := i + 1; end loop; end; / 使用while迴圈顯示1-10 declare i number(2) := 1; begin while i<11 loop dbms_output.put_line(i); i := i + 1; end loop; end; / 使用while迴圈,向emp表中插入999條記錄 declare i number(4) := 1; begin while( i < 1000 ) loop insert into emp(empno,ename) values(i,'哈哈'); i := i + 1; end loop; end; / 使用while迴圈,從emp表中刪除999條記錄 declare i number(4) := 1; begin while i<1000 loop delete from emp where empno = i; i := i + 1; end loop; end; / 使用for迴圈顯示20-30 declare i number(2) := 20; begin for i in 20 .. 30 loop dbms_output.put_line(i); end loop; end; / -------------------------------------------------------------------------------------PLSQL遊標 什麼是游標/遊標/cursor 類似於JDBC中的ResultSet物件的功能,從上向下依次獲取每一記錄的內容 使用無參游標cursor,查詢所有員工的姓名和工資【如果需要遍歷多條記錄時,使用游標cursor,無記錄找到使用cemp%notfound】 declare --定義遊標 cursor cemp is select ename,sal from emp; --定義變數 vename emp.ename%type; vsal emp.sal%type; begin --開啟遊標,這時遊標位於第一條記錄之前 open cemp; --迴圈 loop --向下移動遊標一次 fetch cemp into vename,vsal; --退出迴圈,當遊標下移一次後,找不到記錄時,則退出迴圈 exit when cemp%notfound; --輸出結果 dbms_output.put_line(vename||'--------'||vsal); end loop; --關閉遊標 close cemp; end; / 使用帶參游標cursor,查詢10號部門的員工姓名和工資 declare cursor cemp(pdeptno emp.deptno%type) is select ename,sal from emp where deptno=pdeptno; pename emp.ename%type; psal emp.sal%type; begin open cemp(&deptno); loop fetch cemp into pename,psal; exit when cemp%notfound; dbms_output.put_line(pename||'的薪水是'||psal); end loop; close cemp; end; / 使用無參游標cursor,真正給員工漲工資,ANALYST漲1000,MANAGER漲800,其它漲400,要求顯示編號,姓名,職位,薪水 declare cursor cemp is select empno,ename,job,sal from emp; pempno emp.empno%type; pename emp.ename%type; pjob emp.job%type; psal emp.sal%type; begin open cemp; loop fetch cemp into pempno,pename,pjob,psal; --迴圈退出條件一定要寫 exit when cemp%notfound; if pjob='ANALYST' then update emp set sal = sal + 1000 where empno = pempno; elsif pjob='MANAGER' then update emp set sal = sal + 800 where empno = pempno; else update emp set sal = sal + 400 where empno = pempno; end if; end loop; commit; close cemp; end; / -------------------------------------------------------------------------------------PLSQL例外 使用oracle系統內建例外,演示除0例外【zero_divide】 declare myresult number; begin myresult := 1/0; dbms_output.put_line(myresult); exception when zero_divide then dbms_output.put_line('除數不能為0'); delete from emp; end; / 使用oracle系統內建例外,查詢100號部門的員工姓名,演示沒有找到資料【no_data_found】 declare pename varchar2(20); begin select ename into pename from emp where deptno = 100; dbms_output.put_line(pename); exception when NO_DATA_FOUND then dbms_output.put_line('查無該部門員工'); insert into emp(empno,ename) values(1111,'ERROR'); end; / 使用使用者自定義例外,使用游標cursor,查詢10/20/30/100號部門的員工姓名,演示沒有找到資料【nohave_emp_found】 ------------------------------------------------------------------------------------儲存過程概念 什麼是儲存過程【procedure】? 為什麼要用儲存過程? (1)PLSQL每次執行都要整體執行一遍,才有結果 (2)PLSQL不能將其封裝起來,長期儲存在oracle伺服器中 (3)PLSQL不能被其它應用程式呼叫,例如:Java 儲存過程與PLSQL是什麼關係? --------------------------------------------------------儲存過程 建立無參儲存過程hello,無返回值,語法:create or replace procedure 過程名 as PLSQL程式 刪除儲存過程hello,語法:drop procedure 過程名 呼叫儲存過程方式一,exec 儲存過程名 呼叫儲存過程方式二,PLSQL程式 呼叫儲存過程方式三,Java程式 建立有參儲存過程raiseSalary(編號),為7369號員工漲10%的工資,演示in的用法,預設in,大小寫不敏感 建立有參儲存過程findEmpNameAndSalAndJob(編號),查詢7788號員工的的姓名,職位,月薪,返回多個值,演示out的用法 什麼情況下用exec呼叫,什麼情況下用PLSQL呼叫儲存過程? 用儲存過程,寫一個計算個人所得稅的功能 -------------------------------------------------------------------------------------儲存函式 建立無參儲存函式getName,有返回值,語法:create or replace function 函式名 return 返回型別 as PLSQL程式段 刪除儲存函式getName,語法:drop function 函式名 呼叫儲存函式方式一,PLSQL程式 呼叫儲存函式方式二,Java程式 建立有參儲存函式findEmpIncome(編號),查詢7369號員工的年收入,演示in的用法,預設in 建立有參儲存函式findEmpNameAndJobAndSal(編號),查詢7788號員工的的姓名(return),職位(out),月薪(out),返回多個值 -------------------------------------------------------------------------------------過程函式適合場景 宣告:適合不是強行要你使用,只是優先考慮 什麼情況下【適合使用】儲存過程?什麼情況下【適合使用】儲存函式? 【適合使用】儲存過程: 【適合使用】儲存函式: 什麼情況【適合使用】過程函式,什麼情況【適合使用】SQL? 【適合使用】過程函式: 》需要長期儲存在資料庫中 》需要被多個使用者重複呼叫 》業務邏輯相同,只是引數不一樣 》批操作大量資料,例如:批量插入很多資料 【適合使用】SQL: 》凡是上述反面,都可使用SQL 》對錶,檢視,序列,索引,等這些還是要用SQL -------------------------------------------------------------------------------------觸發器 什麼是觸發器【Trigger】? 為什麼要用觸發器? 建立語句級觸發器insertEmpTrigger,當對錶【emp】進行增加【insert】操作前【before】,顯示"hello world" 刪除觸發器insertEmpTrigger,語法:drop trigger 觸發器名 使用insert語句插入一條記錄,引起insertEmpTrigger觸發器工作 使用insert語句插入N條記錄,引起insertEmpTrigger觸發器工作 建立語句級觸發器deleteEmpTrigger,當對錶【emp】進行刪除【delete】操作後【after】,顯示"world hello" 使用delete語句刪除一條記錄,引起deleteEmpTrigger觸發器工作 使用delete語句刪除N條記錄,引起deleteEmpTrigger觸發器工作 星期一到星期五,且9-20點能向資料庫emp表插入資料,否則使用函式丟擲異常, 語法:raise_application_error('-20000','例外原因') 建立行級觸發器checkSalaryTrigger,漲後工資這一列,確保大於漲前工資,語法:for each row/:new.sal/:old.sal 刪除觸發器,表還在嗎? 將表丟到回收站,觸發器還在嗎? 當閃回表後,觸發器會在嗎? 徹底刪除表,觸發器會在嗎? -------------------------------------------------------------------------------------oracleSQL優化方案 為什麼要Oracle優化: 隨著實際專案的啟動,Oracle經過一段時間的執行,最初的Oracle設定,會與實際Oracle執行效能會有一些差異,這時我們 就需要做一個優化調整。 Oracle優化這個課題較大,可分為四大類: 》主機效能 》記憶體使用效能 》網路傳輸效能 》SQL語句執行效能【程式設計師】 下面列出一些oracleSQL優化方案: (01)選擇最有效率的表名順序(筆試常考) ORACLE的解析器按照從右到左的順序處理FROM子句中的表名, FROM子句中寫在最後的表將被最先處理, 在FROM子句中包含多個表的情況下,你必須選擇記錄條數最少的表放在最後, 如果有3個以上的表連線查詢,那就需要選擇那個被其他表所引用的表放在最後。 例如:查詢員工的編號,姓名,工資,工資等級,部門名 select emp.empno,emp.ename,emp.sal,salgrade.grade,dept.dname from salgrade,dept,emp where (emp.deptno = dept.deptno) and (emp.sal between salgrade.losal and salgrade.hisal) 1)如果三個表是完全無關係的話,將記錄和列名最少的表,寫在最後,然後依次類推 2)如果三個表是有關係的話,將引用最多的表,放在最後,然後依次類推 (02)WHERE子句中的連線順序(筆試常考) ORACLE採用自右而左的順序解析WHERE子句,根據這個原理,表之間的連線必須寫在其他WHERE條件之左, 那些可以過濾掉最大數量記錄的條件必須寫在WHERE子句的之右。 例如:查詢員工的編號,姓名,工資,部門名 select emp.empno,emp.ename,emp.sal,dept.dname from emp,dept where (emp.deptno = dept.deptno) and (emp.sal > 1500) (03)SELECT子句中避免使用*號 ORACLE在解析的過程中,會將*依次轉換成所有的列名,這個工作是通過查詢資料字典完成的,這意味著將耗費更多的時間 select empno,ename from emp; (04)使用DECODE函式來減少處理時間 使用DECODE函式可以避免重複掃描相同記錄或重複連線相同的表 (05)整合簡單,無關聯的資料庫訪問 (06)用TRUNCATE替代DELETE (07)儘量多使用COMMIT 因為COMMIT會釋放回滾點 (08)用WHERE子句替換HAVING子句 WHERE先執行,HAVING後執行 (09)多使用內部函式提高SQL效率 (10)使用表的別名 salgrade s (11)使用列的別名 ename e (12)用索引提高效率 在查詢中,善用索引 (13)字串型,能用=號,不用like 因為=號表示精確比較,like表示模糊比較 (14)SQL語句用大寫的 因為Oracle伺服器總是先將小寫字母轉成大寫後,才執行 在eclipse中,先寫小寫字母,再通過ctrl+shift+X轉大寫;ctrl+shift+Y轉小寫 (15)避免在索引列上使用NOT 因為Oracle伺服器遇到NOT後,他就會停止目前的工作,轉而執行全表掃描 (16)避免在索引列上使用計算 WHERE子句中,如果索引列是函式的一部分,優化器將不使用索引而使用全表掃描,這樣會變得變慢 例如,SAL列上有索引, 低效: SELECT EMPNO,ENAME FROM EMP WHERE SAL*12 > 24000; 高效: SELECT EMPNO,ENAME FROM EMP WHERE SAL > 24000/12; (17)用 >= 替代 > 低效: SELECT * FROM EMP WHERE DEPTNO > 3 首先定位到DEPTNO=3的記錄並且掃描到第一個DEPT大於3的記錄 高效: SELECT * FROM EMP WHERE DEPTNO >= 4 直接跳到第一個DEPT等於4的記錄 (18)用IN替代OR select * from emp where sal = 1500 or sal = 3000 or sal = 800; select * from emp where sal in (1500,3000,800); (19)總是使用索引的第一個列 如果索引是建立在多個列上,只有在它的第一個列被WHERE子句引用時,優化器才會選擇使用該索引 當只引用索引的第二個列時,不引用索引的第一個列時,優化器使用了全表掃描而忽略了索引 create index emp_sal_job_idex on emp(sal,job); ---------------------------------- select * from emp where job != 'SALES'; (20)避免改變索引列的型別,顯示比隱式更安全 當字元和數值比較時,ORACLE會優先轉換數值型別到字元型別 select 123 || '123' from dual;