繫結變數及其優缺點

dawn009發表於2014-07-31

繫結變數是Oracle解決硬解析的首要利器,能解決OLTP系統中library cache的過度耗用以提高效能。然刀子磨的太快,使起來鋒利,卻容
易折斷。凡事皆有利弊二性,因地制宜,因時制宜,全在如何權衡而已。本文講述了繫結變數的使用方法,以及繫結變數的優缺點、使用場合。

 

一、繫結變數

    提到繫結變數,就不得不瞭解硬解析與軟解析。硬解析簡言之即一條SQL語句沒有被執行過,處於首次執行,則需要對其進行語法分析,語
義識別,跟據統計資訊生成最佳的執行計劃,然後對其執行。而軟解析呢,則是由於在library cache已經存在與該SQL語句一致的SQL語句文字
、執行環境,即有相同的父遊標與子游標,採用拿來主義,直接執行即可。軟解析同樣經歷語法分析,語義識別,且生成hash value ,接下來
在library cache搜尋相同的hash value ,如存在在實施軟解析。有關更多的硬解析與軟解析以及父遊標,子游標請作如下參考:
    
    有關硬解析與軟解析,請參考:Oracle 硬解析與軟解析
    有關父遊標、子游標,請參考:父遊標、子游標與共享遊標
    
    繫結變數
      首先其實質是變數,有些類似於我們經常使用的替代變數,替代變數使用&佔位符,只不過繫結變數使用:
      替代變數使用時為 &variable_para,相應的繫結變數則為 :bind_variable_para
      通常一個SQL語句包含動態部分和靜態部分,佔位符實質是SQL語句中容易發生變化的部分,通常為其條件或取值範圍。動態部分在一般情
      況下(資料傾斜除外),對執行計劃的生成的影響是微乎其微的。故同一SQL語句不同的動態部分產生的執行計劃都是相同的。
        
