【DB筆試面試585】在Oracle中,什麼是常規游標共享?

  • 2019 年 10 月 10 日
  • 筆記

題目部分

在Oracle中,什麼是常規游標共享?

答案部分

游標共享(Cursor Sharing)是指共享游標(Shared Cursor)之間的共享,游標共享可以實現重用存儲在子游標(Child Cursor)中的解析樹和執行計劃而不用從頭開始做硬解析,從而提高系統性能。特別對於同一類型的目標SQL更應該實現游標共享,而使用綁定變數就可以實現游標共享。

由於很多OLTP類型的應用系統的開發人員在開發階段並未意識到硬解析的危害,所以也沒有使用綁定變數,等到系統上線後才發現問題。此時若要使用綁定變數,則意味著絕大多數SQL都得改寫,但這個代價就太大了,所以Oracle引入了常規游標共享。

即使應用系統在開發階段使用了綁定變數,但在默認情況下也會受到綁定變數窺探的影響。綁定變數窺探的副作用在於,一旦啟用(默認情況下綁定變數窺探就已經被啟用),使用了綁定變數的目標SQL就只會沿用之前硬解析時所產生的解析樹和執行計劃,即使這種沿用完全不適合當前的情形,為了解決這個問題,Oracle引入了自適應游標共享。

先介紹一下與本小節相關的幾個概念:

l 安全的謂詞條件是指如果一個謂詞條件所在的目標SQL的執行計劃並不隨該謂詞條件的輸入值的變化而變化,那麼該謂詞條件就是安全的。比如,對於主鍵列施加等值查詢的謂詞條件,無論傳入的主鍵值是什麼,其執行計劃都會是固定的,不會變化。

l 不安全的謂詞條件是指如果目標SQL的執行計劃可能會隨著謂詞條件的輸入值的不同而發生變化,那麼該謂詞條件就是一個不安全的謂詞條件。Oracle資料庫中典型的不安全的謂詞條件有範圍查詢(使用了>、>=、<、<=、BETWEEN的謂詞條件),使用了帶通配符(%)的LIKE,以及對有直方圖統計資訊的目標列施加的等值查詢等。

l 同一類型SQL是指除SQL文本中對應的輸入值不同外,其它部分都一模一樣的目標SQL,例如,「SELECT ENAME FROM EMP WHERE EMPNO=7369」和「SELECT ENAME FROM EMP WHERE EMPNO=7370」就是同一類型的SQL。

下面分別來介紹常規游標共享和自適應游標共享這兩個方面。

1、 常規游標共享

常規游標共享是在Oracle 8i中引入的。常規游標共享可以做到既有效降低系統硬解析的數量,又對應用透明,即常規游標共享可以做到在應用不改一行程式碼的情況下,使那些僅僅是SQL文本中的WHERE條件或者INSERT語句的VALUES子句中的具體輸入值不同的目標SQL彼此之間共享解析樹和執行計劃。當開啟了常規游標共享後,Oracle在實際解析目標SQL之前,會先用系統產生的綁定變數來替換目標SQL的SQL文本中WHERE條件或者INSERT中的VALUES子句中的具體輸入值,這樣替換後實際執行的SQL就己經是使用了綁定變數的改寫後的等價SQL。Oracle資料庫里系統產生的綁定變數的命名規則是「:"SYS_B_n"(n=0,1,2,……)」。例如,原目標SQL為「SELECT ENAME FROM EMP WHERE EMPNO=7369」,如果開啟了常規游標共享,那麼Oracle做替換後的等價改寫形式就是「SELECT ENAME FROM EMP WHERE EMPNO=:"SYS_B_0"」。

Oracle資料庫中的常規游標共享受參數CURSOR_SHARING的控制,其值可以被設置為EXACT、SIMILAR或FORCE,它們各自的含義如下所示:

l EXACT表示Oracle不會用系統產生的綁定變數來替換目標SQL的SQL文本中WHERE條件或者INSERT語句的VALUES子句中的具體輸入值,EXACT是CURSOR_SHARING的默認值。

