當第一次使用梆定變量爲字符串類型,並且小於32bytes時,oracle捕獲這個梆定變量,並改寫該梆定變量的字符串長度爲32Bytes。如果第二次再使用該sql和梆定變量,如果梆定變量長度超過32字節小於128字節,則oracle會自動將該梆定變量長度改爲128字節,並且重新生成一個子遊標和執行計劃,造成相同梆定變量的sql的執行計劃不能共享,需要重新生成解析樹和執行計劃(即是硬解析),過多的硬解析是萬惡之源,會佔用大量的CPU、IO和內存資源,造成系統性能瓶頸。所以使用相同SQL的相同梆定變量時,要特別注意每次該梆定變量的長度是否在符合軟解析的區間長度內。
測試結果如下:
1)、創建一個10個字節可變長度的字符串變量 v:
23:24:15SYS@orcl*SQL> var v varchar2(10);
23:24:26SYS@orcl*SQL> exec :v :='abcd';
23:24:26SYS@orcl*SQL> print v;
V
----------
abcd
2)、將變量 v 梆定到 sql 的查詢謂詞條件值中,並執行:
23:24:27SYS@orcl*SQL> select * from tuser.testbind where test=:v;
TEST
----------
abcd
3)、查詢上面執行的sql在 Library Cache (Sharedpool共享池中的庫緩存)中生成遊標和執行計劃的 sql_id,並注意子游標號CHILD_NUMBER是0(第一個子游標,即第一個執行計劃版本):
23:24:32SYS@orcl*SQL> select sql_id,sql_text,child_number from v$sql where sql_textlike 'select * from tuser.testbind where test=:v%';
SQL_ID SQL_TEXT CHILD_NUMBER
-------------------------------------------------------------------------------------- ------------------
13gr9g2810v9c select * from tuser.testbindwhere test=:v 0
4)、由於是第一次執行第2步的sql,並且梆定變量長度還沒有變化,所以如下顯示的梆定變量長度已更改字段(BIND_LENGTH_UPGRADEABLE)爲No:
23:24:38SYS@orcl*SQL> select sql_id,child_number,BIND_LENGTH_UPGRADEABLE fromv$sql_shared_cursor
23:24:50 2 where sql_id='13gr9g2810v9c';
SQL_ID CHILD_NUMBERBIND_LENGTH_UPGRADEABLE
-------------------------------------------- ------------------------------
13gr9g2810v9c 0 N
5)、查詢捕獲的梆定變量信息,DATATYPE_STRING字段列自動將小於32字節的梆定變量更改爲32字節:
23:25:03SYS@orcl*SQL> select position,LAST_CAPTURED,datatype_string,value_string
23:25:04 2 from v$sql_bind_capture
23:25:04 3 where sql_id='13gr9g2810v9c';
NAME POSITION LAST_CAPTURED DATATYPE_STRING VALUE_STRING
----------- -------- ----------------------------------------- -------------------------
:V 1 2017-04-14 23:24:31 VARCHAR2(32) abcd
6)、再將梆定變量 v 的長度更改爲超過32字節,如下33字節:
23:25:05SYS@orcl*SQL>
23:25:09SYS@orcl*SQL> var v varchar2(33);
23:25:18SYS@orcl*SQL> exec :v :='abcd';
23:25:18SYS@orcl*SQL> print v;
V
----------
abcd
7)、再次執行以上第二步的sql:
23:25:19SYS@orcl*SQL> select * from tuser.testbind where test=:v;
TEST
----------
abcd
8)、和以上第3步對照,再次查詢 Library Cache 中的 v$sql字典視圖,發現生成多了一個子遊標(CHILD_NUMBER=1),也就是發生硬解析,重新生成了新的執行計劃:
23:25:23SYS@orcl*SQL> select sql_id,sql_text,child_number from v$sql where sql_textlike 'select * from tuser.testbind where test=:v%';
SQL_ID SQL_TEXT CHILD_NUMBER
-------------------------------------------------------------------------------------- ------------------
13gr9g2810v9c select * from tuser.testbindwhere test=:v 0
13gr9g2810v9c select * from tuser.testbindwhere test=:v 1
9)、和以上第4步對照,再次查詢可以發現,oracle已經捕獲到梆定變量 v 的長度相比之前發生了變化:
23:25:39SYS@orcl*SQL> select sql_id,child_number,BIND_LENGTH_UPGRADEABLE fromv$sql_shared_cursor
23:25:40 2 where sql_id='13gr9g2810v9c';
SQL_ID CHILD_NUMBERBIND_LENGTH_UPGRADEABLE
-------------------------------------------- ------------------------------
13gr9g2810v9c 0 N
13gr9g2810v9c 1 Y
10)、oracle 自動將梆定變量 v 的長度改爲128字節,第二次定義時是33字節(見第6步)
23:25:40SYS@orcl*SQL> select position,LAST_CAPTURED,datatype_string,value_string
23:25:46 2 from v$sql_bind_capture
23:25:46 3 where sql_id='13gr9g2810v9c';
POSITION LAST_CAPTURED DATATYPE_STRING VALUE_STRING
------------------------------------- ------------------------------------------------------------
1 2017-04-14 23:25:23VARCHAR2(128) abcd
1 2017-04-14 23:24:31VARCHAR2(32) abcd
由此可見,雖然使用梆定變量能讓相同的SQL,不同的條件值可以走上相同的執行計劃,從而避免過多的硬解析,不會消耗過多的資源,但也要注意梆定變量長度的變化,否則使用了梆定變量也可能會產生硬解析,造成資源浪費。
(完)