视图v$sql,v$sqlarea,v$sqltext,v$sqltext_with_newlines 是几个经常容易混淆的视图,主要是提供library cache中当前缓存的sql语句的信息。这几个视图都可以提供当前有关sql语句的具体信息,但稍有差异。本文主要描述其差异并给出实例。

 

一、sql语句与游标

      sql语句,这个没什么好说的,就是按照sql标准书写的sql语句

      游标,包含shared cursor,session cursor,简单点来理解,一条sql语句对应一个或多个游标,且一条sql语句至少解析为一个游标。

      当任一sql语句被解析到shared_pool中之后,必定会产生相应的游标,有下列三种情形,

          a、存在可完全共享的父游标

          b、父游标存在,但是由于执行环境的变化,不得不生存新的子游标

          c、没有父游标存在,需要生成全新的游标

      对于情形a,由于存在可共享的父游标,也就是说v$sql中必定已经存在一个对应的sql游标,我们可以查询到,执行之后对应executions及相关列会发生变化。

      对于情形b或c,sql语句产生的游标会被添加到v$sql视图,也即是新增游标(b为新增子游标,c为新增父游标)。

      注:在shared_pool由于aged out原则后的sql可能无法在该视图查询到,这个是另外一个话题。

 

二、视图差异

1、v$sql视图

      假定用户A与用户B都基于自身schema创建了表t

      用户A发布查询select * from t,此时共享池中产生一条与该语句的相关的sql游标,在v$sql视图体现(假定为首次执行)

      不久用户B也发出select * from t的查询,同上,v$sql中也对应有一条该语句的游标

      为了便于理解,我们将v$sql视图中的sql文本称之为游标,将v$sqlarea中的sql文本称为sql语句

2、v$sqlarea

      对于上述情形

      此时v$sqlarea则是对视图v$sql的一个聚合,也即是相当于对视图v$sql使用了distinct关键字。

      尽管v$sql中出现了两条select * from t,而v$sqlarea仅为一条

      v$sqlarea提供的是每条sql语句执行的汇总信息

3、v$sqltext

      该视图包括Shared pool中SQL语句的完整文本,但一条SQL语句是被分成多个块来进行保存的。

      对于比较短的sql语句,一个piece就搞定,对于比较长的sql语句则需要多个piece的结合来完整展现一条真正的sql语句。

4、v$sqltext_with_newlines

      该视图用于完整保存SQL语句所有字符,保留SQL语句的回车和制表符。

 