l SIMILAR表示Oracle會用系統產生的綁定變數來替換目標SQL的SQL文本中WHERE條件或者INSERT語句的VALUES子句中的具體輸入值。在這種情況下,Oracle只會對那些它認為是安全的謂詞條件在替換後重用解析樹和執行計劃,對於它認為的不安全的謂詞條件,即便用系統產生的綁定變數替換後的SQL文本是一模一樣的,對於每一個不同的輸入值,Oracle都會執行一次硬解析,即此時會出現一個Parent Cursor下掛一堆Child Cursor的現象,而這些Child Cursor中存儲的解析樹和執行計劃很可能是一樣的(需要注意的是,因為自適應游標共享的引入,這種行為不再適用於Oracle 11g及其後續的版本)。在Oracle 12c以及後續的版本中SIMILAR將過時,不再被繼續支援。因為當CURSOR_SHARING設成SIMILAR後會帶來一系列的問題,並且有太多與SIMILAR相關的Bug。

l FORCE和SIMILAR一樣,FORCE表示Oracle會用系統產生的綁定變數來替換目標SQL的SQL文本中WHERE條件或者INSERT語句的VALUES子句中的具體輸入值。但和SIMILAR不同的是,當CURSOR_SHARING的值為FORCE時,替換後同一類型的SQL總是會無條件地重用之前硬解析時的解析樹和執行計劃(需要注意的是,因為自適應游標共享的引入,這種行為不再適用於Oracle 11g及其後續的版本)。

下面給出一些游標不能共享的示例:

第一組,表名大小寫,空格不同:

--第一組,表名大小寫,空格不同:  ① select * from emp;  ② select * from Emp; --表名大小寫不同  ③ select * from  EMP;--FROM後比①多了1個空格    --第二組,輸入值不同:  ① select * from emp where empno=7369;--輸入值不同  ② select * from emp where empno=7788;--輸入值不同    --第三組,表所屬用戶不同:  ① lhr66@lhrdb> select * from t_lhr;--t_lhr屬於lhr66用戶  ② scott@lhrdb> select * from t_lhr;--t_lhr屬於scott用戶    --下面給出一個與常規游標共享有關的示例(資料庫版本為10.2.0.1):  --準備相關的表並收集統計資訊:  CREATE TABLE T_CS_20170610 AS SELECT * FROM DBA_OBJECTS;  CREATE INDEX IDX_OBJ_LHR  ON  T_CS_20170610(OBJECT_ID);  EXEC DBMS_STATS.GATHER_TABLE_STATS(USER,'T_CS_20170610',ESTIMATE_PERCENT => 100,CASCADE => TRUE,METHOD_OPT => 'FOR ALL COLUMNS SIZE 1',NO_INVALIDATE => FALSE);--不收集直方圖    --查詢:  SYS@ora10g> show parameter cursor_sharing    NAME                                 TYPE        VALUE  ------------------------------------ ----------- ------------------------------  cursor_sharing                       string      EXACT    SYS@ora10g> SELECT COUNT(*) FROM T_CS_20170610 WHERE OBJECT_ID=0;      COUNT(1)  ----------           0    SYS@ora10g> SELECT COUNT(*) FROM T_CS_20170610 WHERE OBJECT_ID=1;      COUNT(1)  ----------           0    SYS@ora10g> SELECT SQL_TEXT,SQL_ID,VERSION_COUNT,EXECUTIONS FROM V$SQLAREA WHERE SQL_TEXT LIKE 'SELECT COUNT(*) FROM T_CS_20170610 WHERE OBJECT_ID=%';    SQL_TEXT                                                     SQL_ID        VERSION_COUNT EXECUTIONS  ------------------------------------------------------------ ------------- ------------- ----------  SELECT COUNT(*) FROM T_CS_20170610 WHERE OBJECT_ID=1         gbkpakaxfmbm4             1          1  SELECT COUNT(*) FROM T_CS_20170610 WHERE OBJECT_ID=0         f9uyh6hyf7kcc             1          1  

