Oracle04

要求


1)掌握PLSQL程序設計


2)掌握存儲過程,函數和觸發器


3)瞭解一些oralceSQL語句優化方案




-------------------------------------------------------------------------------------準備篇




col empno for 9999;
col ename for a10;
col job for a10;
col mgr for 9999;
col hiredate for a12;
col sal for 9999;
col comm for 9999;
col deptno for 99;
col tname for a40;
set pagesize 80;




-------------------------------------------------------------------------------------SQL對比PLSQL




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;
      
    
總之,Oracle優化不是一天的課題,你得在長期工作實踐中,進行反覆測試與總結,希望學員們日後好好領會









發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章