三、示例说明



  1. 1、创建演示环境  
  2. SQL> select * from v$version where rownum<2;  
  3.   
  4. BANNER  
  5. --------------------------------------------------------------------------------  
  6. Oracle Database 11g Enterprise Edition Release 11.2.0.1.0 - 64bit Production  
  7.   
  8. SQL> create table t ( x varchar2(30) primary key, y int );  
  9.   
  10. SQL> exec dbms_stats.set_table_stats('SCOTT','T', numrows => 1000000, numblks=>100000);  
  11.   
  12. SQL> alter system flush shared_pool;  
  13.   
  14. SQL> select sql_text from v$sql where upper(sql_text) like 'SELECT % T LOOK_FOR_ME%'  
  15.   2  and upper(sql_text) not like '%FROM V$SQL%';  
  16.   
  17. no rows selected  
  18.   
  19. 2、产生sql游标  
  20. SQL> declare  
  21.   2     l_x_number      number;  
  22.   3     l_x_string  varchar2(30);  
  23.   4  begin  
  24.   5     execute immediate 'alter session set optimizer_mode=all_rows';  
  25.   6     for x in (select * from t look_for_me where x = l_x_number) loop null; end loop;  
  26.   7     for x in (select * from t look_for_me where x = l_x_string) loop null; end loop;  
  27.   8     execute immediate 'alter session set optimizer_mode=first_rows';  
  28.   9     for x in (select * from t look_for_me where x = l_x_number) loop null; end loop;  
  29.  10     for x in (select * from t look_for_me where x = l_x_string) loop null; end loop;  
  30.  11  end;  
  31.  12  /  
  32.   
  33. PL/SQL procedure successfully completed.  
  34.   
  35. 3、分析   
  36. SQL> col sql_text format a55  
  37. SQL> set linesize 180  
  38. SQL> col plan_table_output format a80 truncate  
  39. SQL> col sql_id new_val sql_id  
  40. SQL> select sql_id, sql_text from v$sql where upper(sql_text) like 'SELECT % T LOOK_FOR_ME %B1_';  
  41.   
  42. SQL_ID        SQL_TEXT  
  43. ------------- -------------------------------------------------------  
  44. 1qqtru155tyz8 SELECT * FROM T LOOK_FOR_ME WHERE X = :B1  
  45. 1qqtru155tyz8 SELECT * FROM T LOOK_FOR_ME WHERE X = :B1  
  46. 1qqtru155tyz8 SELECT * FROM T LOOK_FOR_ME WHERE X = :B1  
  47. 1qqtru155tyz8 SELECT * FROM T LOOK_FOR_ME WHERE X = :B1  
  48.   
  49. --从上面的查询可知,对于上面的SQL语句我们得到了相同的SQL_ID。这是因为SQL_ID是由SQL文本hash得到的一个值  
  50. --只要SQL文本相同(完全相同),则SQL_ID一定是相同的。  
  51.   
  52. --我们从v$sql视图里边查询得到了四条相同sql_id的sql语句,也即是四个不同的游标  
  53. --为什么同样的sql文本产生了四个不同的游标呢?这是因为:  
  54. -- cursor 1) 使用ALL_ROWS 优化器模式, 绑定变量为number类型  
  55. -- cursor 2) 使用ALL_ROWS 优化器模式, 绑定变量为varchar2类型  
  56. -- cursor 3) 使用FIRST_ROWS 优化器模式, 绑定变量为number类型    
  57. -- cursor 4) 使用FIRST_ROWS 优化器模式,绑定变量为varchar2类型  
  58.   
  59. --查询v$sql视图  
  60. SQL> select sql_id,loaded_versions,executions,optimizer_mode, plan_hash_value,child_number,child_address  
  61.   2  from v$sql where sql_id = '&sql_id';  
  62. old   2: from v$sql where sql_id = '&sql_id'  
  63. new   2: from v$sql where sql_id = '1qqtru155tyz8'  
  64.   
  65. SQL_ID        LOADED_VERSIONS EXECUTIONS OPTIMIZER_ PLAN_HASH_VALUE CHILD_NUMBER CHILD_ADDRESS  
  66. ------------- --------------- ---------- ---------- --------------- ------------ ----------------  
  67. 1qqtru155tyz8               1          1 ALL_ROWS        1601196873            0 0000000081111008  
  68. 1qqtru155tyz8               1          1 ALL_ROWS        2572036781            1 00000000841B1DD8  
  69. 1qqtru155tyz8               1          1 FIRST_ROWS      1601196873            2 00000000813D1A70  
  70. 1qqtru155tyz8               1          1 FIRST_ROWS      2572036781            3 000000007FFE3370  
  71.   
  72. --从上面的查询结果知,optimizer_mode不同,plan_hash_value的值不同,child_address的值也不同  
  73. --尤其是child_address表明是pin到shared_pool中不同的位置  
  74.   
  75. --查看v$sqlarea视图  
  76. SQL> select sql_id,sql_text,version_count vs_cnt,loaded_versions ld_vs,executions ex_cnt  
  77.   2  from v$sqlarea where sql_id = '&sql_id';  
  78. old   2: from v$sqlarea where sql_id = '&sql_id'  
  79. new   2: from v$sqlarea where sql_id = '1qqtru155tyz8'  
  80.   
  81. SQL_ID        SQL_TEXT                                        VS_CNT      LD_VS     EX_CNT  
  82. ------------- ------------------------------------------- ---------- ---------- ----------  
  83. 1qqtru155tyz8 SELECT * FROM T LOOK_FOR_ME WHERE X = :B1            4          4          4  
  84.   
  85. --从上面的视图可知,是sql_id的一个聚合,列出了version_count以及executions的总次数等  
  86.   
  87. --下面来看看每个sql对应的执行计划  
  88. --child_number为0的游标,此时为父游标  
  89. SQL> select * from table(dbms_xplan.display_cursor('&sql_id',0));  
  90. old   1: select * from table(dbms_xplan.display_cursor('&sql_id',0))  
  91. new   1: select * from table(dbms_xplan.display_cursor('1qqtru155tyz8',0))  
  92.   
  93. PLAN_TABLE_OUTPUT  
  94. --------------------------------------------------------------------------------  
  95. SQL_ID  1qqtru155tyz8, child number 0  
  96. -------------------------------------  
  97. SELECT * FROM T LOOK_FOR_ME WHERE X = :B1  
  98.   
  99. Plan hash value: 1601196873  
  100.   
  101. --------------------------------------------------------------------------  
  102. | Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |  
  103. --------------------------------------------------------------------------  
  104. |   0 | SELECT STATEMENT  |      |       |       | 28616 (100)|          |  
  105. |*  1 |  TABLE ACCESS FULL| T    | 10000 |   292K| 28616   (6)| 00:05:44 |  
  106. --------------------------------------------------------------------------------  
  107. PLAN_TABLE_OUTPUT  
  108. --------------------------------------------------------------------------------  
  109. Predicate Information (identified by operation id):  
  110. ---------------------------------------------------  
  111.   
  112.    1 - filter(TO_NUMBER("X")=:B1)  -->存在谓词转换  
  113.   
  114. --下面是child_number为1的子游标  
  115. SQL> select * from table(dbms_xplan.display_cursor('&sql_id',1));  
  116. old   1: select * from table(dbms_xplan.display_cursor('&sql_id',1))  
  117. new   1: select * from table(dbms_xplan.display_cursor('1qqtru155tyz8',1))  
  118.   
  119. PLAN_TABLE_OUTPUT  
  120. --------------------------------------------------------------------------------  
  121. SQL_ID  1qqtru155tyz8, child number 1  
  122. -------------------------------------  
  123. SELECT * FROM T LOOK_FOR_ME WHERE X = :B1  
  124.   
  125. Plan hash value: 2572036781  
  126.   
  127. --------------------------------------------------------------------------------  
  128. | Id  | Operation                   | Name         | Rows  | Bytes | Cost (%CPU)  
  129. --------------------------------------------------------------------------------  
  130. |   0 | SELECT STATEMENT            |              |       |       |     2 (100)  
  131. |   1 |  TABLE ACCESS BY INDEX ROWID| T            |     1 |    30 |     2   (0)  
  132. |*  2 |   INDEX UNIQUE SCAN         | SYS_C0011143 |     1 |       |     1   (0)  
  133. --------------------------------------------------------------------------------  
  134.   
  135. Predicate Information (identified by operation id):  
  136. ---------------------------------------------------  
  137.   
  138.    2 - access("X"=:B1)     
  139.   
  140. --从上面的两个执行计划中可以看出,因为绑定变量的类型不同,导致了sql语句产生了不同的执行计划  
  141. --且第一个执行计划中使用了隐式转换  
  142.   
  143. --下面是child_number为2的子游标的执行计划  
  144. --Author : Robinson  
  145. --Blog   : http://blog.csdn.net/robinson_0612  
  146.   
  147. SQL> select * from table(dbms_xplan.display_cursor('&sql_id',2));  
  148. old   1: select * from table(dbms_xplan.display_cursor('&sql_id',2))  
  149. new   1: select * from table(dbms_xplan.display_cursor('1qqtru155tyz8',2))  
  150.   
  151. PLAN_TABLE_OUTPUT  
  152. --------------------------------------------------------------------------------  
  153. SQL_ID  1qqtru155tyz8, child number 2  
  154. -------------------------------------  
  155. SELECT * FROM T LOOK_FOR_ME WHERE X = :B1  
  156.   
  157. Plan hash value: 1601196873  
  158.   
  159. --------------------------------------------------------------------------  
  160. | Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |  
  161. --------------------------------------------------------------------------  
  162. |   0 | SELECT STATEMENT  |      |       |       | 28616 (100)|          |  
  163. |*  1 |  TABLE ACCESS FULL| T    | 10000 |   292K| 28616   (6)| 00:05:44 |  
  164. --------------------------------------------------------------------------------  
  165. PLAN_TABLE_OUTPUT  
  166. --------------------------------------------------------------------------------  
  167. Predicate Information (identified by operation id):  
  168. ---------------------------------------------------  
  169.   
  170.    1 - filter(TO_NUMBER("X")=:B1)  
  171.   
  172. --下面是child_number为3的子游标的执行计划  
  173. SQL> select * from table(dbms_xplan.display_cursor('&sql_id',3));  
  174. old   1: select * from table(dbms_xplan.display_cursor('&sql_id',3))  
  175. new   1: select * from table(dbms_xplan.display_cursor('1qqtru155tyz8',3))  
  176.   
  177. PLAN_TABLE_OUTPUT  
  178. --------------------------------------------------------------------------------  
  179. SQL_ID  1qqtru155tyz8, child number 3  
  180. -------------------------------------  
  181. SELECT * FROM T LOOK_FOR_ME WHERE X = :B1  
  182.   
  183. Plan hash value: 2572036781  
  184.   
  185. --------------------------------------------------------------------------------  
  186. | Id  | Operation                   | Name         | Rows  | Bytes | Cost (%CPU)  
  187. --------------------------------------------------------------------------------  
  188. |   0 | SELECT STATEMENT            |              |       |       |     2 (100)  
  189. |   1 |  TABLE ACCESS BY INDEX ROWID| T            |     1 |    30 |     2   (0)  
  190. |*  2 |   INDEX UNIQUE SCAN         | SYS_C0011143 |     1 |       |     1   (0)  
  191. --------------------------------------------------------------------------------  
  192.   
  193. Predicate Information (identified by operation id):  
  194. ---------------------------------------------------  
  195.   
  196.    2 - access("X"=:B1)  
  197.   
  198. --子游标2与子游标3也是使用了不同的执行计划,这个原因与父游标0,子游标1的原因相同  
  199. --子游标2与父游标0有相同的执行计划,从Plan hash value的值可知  
  200. --同样,子游标3与父游标1也有相同的执行计划,从Plan hash value的值可知  
  201.   
  202. --产生不同执行计划的原因  
  203. --v$sql_shared_cursor视图记录了那些不能共享子游标的记录并给给出原因,如下查询  
  204. SQL> SELECT child_number,bind_mismatch, optimizer_mode_mismatch   
  205.   2  from v$sql_shared_cursor  
  206.   3  where sql_id = '&SQL_ID';  
  207. old   3: where sql_id = '&SQL_ID'  
  208. new   3: where sql_id = '1qqtru155tyz8'  
  209.   
  210. CHILD_NUMBER B O  
  211. ------------ - -  
  212.            0 N N  
  213.            1 Y N  
  214.            2 N Y  
  215.            3 Y Y  
  216.   
  217. --从上面的查询结果可知,游标1与父游标0是由于绑定变量不匹配而导致了不可共享子游标  
  218. --游标2则是由于不同的执行环境,游标3则是不同的执行环境与不匹配的绑定变量导致不可共享子游标             
  219.   
  220. --从上面的测试可以,父游标相同,子游标不同,执行计划可能相同,也可能不相同   
  221. --下面的这个查询也说明了这个问题,得到的是不同的PLAN_HASH_VALUE    
  222. SQL> select a.snap_id, a.sql_id, a.plan_hash_value,to_char(b.begin_interval_time,'yyyy-mm-dd hh24:mi:ss')  
  223.   2  from dba_hist_sqlstat a, dba_hist_snapshot b   
  224.   3  where a.snap_id = b.snap_id  
  225.   4  and sql_id ='&sql_id';  
  226. old   4: and sql_id ='&sql_id'  
  227. new   4: and sql_id ='1qqtru155tyz8'  
  228.   
  229.    SNAP_ID SQL_ID        PLAN_HASH_VALUE TO_CHAR(B.BEGIN_INT  
  230. ---------- ------------- --------------- -------------------  
  231.        275 1qqtru155tyz8      1601196873 2013-03-08 12:00:25  
  232.        275 1qqtru155tyz8      2572036781 2013-03-08 12:00:25  


四、小结

    a、本文讨论了v$sql,v$sqlarea,v$sqltext以及v$sqltext_with_newlines几个视图的差异

    b、需要记住的是v$sql存储所有游标,v$sqlarea等同于使用了distinct关键字,仅保留sql语句。v$sqltext提供完整的sql语句

    c、硬解析通常是由于不可共享的父游标造成的,如经常变动的SQL语句,或动态SQL或未使用绑定变量等

    d、与父游标SQL文本完全一致的情形下,多个相同的SQL语句可以共享一个父游标

    e、SQL文本、执行环境完全一致的情形下,子游标能够被共享,否则如果执行环境不一致则生成新的子游标

    f、父游标相同,子游标不同,执行计划可能相同,也可能不相同