現在CURSOR_SHARING的值為EXACT,所以Oracle不會用系統產生的綁定變數來替換上述SQL的WHERE條件中的輸入值,而上述兩個SQL的WHERE條件中的輸入值並不相同(一個是0,另一個是1),即意味著這兩個SQL在執行時均會使用硬解析。

對於上述兩個SQL而言,其謂詞條件均為「OBJECT_ID=XXX」,這是一個等值查詢條件,同時目標列OBJECT_ID上沒有直方圖統計資訊,所以該謂詞條件是一個安全的謂詞條件。也就是說,如果把CURSOR_SHARING的值改為SIMILAR後再次執行這兩個SQL,那麼Oracle就會用系統產生的綁定變數來替換上述謂詞條件中的輸入值,這意味著當執行「SELECT COUNT(1) FROM T_CS_20170610 WHERE OBJECT_ID=1」時,Oracle會沿用之前執行「SELECT COUNT(1) FROM T_CS_20170610 WHERE OBJECT_ID=0」所對應的解析樹和執行計劃。

下面把CURSOR_SHARING修改為SIMILAR:

SYS@ora10g> ALTER SESSION SET CURSOR_SHARING='SIMILAR';    Session altered.    SYS@ora10g> ALTER SYSTEM FLUSH SHARED_POOL;    System altered.    SYS@ora10g> SELECT COUNT(*) FROM T_CS_20170610 WHERE OBJECT_ID=0;      COUNT(*)  ----------           0    SYS@ora10g> SELECT COUNT(*) FROM T_CS_20170610 WHERE OBJECT_ID=1;      COUNT(*)  ----------           0    SYS@ora10g> col sql_text format a80  SYS@ora10g> SELECT SQL_TEXT,SQL_ID,VERSION_COUNT,EXECUTIONS FROM V$SQLAREA WHERE SQL_TEXT LIKE 'SELECT COUNT(*) FROM T_CS_20170610 WHERE OBJECT_ID=%';    SQL_TEXT                                                                         SQL_ID        VERSION_COUNT EXECUTIONS  -------------------------------------------------------------------------------- ------------- ------------- ----------  SELECT COUNT(*) FROM T_CS_20170610 WHERE OBJECT_ID=:"SYS_B_0"                    bgzgahgcxyss7             1          2  

注意,列VERSION_COUNT的值為1,列EXECUTIONS的值為2,這說明在目標SQL的謂詞條件是安全的謂詞條件,且CURSOR_SHARING的值為SIMILAR的前提條件下,Oracle確實會重用之前硬解析時所對應的解析樹和執行計劃。

由於上述兩個SQL的謂詞條件是安全的謂詞條件,因此把CURSOR_SHARING的值改為SIMILAR或者FORCE並沒有什麼區別,即如果把CURSOR_SHARING的值改為FORCE後再次執行這兩個SQL,所得到的結果應該和CURSOR SHARING的值為SIMILAR時一樣。

來驗證一下把CURSOR_SHARING的值改為FORCE,並再次執行這兩個SQL:

SYS@ora10g> ALTER SESSION SET CURSOR_SHARING='FORCE';    Session altered.    SYS@ora10g> ALTER SYSTEM FLUSH SHARED_POOL;    System altered.    SYS@ora10g> SELECT COUNT(*) FROM T_CS_20170610 WHERE OBJECT_ID=0;      COUNT(*)  ----------           0    SYS@ora10g> SELECT COUNT(*) FROM T_CS_20170610 WHERE OBJECT_ID=1;      COUNT(*)  ----------           0    SYS@ora10g> col sql_text format a80  SYS@ora10g> SELECT SQL_TEXT,SQL_ID,VERSION_COUNT,EXECUTIONS FROM V$SQLAREA WHERE SQL_TEXT LIKE 'SELECT COUNT(*) FROM T_CS_20170610 WHERE OBJECT_ID=%';    SQL_TEXT                                                                         SQL_ID        VERSION_COUNT EXECUTIONS  -------------------------------------------------------------------------------- ------------- ------------- ----------  SELECT COUNT(*) FROM T_CS_20170610 WHERE OBJECT_ID=:"SYS_B_0"                    bgzgahgcxyss7             1          2  