二、繫結變數的使用
    1、在SQLPlus中使用繫結變數

  1. SQL> variable eno number;                           --&gt使用variable定義變數                                              
  2. SQL> exec :eno:=7788;                                                                                                    
  3. SQL> select ename,job,sal from emp where empno=:eno;                                                                     
  4.                                                                                                                          
  5. ENAME      JOB              SAL                                                                                          
  6. ---------- --------- ----------                                                                                          
  7. SCOTT      ANALYST         3000                                                                                          
  8.                                                                                                                          
  9. SQL> col sql_text format a55                                                                                             
  10. SQL> select sql_id,sql_text,executions from v$sqlarea   --&gt首次查詢後在v$sqlarea儲存父遊標且執行次數EXECUTIONS為1        
  11.   2  where sql_text like '%select ename,job,sal%' and sql_text not like '%from v$sql%';                                  
  12.                                                                                                                          
  13. SQL_ID        SQL_TEXT                                                EXECUTIONS                                         
  14. ------------- ------------------------------------------------------- ----------                                         
  15. dbc6vx6z6n1zv select ename,job,sal from emp where empno=:eno                   1                                         
  16.                                                                                                                          
  17. SQL> select sql_id,hash_value,child_number,sql_text from v$sql --&gt查詢檢視v$sql檢視該SQL對應的子游標,且CHILD_NUMBER為0  
  18.   2  where sql_text like '%select ename,job,sal%' and sql_text not like '%from v$sql%';                                  
  19.                                                                                                                          
  20. SQL_ID        HASH_VALUE CHILD_NUMBER SQL_TEXT                                                                           
  21. ------------- ---------- ------------ -------------------------------------------------------                            
  22. dbc6vx6z6n1zv 3194619899            0 select ename,job,sal from emp where empno=:eno                                     
  23.                                                                                                                          
  24. SQL> exec :eno:=7369;                                                                                                    
  25. SQL> select ename,job,sal from emp where empno=:eno;  --&gt再次對變數賦值並查詢                                            
  26.                                                                                                                          
  27. ENAME      JOB              SAL                                                                                          
  28. ---------- --------- ----------                                                                                          
  29. SMITH      CLERK            800                                                                                          
  30.                                                                                                                          
  31. SQL> exec :eno:=7521                                                                                                     
  32. SQL> select ename,job,sal from emp where empno=:eno;                                                                     
  33.                                                                                                                          
  34. ENAME      JOB              SAL                                                                                          
  35. ---------- --------- ----------                                                                                          
  36. WARD       SALESMAN        1250                                                                                          
  37.                                                                                                                          
  38. SQL> select sql_id,sql_text,executions from v$sqlarea --&gt檢視v$sqlarea中EXECUTIONS值為3,對應的SQL被執行了3次            
  39.   2  where sql_text like '%select ename,job,sal%' and sql_text not like '%from v$sql%';                                  
  40.                                                                                                                          
  41. SQL_ID        SQL_TEXT                                                EXECUTIONS                                         
  42. ------------- ------------------------------------------------------- ----------                                         
  43. dbc6vx6z6n1zv select ename,job,sal from emp where empno=:eno                   3                                         
  44.                                                                                                                          
  45. --&gt檢視v$sql中對應的子游標也實現了完全共享,保持CHILD_NUMBER為0                                                          
  46. SQL> select sql_id,hash_value,child_number,sql_text from v$sql                                                           
  47.   2  where sql_text like '%select ename,job,sal%' and sql_text not like '%from v$sql%';                                  
  48.                                                                                                                          
  49. SQL_ID        HASH_VALUE CHILD_NUMBER SQL_TEXT                                                                           
  50. ------------- ---------- ------------ -------------------------------------------------------                            
  51. dbc6vx6z6n1zv 3194619899            0 select ename,job,sal from emp where empno=:eno                                     

    2、PL/SQL塊中使用繫結變數

  1. SQL> create table t(id number,val number);  --&gt首先建立表t                                                               
  2.                                                                                                                          
  3. SQL> get get_parse.sql                                                                                                   
  4.   1  select name,value from v$mystat a join v$statname b                                                                 
  5.   2* on a.statistic#=b.statistic# where b.name like 'parse count%';                                                      
  6.                                                                                                                          
  7. SQL> @get_parse.sql   --&gt獲得當前的解析情況,此時hard parase 為63                                                        
  8.                                                                                                                          
  9. NAME                           VALUE                                                                                     
  10. ------------------------- ----------                                                                                     
  11. parse count (total)              394                                                                                     
  12. parse count (hard)                63                                                                                     
  13. parse count (failures)             1                                                                                     
  14.                                                                                                                          
  15. --&gt下面的pl/sql程式碼中,Oracle實現自動變數自動繫結,執行了30次的insert操作,但oracle認為每次執行的語句都是一樣的          
  16. /**************************************************/                                                                     
  17. /* Author: Robinson Cheng                         */                                                                     
  18. /* Blog:   http://blog.csdn.net/robinson_0612     */                                                                     
  19. /* MSN:    robinson_0612@hotmail.com              */                                                                     
  20. /* QQ:     645746311                              */                                                                     
  21. /**************************************************/                                                                     
  22.                                                                                                                          
  23. SQL> begin                     --&gt執行pl/sql程式碼,向表t中插入30條記錄                                                    
  24.   2  for i in 1..30 loop                                                                                                 
  25.   3  insert into t values(i,i*2);                                                                                        
  26.   4  end loop;                                                                                                           
  27.   5  commit;                                                                                                             
  28.   6  end;                                                                                                                
  29.   7  /                                                                                                                   
  30.                                                                                                                          
  31. PL/SQL procedure successfully completed.                                                                                 
  32.                                                                                                                          
  33. SQL>  @get_parse              --&gt程式碼執行後的結果,硬解析數量僅僅增加了3次                                               
  34.                                                                                                                          
  35. NAME                           VALUE                                                                                     
  36. ------------------------- ----------                                                                                     
  37. parse count (total)              401                                                                                     
  38. parse count (hard)                67                                                                                     
  39. parse count (failures)             1                                                                                     

    3、在儲存過程或包中使用繫結變數

  1. --&gt儲存過程和保重,對引數的傳遞即是使用自動繫結變數來實現,因此程式設計人員無須操心繫結變數問題,如下例所示:               
  2. SQL> create or replace procedure ins_t(p_id in number,p_value in number) --&gt建立一個過程用於向表t插入記錄               
  3.   2  as                                                                                                                 
  4.   3    begin                                                                                                            
  5.   4      insert into t values(p_id,p_value);                                                                            
  6.   5      commit;                                                                                                        
  7.   6    end;                                                                                                             
  8.   7  /                                                                                                                  
  9.                                                                                                                         
  10. Procedure created.                                                                                                      
  11.                                                                                                                         
  12. SQL> select sid,serial# from v$session where username='SCOTT';  --&gt獲得當前使用者的sid,serial#                            
  13.                                                                                                                         
  14.        SID    SERIAL#                                                                                                   
  15. ---------- ----------                                                                                                   
  16.       1084        938                                                                                                   
  17.                                                                                                                         
  18. SQL> exec dbms_monitor.session_trace_enable(session_id=>1084,serial_num=>938);  --&gt對當前的session啟用跟蹤              
  19.                                                                                                                         
  20. PL/SQL procedure successfully completed.                                                                                
  21.                                                                                                                         
  22. SQL> exec ins_t(31,62);               --&gt執行儲存過程                                                                   
  23.                                                                                                                         
  24. PL/SQL procedure successfully completed.                                                                                
  25.                                                                                                                         
  26. SQL> exec ins_t(32,64);                                                                                                 
  27.                                                                                                                         
  28. PL/SQL procedure successfully completed.                                                                                
  29.                                                                                                                         
  30. SQL> exec dbms_monitor.session_trace_disable(session_id=>1084,serial_num=>938); --&gt關閉對session的跟蹤                  
  31.                                                                                                                         
  32. PL/SQL procedure successfully completed.                                                                                
  33.                                                                                                                         
  34. SQL> SET LINESIZE 180                                                                                                   
  35. SQL> COLUMN trace_file FORMAT A100                                                                                      
  36. SQL> SELECT s.sid,                   --&gt獲得跟蹤檔案位置                                                                
  37.   2  s.serial#,                                                                                                         
  38.   3  p.spid,                                                                                                            
  39.   4  pa.value || '/' || LOWER(SYS_CONTEXT('userenv','instance_name')) ||                                                
  40.   5  '_ora_' || p.spid || '.trc' AS trace_file                                                                          
  41.   6  FROM v$session s,                                                                                                  
  42.   7  v$process p,                                                                                                       
  43.   8  v$parameter pa                                                                                                     
  44.   9  WHERE pa.name = 'user_dump_dest'                                                                                   
  45.  10  AND s.paddr = p.addr                                                                                               
  46.  11  AND s.audsid = SYS_CONTEXT('USERENV''SESSIONID');                                                                
  47.                                                                                                                         
  48.        SID    SERIAL# SPID         TRACE_FILE                                                                           
  49. ---------- ---------- ------------ --------------------------------------------------------------                       
  50.       1084        938 10883        /u02/database/CNMMBO/udump/cnmmbo_ora_10883.trc                                      
  51.                                                                                                                         
  52. SQL> SQL>                                                                                                               
  53. SQL> ho pwd                                                                                                             
  54. /users/oracle                                                                                                           
  55.                                                                                                                         
  56. --&gt使用tkprof工具格式化跟蹤檔案便於閱讀                                                                                 
  57. SQL> ho tkprof /u02/database/CNMMBO/udump/cnmmbo_ora_10883.trc /users/oracle/ins_t.txt explain=goex_admin/goex_admin    
  58.                                                                                                                         
  59. TKPROF: Release 10.2.0.3.0 - Production on Fri Sep 9 12:55:18 2011                                                      
  60.                                                                                                                         
  61. Copyright (c) 1982, 2005, Oracle.  All rights reserved.                                                                 
  62.                                                                                                                         
  63. SQL> ho cat /users/oracle/ins_t.txt  --&gt檢視跟蹤檔案                                                                    
  64. ......                                                                                                                  
  65. BEGIN ins_t(31,62); END;                                                                                                
  66. ......                                                                                                                  
  67. INSERT INTO T      --&gt可以看到insert into語句中使用了繫結變數                                                           
  68. VALUES                                                                                                                  
  69. (:B2 ,:B1 )                                                                                                             
  70.                                                                                                                         
  71. call     count       cpu    elapsed       disk      query    current        rows                                        
  72. ------- ------  -------- ---------- ---------- ---------- ----------  ----------                                        
  73. Parse        0      0.00       0.00          0          0          0           0                                        
  74. Execute      2      0.11       0.11          2        281         27           2                                        
  75. .......                                                                                                                 

    4、在動態SQL中是使用繫結變數    

  1. --&gt動態SQL中不能自動使用繫結變數,需要手動設定繫結變數                                                                   
  2. SQL> @get_parse     --&gt獲得當前hard parse解析情況,此時為120                                                             
  3.                                                                                                                          
  4. NAME                           VALUE                                                                                     
  5. ------------------------- ----------                                                                                     
  6. parse count (total)              533                                                                                     
  7. parse count (hard)               120                                                                                     
  8. parse count (failures)             1                                                                                     
  9.                                                                                                                          
  10. SQL> begin                                                                                                               
  11.   2  for i in 1..30 loop                                                                                                 
  12.   3  execute immediate 'insert into t values(:1,:2)' using i,i+i-2;  --&gt動態SQL使用繫結變數,該語句將執行30次            
  13.   4  end loop;                                                                                                           
  14.   5  commit;                                                                                                             
  15.   6  end;                                                                                                                
  16.   7  /                                                                                                                   
  17.                                                                                                                          
  18. PL/SQL procedure successfully completed.                                                                                 
  19.                                                                                                                          
  20. SQL> @get_parse     --&gt 動態SQL執行後,儘管執行了30次,但硬解析數量僅僅增加了2次                                         
  21.                                                                                                                          
  22. NAME                           VALUE                                                                                     
  23. ------------------------- ----------                                                                                     
  24. parse count (total)              537                                                                                     
  25. parse count (hard)               122                                                                                     
  26. parse count (failures)             1                                                                                     
  27.                                                                                                                          
  28. SQL> set serveroutput on;                                                                                                
  29. SQL> get get_sal.sql    --&gt下面的pl/sql中使用了繫結變數                                                                  
  30.   1   DECLARE                                                                                                            
  31.   2   TYPE emp_cur IS REF CURSOR;                                                                                        
  32.   3   my_emp_cur emp_cur;                                                                                                
  33.   4   my_emp_rec emp%ROWTYPE;                                                                                            
  34.   5   BEGIN                                                                                                              
  35.   6   OPEN my_emp_cur FOR 'select * from emp where deptno=:dno' USING 10;                                                
  36.   7   LOOP                                                                                                               
  37.   8   FETCH my_emp_cur INTO my_emp_rec;                                                                                  
  38.   9   EXIT WHEN my_emp_cur%NOTFOUND;                                                                                     
  39.  10   dbms_output.put_line(my_emp_rec.ename||'''s salary is : '||my_emp_rec.sal);                                        
  40.  11   END LOOP;                                                                                                          
  41.  12*  END;                                                                                                               
  42.  13  /                                                                                                                   
  43. CLARK's salary is : 4900                                                                                                 
  44. KING's salary is : 5000                                                                                                  
  45. MILLER's salary is : 1300                                                                                                
  46.                                                                                                                          
  47. PL/SQL procedure successfully completed.                                                                                 
  48.                                                                                                                          
  49. SQL> /                                                                                                                   
  50. CLARK's salary is : 4900                                                                                                 
  51. KING's salary is : 5000                                                                                                  
  52. MILLER's salary is : 1300                                                                                                
  53.                                                                                                                          
  54. PL/SQL procedure successfully completed.                                                                                 
  55.                                                                                                                          
  56. SQL> select sql_text,executions,sql_id from v$sqlarea where sql_text like 'select * from emp where deptno=:dno%';        
  57.                                                                                                                          
  58. SQL_TEXT                                      EXECUTIONS SQL_ID                                                          
  59. --------------------------------------------- ---------- -------------                                                   
  60. select * from emp where deptno=:dno                    2 c1nx6x02h655a                                                   

        
三、繫結變數的優缺點及使用場合
    優點:
        可以在library cache中共享遊標,避免硬解析以及與之相關的額外開銷
        在大批次資料操作時將呈數量級來減少閂鎖的使用,避免閂鎖的競爭
      
    缺點:
        繫結變數被使用時,查詢最佳化器會忽略其具體值,因此其預估的準確性遠不如使用字面量值真實,尤其是在表存在資料傾斜(表上的數
        據非均勻分佈)的列上會提供錯誤的執行計劃。從而使得非高效的執行計劃被使用。
    
    使用場合:
        OLTP
            在OLTP系統中SQL語句重複執行頻度高,但處理的資料量較少,結果集也相對較小,尤其是使用表上的索引來縮小中間結果集,其
            解析時間通常會接近或高於執行時間,因此該場合適合使用繫結變數。
        
        OLAP
            在OLAP系統中,SQL語句執行次數相對較少,但返回的資料量較大,因此多數情況下傾向於使用權標掃描更高效,其SQL語句執行時
            間遠高於其解析時間,因此使用繫結變數對於總響應時間影響不大。而且增加生成低效執行計劃的風險。即在在OLAP系統中使用字
            面量的效能高於使用繫結變數。
    
    注意:
        對於實際的資料庫物件,如(表,檢視,列等),不能使用繫結變數替換,只能替換字面量。如果物件名是在執行時生成的,則需要對其
        用字串拼接,同時,sql只會匹配已經在共享池中相同的物件名。

 

四、相關參考

    Oracle 硬解析與軟解析    
    父遊標、子游標與共享遊標
    啟用使用者程式跟蹤
    PL/SQL --&gt 動態SQL 
    PL/SQL --&gt 動態SQL的常見錯誤

--&gt>轉載於:http://blog.csdn.net/leshami/article/details/6904229

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/29119536/viewspace-1243324/,如需轉載,請註明出處,否則將追究法律責任。

相關文章