現在再來看在不安全的謂詞條件下當CURSOR_SHARING的值分別為EXACT、SIMILAR和FORCE時的對比。還是先來看CURSOR_SHARING的值為EXACT的情形:

SYS@ora10g> ALTER SESSION SET CURSOR_SHARING=EXACT;    Session altered.    SYS@ora10g> ALTER SYSTEM FLUSH SHARED_POOL;    System altered.    SYS@ora10g> SELECT COUNT(*) FROM T_CS_20170610 WHERE OBJECT_ID BETWEEN 0 AND 1;      COUNT(*)  ----------           0    SYS@ora10g> SELECT COUNT(*) FROM T_CS_20170610 WHERE OBJECT_ID BETWEEN 1 AND 2;      COUNT(*)  ----------           1    SYS@ora10g> SELECT SQL_TEXT,SQL_ID,VERSION_COUNT,EXECUTIONS FROM V$SQLAREA WHERE SQL_TEXT LIKE 'SELECT COUNT(*) FROM T_CS_20170610 WHERE OBJECT_ID%';    SQL_TEXT                                                                         SQL_ID        VERSION_COUNT EXECUTIONS  -------------------------------------------------------------------------------- ------------- ------------- ----------  SELECT COUNT(*) FROM T_CS_20170610 WHERE OBJECT_ID BETWEEN 1 AND 2               g6ygwtg4482r3             1          1  SELECT COUNT(*) FROM T_CS_20170610 WHERE OBJECT_ID BETWEEN 0 AND 1               7b5sugy5n62gq             1          1    --下面把CURSOR_SHARING修改為SIMILAR:  SYS@ora10g> ALTER SESSION SET CURSOR_SHARING=SIMILAR;    Session altered.    SYS@ora10g> ALTER SYSTEM FLUSH SHARED_POOL;    System altered.    SYS@ora10g> SELECT COUNT(*) FROM T_CS_20170610 WHERE OBJECT_ID BETWEEN 0 AND 1;      COUNT(*)  ----------           0    SYS@ora10g> SELECT COUNT(*) FROM T_CS_20170610 WHERE OBJECT_ID BETWEEN 1 AND 2;      COUNT(*)  ----------           1    --這裡若不能使用常規游標共享,則可以多清理幾次共享池,另外,執行SQL查詢時中間間隔稍微長一點。  SYS@ora10g> SELECT SQL_TEXT,SQL_ID,VERSION_COUNT,EXECUTIONS FROM V$SQLAREA WHERE SQL_TEXT LIKE 'SELECT COUNT(*) FROM T_CS_20170610 WHERE OBJECT_ID%';    SQL_TEXT                                                                                 SQL_ID        VERSION_COUNT EXECUTIONS  ---------------------------------------------------------------------------------------- ------------- ------------- ----------  SELECT COUNT(*) FROM T_CS_20170610 WHERE OBJECT_ID BETWEEN :"SYS_B_0" AND :"SYS_B_1"     21371b4zdvrkg             2          2    SYS@ora10g> SELECT SQL_TEXT,SQL_ID,D.CHILD_NUMBER,EXECUTIONS,PLAN_HASH_VALUE FROM V$SQL D WHERE SQL_ID = '21371b4zdvrkg';    SQL_TEXT                                                                                 SQL_ID        CHILD_NUMBER EXECUTIONS PLAN_HASH_VALUE  ---------------------------------------------------------------------------------------- ------------- ------------ ---------- ---------------  SELECT COUNT(*) FROM T_CS_20170610 WHERE OBJECT_ID BETWEEN :"SYS_B_0" AND :"SYS_B_1"     21371b4zdvrkg            0          1      3299589416  SELECT COUNT(*) FROM T_CS_20170610 WHERE OBJECT_ID BETWEEN :"SYS_B_0" AND :"SYS_B_1"     21371b4zdvrkg            1          1      3299589416    --上述兩個Child Cursor所對應的列PLAN_HASH_VALUE的值均為3299589416,說明雖然這裡確實產生了兩個Child Cursor,但它們存儲的執行計劃卻是相同的。從如下顯示內容可以看到,這兩個Child Cursor中存儲的執行計劃確實是相同的(走的均是對索引IDX_OBJ_LHR的索引範圍掃描):  SYS@ora10g> SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR('21371b4zdvrkg',0,'advanced'));    PLAN_TABLE_OUTPUT  ------------------------------------------------------  SQL_ID  21371b4zdvrkg, child number 0  -------------------------------------  SELECT COUNT(*) FROM T_CS_20170610 WHERE OBJECT_ID BETWEEN :"SYS_B_0" AND :"SYS_B_1"    Plan hash value: 3299589416    ----------------------------------------------------------------------------------  | Id  | Operation          | Name        | Rows  | Bytes | Cost (%CPU)| Time     |  ----------------------------------------------------------------------------------  |   0 | SELECT STATEMENT   |             |       |       |     2 (100)|          |  |   1 |  SORT AGGREGATE    |             |     1 |     5 |            |          |  |*  2 |   FILTER           |             |       |       |            |          |  |*  3 |    INDEX RANGE SCAN| IDX_OBJ_LHR |     1 |     5 |     2   (0)| 00:00:01 |  ----------------------------------------------------------------------------------    Query Block Name / Object Alias (identified by operation id):  -------------------------------------------------------------       1 - SEL$1     3 - SEL$1 / T_CS_20170610@SEL$1    Outline Data  -------------      /*+        BEGIN_OUTLINE_DATA        IGNORE_OPTIM_EMBEDDED_HINTS        OPTIMIZER_FEATURES_ENABLE('10.2.0.1')        ALL_ROWS        OUTLINE_LEAF(@"SEL$1")        INDEX(@"SEL$1" "T_CS_20170610"@"SEL$1" ("T_CS_20170610"."OBJECT_ID"))        END_OUTLINE_DATA    */    Peeked Binds (identified by position):  --------------------------------------       1 - :SYS_B_0 (NUMBER): 0     2 - :SYS_B_1 (NUMBER): 1    Predicate Information (identified by operation id):  ---------------------------------------------------       2 - filter(:SYS_B_0<=:SYS_B_1)     3 - access("OBJECT_ID">=:SYS_B_0 AND "OBJECT_ID"<=:SYS_B_1)    Column Projection Information (identified by operation id):  -----------------------------------------------------------       1 - (#keys=0) COUNT(*)[22]      52 rows selected.    SYS@ora10g>  SYS@ora10g> SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR('21371b4zdvrkg',1,'advanced'));    PLAN_TABLE_OUTPUT  ----------------------------------------------------------------  SQL_ID  21371b4zdvrkg, child number 1  -------------------------------------  SELECT COUNT(*) FROM T_CS_20170610 WHERE OBJECT_ID BETWEEN :"SYS_B_0" AND  :"SYS_B_1"    Plan hash value: 3299589416    ----------------------------------------------------------------------------------  | Id  | Operation          | Name        | Rows  | Bytes | Cost (%CPU)| Time     |  ----------------------------------------------------------------------------------  |   0 | SELECT STATEMENT   |             |       |       |     2 (100)|          |  |   1 |  SORT AGGREGATE    |             |     1 |     5 |            |          |  |*  2 |   FILTER           |             |       |       |            |          |  |*  3 |    INDEX RANGE SCAN| IDX_OBJ_LHR |     1 |     5 |     2   (0)| 00:00:01 |  ----------------------------------------------------------------------------------    Query Block Name / Object Alias (identified by operation id):  -------------------------------------------------------------       1 - SEL$1     3 - SEL$1 / T_CS_20170610@SEL$1    Outline Data  -------------      /*+        BEGIN_OUTLINE_DATA        IGNORE_OPTIM_EMBEDDED_HINTS        OPTIMIZER_FEATURES_ENABLE('10.2.0.1')        ALL_ROWS        OUTLINE_LEAF(@"SEL$1")        INDEX(@"SEL$1" "T_CS_20170610"@"SEL$1" ("T_CS_20170610"."OBJECT_ID"))        END_OUTLINE_DATA    */    Peeked Binds (identified by position):  --------------------------------------       1 - :SYS_B_0 (NUMBER): 1     2 - :SYS_B_1 (NUMBER): 2    Predicate Information (identified by operation id):  ---------------------------------------------------       2 - filter(:SYS_B_0<=:SYS_B_1)     3 - access("OBJECT_ID">=:SYS_B_0 AND "OBJECT_ID"<=:SYS_B_1)    Column Projection Information (identified by operation id):  -----------------------------------------------------------       1 - (#keys=0) COUNT(*)[22]      52 rows selected.  

這是很不合理的,也是CURSOR_SHARING的值被設為SIMILAR後的主要弊端之一。將CURSOR_SHARING的值設為SIMILAR的目的是想在應用不改一行程式碼的情形下,使那些僅僅是SQL文本中的WHERE條件或者VALUES子句(適用於INSERT語句)的具體輸入值不同的目標SQL彼此之間共享解析樹和執行計劃,以達到有效降低系統硬解析數量的目的。但在Oracle l1g之前,CURSOR_SHARING的值被設為SIMILAR後你可能會發現這麼做的效果有限,系統硬解析的數量並未得到大幅度的降低,而且會出現一個Parent Cursor下掛一堆Child Cursor的現象,而這些Child Cursor中存儲的解析樹和執行計劃很可能是一樣的。

以上述兩個SQL為例,在當前條件下,「SELECT COUNT(*) FROM T_CS_20170610 WHERE OBJECT_ID BETWEEN 0 AND 1」和「SELECT COUNT(*) FROM T_CS_20170610 WHERE OBJECT_ID BETWEEN 1 AND 2」的執行計劃是一樣的,顯然它們本應共享解析樹和執行計劃,但就是因為CURSOR_SHARING的值被設為SIMILAR,外加這兩個SQL使用的是不安全的謂詞條件,所以就導致Oracle在執行它們時均使用了硬解析。

在Oracle 10g及其後續的版本中,Oracle會自動收集直方圖統計資訊,這意味著在Oracle 10g及其後續的版本中出現不安全的謂詞條件的概率要大大高於Oracle 10g之前的版本,所以在Oracle 10g里不要將CURSOR_SHARING的值設成SIMILAR,因為很可能達不到在不改一行應用程式碼的情形下有效降低系統硬解析數量的目的(更何況還可能會因此而引入一堆Bug)。

在Oracle 11g里也不要將CURSOR_SHARING的值設成SIMILAR,因為在Oracle 11g里自適應游標共享已經被默認啟用了,在自適應游標共享被啟用的情形下,Oracle並不推薦將CURSOR_SHARING的值設為SIMILAR(參見MOS上的文章"FAQ:Adaptive Cursor Shanng(ACS)Frequently Asked Questions(ID 1518681.1)」。

再次執行SQL語句「SELECT COUNT(*) FROM T_CS_20170610 WHERE OBJECT_ID BETWEEN 1 AND 2;」:

SYS@ora10g> SELECT COUNT(*) FROM T_CS_20170610 WHERE OBJECT_ID BETWEEN 1 AND 2;      COUNT(*)  ----------           1    SYS@ora10g> SELECT SQL_TEXT,SQL_ID,VERSION_COUNT,EXECUTIONS FROM V$SQLAREA WHERE SQL_TEXT LIKE 'SELECT COUNT(*) FROM T_CS_20170610 WHERE OBJECT_ID%';    SQL_TEXT                                                                                 SQL_ID        VERSION_COUNT EXECUTIONS  ---------------------------------------------------------------------------------------- ------------- ------------- ----------  SELECT COUNT(*) FROM T_CS_20170610 WHERE OBJECT_ID BETWEEN :"SYS_B_0" AND :"SYS_B_1"     21371b4zdvrkg             2          3  

從查詢結果可以看到列VERSION_COUNT的值還是2,但列EXECUTIONS的值己經從之前的2變為現在的3,說明在目標SQL的謂詞條件是不安全的謂詞條件,且CURSOR_SHARING的值為SIMILAR的前提條件下,只有針對該謂詞條件的當前輸入值和之前的輸入值完全相同時,Oracle才會重用之前該輸入值所對應的解析樹和執行計劃。

上述兩個SQL的謂詞條件雖然是不安全的,但不管是「安全的謂詞條件」還是「不安全的謂詞條件」,當把CURSOR_SHARING的值設為FORCE後,Oracle總是會無條件重用目標SQL之前硬解析時的解析樹和執行計劃(僅適用於Oracle 11g之前的版本)。所以如果把CURSOR_SHARING的值設為FORCE後再次執行這兩個SQL,那麼得到的結果應和之前CURSOR_SHARING的值為SIMILAR時不同。

SYS@ora10g> ALTER SESSION SET CURSOR_SHARING=FORCE;    Session altered.    SYS@ora10g> ALTER SYSTEM FLUSH SHARED_POOL;    System altered.    SYS@ora10g> ALTER SYSTEM FLUSH SHARED_POOL;    System altered.    SYS@ora10g> ALTER SYSTEM FLUSH SHARED_POOL;    System altered.    SYS@ora10g> SELECT COUNT(*) FROM T_CS_20170610 WHERE OBJECT_ID BETWEEN 0 AND 1;      COUNT(*)  ----------           0    SYS@ora10g> SELECT COUNT(*) FROM T_CS_20170610 WHERE OBJECT_ID BETWEEN 1 AND 2;      COUNT(*)  ----------           1    SYS@ora10g> SELECT SQL_TEXT,SQL_ID,VERSION_COUNT,EXECUTIONS FROM V$SQLAREA WHERE SQL_TEXT LIKE 'SELECT COUNT(*) FROM T_CS_20170610 WHERE OBJECT_ID%';    SQL_TEXT                                                                                 SQL_ID        VERSION_COUNT EXECUTIONS  ---------------------------------------------------------------------------------------- ------------- ------------- ----------  SELECT COUNT(*) FROM T_CS_20170610 WHERE OBJECT_ID BETWEEN :"SYS_B_0" AND :"SYS_B_1"     21371b4zdvrkg             1          2  

上述顯示內容中列VERSION_COUNT的值為1,列EXECUTIONS的值為2,說明即使目標SQL的謂詞條件是不安全的,只要CURSOR_SHARING的值為FORCE,那麼Oracle就會無條件地重用之前硬解析時對應的解析樹和執行計劃(僅適用於Oracle l1g之前的版本)。

從上述整個測試過程可以得到如下結論。

l SIMILAR是一個即將過時的值,它有太多的副作用,無論什麼時候都不要將CURSOR_SHARING的值設為SIMILAR。

l 如果想在不改一行應用程式碼的情況下,使那些僅僅是SQL文本中的WHERE條件或者VALUES子句(適用於INSERT語句)中的具體輸入值不同的目標SQL共享解析樹和執行計劃,以達到有效降低系統硬解析數量的目的,那就將CURSOR_SHARING的值設成FORCE吧,雖然這不是最理想的方案(最理想的方案當然還是修改應用的程式碼,在SQL語句里使用綁定變數,並且儘可能使用批量綁定),但這也許是最省事的方案。

本文選自《Oracle程式設計師面試筆試寶典》,作者:李華榮。