视图 v$sql,v$sqlarea,$sqltext,v$sqltext_with_newlines 的差异
http://blog.csdn.net/leshami/article/details/8658205
视图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、创建演示环境
- SQL> select * from v$version where rownum<2;
- BANNER
- --------------------------------------------------------------------------------
- Oracle Database 11g Enterprise Edition Release 11.2.0.1.0 - 64bit Production
- SQL> create table t ( x varchar2(30) primary key, y int );
- SQL> exec dbms_stats.set_table_stats('SCOTT','T', numrows => 1000000, numblks=>100000);
- SQL> alter system flush shared_pool;
- SQL> select sql_text from v$sql where upper(sql_text) like 'SELECT % T LOOK_FOR_ME%'
- 2 and upper(sql_text) not like '%FROM V$SQL%';
- no rows selected
- 2、产生sql游标
- SQL> declare
- 2 l_x_number number;
- 3 l_x_string varchar2(30);
- 4 begin
- 5 execute immediate 'alter session set optimizer_mode=all_rows';
- 6 for x in (select * from t look_for_me where x = l_x_number) loop null; end loop;
- 7 for x in (select * from t look_for_me where x = l_x_string) loop null; end loop;
- 8 execute immediate 'alter session set optimizer_mode=first_rows';
- 9 for x in (select * from t look_for_me where x = l_x_number) loop null; end loop;
- 10 for x in (select * from t look_for_me where x = l_x_string) loop null; end loop;
- 11 end;
- 12 /
- PL/SQL procedure successfully completed.
- 3、分析
- SQL> col sql_text format a55
- SQL> set linesize 180
- SQL> col plan_table_output format a80 truncate
- SQL> col sql_id new_val sql_id
- SQL> select sql_id, sql_text from v$sql where upper(sql_text) like 'SELECT % T LOOK_FOR_ME %B1_';
- SQL_ID SQL_TEXT
- ------------- -------------------------------------------------------
- 1qqtru155tyz8 SELECT * FROM T LOOK_FOR_ME WHERE X = :B1
- 1qqtru155tyz8 SELECT * FROM T LOOK_FOR_ME WHERE X = :B1
- 1qqtru155tyz8 SELECT * FROM T LOOK_FOR_ME WHERE X = :B1
- 1qqtru155tyz8 SELECT * FROM T LOOK_FOR_ME WHERE X = :B1
- --从上面的查询可知,对于上面的SQL语句我们得到了相同的SQL_ID。这是因为SQL_ID是由SQL文本hash得到的一个值
- --只要SQL文本相同(完全相同),则SQL_ID一定是相同的。
- --我们从v$sql视图里边查询得到了四条相同sql_id的sql语句,也即是四个不同的游标
- --为什么同样的sql文本产生了四个不同的游标呢?这是因为:
- -- cursor 1) 使用ALL_ROWS 优化器模式, 绑定变量为number类型
- -- cursor 2) 使用ALL_ROWS 优化器模式, 绑定变量为varchar2类型
- -- cursor 3) 使用FIRST_ROWS 优化器模式, 绑定变量为number类型
- -- cursor 4) 使用FIRST_ROWS 优化器模式,绑定变量为varchar2类型
- --查询v$sql视图
- SQL> select sql_id,loaded_versions,executions,optimizer_mode, plan_hash_value,child_number,child_address
- 2 from v$sql where sql_id = '&sql_id';
- old 2: from v$sql where sql_id = '&sql_id'
- new 2: from v$sql where sql_id = '1qqtru155tyz8'
- SQL_ID LOADED_VERSIONS EXECUTIONS OPTIMIZER_ PLAN_HASH_VALUE CHILD_NUMBER CHILD_ADDRESS
- ------------- --------------- ---------- ---------- --------------- ------------ ----------------
- 1qqtru155tyz8 1 1 ALL_ROWS 1601196873 0 0000000081111008
- 1qqtru155tyz8 1 1 ALL_ROWS 2572036781 1 00000000841B1DD8
- 1qqtru155tyz8 1 1 FIRST_ROWS 1601196873 2 00000000813D1A70
- 1qqtru155tyz8 1 1 FIRST_ROWS 2572036781 3 000000007FFE3370
- --从上面的查询结果知,optimizer_mode不同,plan_hash_value的值不同,child_address的值也不同
- --尤其是child_address表明是pin到shared_pool中不同的位置
- --查看v$sqlarea视图
- SQL> select sql_id,sql_text,version_count vs_cnt,loaded_versions ld_vs,executions ex_cnt
- 2 from v$sqlarea where sql_id = '&sql_id';
- old 2: from v$sqlarea where sql_id = '&sql_id'
- new 2: from v$sqlarea where sql_id = '1qqtru155tyz8'
- SQL_ID SQL_TEXT VS_CNT LD_VS EX_CNT
- ------------- ------------------------------------------- ---------- ---------- ----------
- 1qqtru155tyz8 SELECT * FROM T LOOK_FOR_ME WHERE X = :B1 4 4 4
- --从上面的视图可知,是sql_id的一个聚合,列出了version_count以及executions的总次数等
- --下面来看看每个sql对应的执行计划
- --child_number为0的游标,此时为父游标
- SQL> select * from table(dbms_xplan.display_cursor('&sql_id',0));
- old 1: select * from table(dbms_xplan.display_cursor('&sql_id',0))
- new 1: select * from table(dbms_xplan.display_cursor('1qqtru155tyz8',0))
- PLAN_TABLE_OUTPUT
- --------------------------------------------------------------------------------
- SQL_ID 1qqtru155tyz8, child number 0
- -------------------------------------
- SELECT * FROM T LOOK_FOR_ME WHERE X = :B1
- Plan hash value: 1601196873
- --------------------------------------------------------------------------
- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
- --------------------------------------------------------------------------
- | 0 | SELECT STATEMENT | | | | 28616 (100)| |
- |* 1 | TABLE ACCESS FULL| T | 10000 | 292K| 28616 (6)| 00:05:44 |
- --------------------------------------------------------------------------------
- PLAN_TABLE_OUTPUT
- --------------------------------------------------------------------------------
- Predicate Information (identified by operation id):
- ---------------------------------------------------
- 1 - filter(TO_NUMBER("X")=:B1) -->存在谓词转换
- --下面是child_number为1的子游标
- SQL> select * from table(dbms_xplan.display_cursor('&sql_id',1));
- old 1: select * from table(dbms_xplan.display_cursor('&sql_id',1))
- new 1: select * from table(dbms_xplan.display_cursor('1qqtru155tyz8',1))
- PLAN_TABLE_OUTPUT
- --------------------------------------------------------------------------------
- SQL_ID 1qqtru155tyz8, child number 1
- -------------------------------------
- SELECT * FROM T LOOK_FOR_ME WHERE X = :B1
- Plan hash value: 2572036781
- --------------------------------------------------------------------------------
- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)
- --------------------------------------------------------------------------------
- | 0 | SELECT STATEMENT | | | | 2 (100)
- | 1 | TABLE ACCESS BY INDEX ROWID| T | 1 | 30 | 2 (0)
- |* 2 | INDEX UNIQUE SCAN | SYS_C0011143 | 1 | | 1 (0)
- --------------------------------------------------------------------------------
- Predicate Information (identified by operation id):
- ---------------------------------------------------
- 2 - access("X"=:B1)
- --从上面的两个执行计划中可以看出,因为绑定变量的类型不同,导致了sql语句产生了不同的执行计划
- --且第一个执行计划中使用了隐式转换
- --下面是child_number为2的子游标的执行计划
- --Author : Robinson
- --Blog : http://blog.csdn.net/robinson_0612
- SQL> select * from table(dbms_xplan.display_cursor('&sql_id',2));
- old 1: select * from table(dbms_xplan.display_cursor('&sql_id',2))
- new 1: select * from table(dbms_xplan.display_cursor('1qqtru155tyz8',2))
- PLAN_TABLE_OUTPUT
- --------------------------------------------------------------------------------
- SQL_ID 1qqtru155tyz8, child number 2
- -------------------------------------
- SELECT * FROM T LOOK_FOR_ME WHERE X = :B1
- Plan hash value: 1601196873
- --------------------------------------------------------------------------
- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
- --------------------------------------------------------------------------
- | 0 | SELECT STATEMENT | | | | 28616 (100)| |
- |* 1 | TABLE ACCESS FULL| T | 10000 | 292K| 28616 (6)| 00:05:44 |
- --------------------------------------------------------------------------------
- PLAN_TABLE_OUTPUT
- --------------------------------------------------------------------------------
- Predicate Information (identified by operation id):
- ---------------------------------------------------
- 1 - filter(TO_NUMBER("X")=:B1)
- --下面是child_number为3的子游标的执行计划
- SQL> select * from table(dbms_xplan.display_cursor('&sql_id',3));
- old 1: select * from table(dbms_xplan.display_cursor('&sql_id',3))
- new 1: select * from table(dbms_xplan.display_cursor('1qqtru155tyz8',3))
- PLAN_TABLE_OUTPUT
- --------------------------------------------------------------------------------
- SQL_ID 1qqtru155tyz8, child number 3
- -------------------------------------
- SELECT * FROM T LOOK_FOR_ME WHERE X = :B1
- Plan hash value: 2572036781
- --------------------------------------------------------------------------------
- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)
- --------------------------------------------------------------------------------
- | 0 | SELECT STATEMENT | | | | 2 (100)
- | 1 | TABLE ACCESS BY INDEX ROWID| T | 1 | 30 | 2 (0)
- |* 2 | INDEX UNIQUE SCAN | SYS_C0011143 | 1 | | 1 (0)
- --------------------------------------------------------------------------------
- Predicate Information (identified by operation id):
- ---------------------------------------------------
- 2 - access("X"=:B1)
- --子游标2与子游标3也是使用了不同的执行计划,这个原因与父游标0,子游标1的原因相同
- --子游标2与父游标0有相同的执行计划,从Plan hash value的值可知
- --同样,子游标3与父游标1也有相同的执行计划,从Plan hash value的值可知
- --产生不同执行计划的原因
- --v$sql_shared_cursor视图记录了那些不能共享子游标的记录并给给出原因,如下查询
- SQL> SELECT child_number,bind_mismatch, optimizer_mode_mismatch
- 2 from v$sql_shared_cursor
- 3 where sql_id = '&SQL_ID';
- old 3: where sql_id = '&SQL_ID'
- new 3: where sql_id = '1qqtru155tyz8'
- CHILD_NUMBER B O
- ------------ - -
- 0 N N
- 1 Y N
- 2 N Y
- 3 Y Y
- --从上面的查询结果可知,游标1与父游标0是由于绑定变量不匹配而导致了不可共享子游标
- --游标2则是由于不同的执行环境,游标3则是不同的执行环境与不匹配的绑定变量导致不可共享子游标
- --从上面的测试可以,父游标相同,子游标不同,执行计划可能相同,也可能不相同
- --下面的这个查询也说明了这个问题,得到的是不同的PLAN_HASH_VALUE
- SQL> select a.snap_id, a.sql_id, a.plan_hash_value,to_char(b.begin_interval_time,'yyyy-mm-dd hh24:mi:ss')
- 2 from dba_hist_sqlstat a, dba_hist_snapshot b
- 3 where a.snap_id = b.snap_id
- 4 and sql_id ='&sql_id';
- old 4: and sql_id ='&sql_id'
- new 4: and sql_id ='1qqtru155tyz8'
- SNAP_ID SQL_ID PLAN_HASH_VALUE TO_CHAR(B.BEGIN_INT
- ---------- ------------- --------------- -------------------
- 275 1qqtru155tyz8 1601196873 2013-03-08 12:00:25
- 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、父游标相同,子游标不同,执行计划可能相同,也可能不相同
五、相关参考
父游标、子游标及共享游标
绑定变量及其优缺点
Oracle 硬解析与软解析
Oracle 绑定变量窥探
Oracle自适应共享游标
Whats the difference between the v$sql* views
六、更多参考:
有关Oracle RAC请参考
使用crs_setperm修改RAC资源的所有者及权限
使用crs_profile管理RAC资源配置文件
RAC 数据库的启动与关闭
再说 Oracle RAC services
Services in Oracle Database 10g
Migrate datbase from single instance to Oracle RAC
Oracle RAC 连接到指定实例
Oracle RAC 负载均衡测试(结合服务器端与客户端)
Oracle RAC 服务器端连接负载均衡(Load Balance)
Oracle RAC 客户端连接负载均衡(Load Balance)
ORACLE RAC 下非缺省端口监听配置(listener.ora tnsnames.ora)
ORACLE RAC 监听配置 (listener.ora tnsnames.ora)
配置 RAC 负载均衡与故障转移
CRS-1006 , CRS-0215 故障一例
基于Linux (RHEL 5.5) 安装Oracle 10g RAC
使用 runcluvfy 校验Oracle RAC安装环境
有关Oracle 网络配置相关基础以及概念性的问题请参考:
配置非默认端口的动态服务注册
配置sqlnet.ora限制IP访问Oracle
Oracle 监听器日志配置与管理
设置 Oracle 监听器密码(LISTENER)
配置ORACLE 客户端连接到数据库
有关基于用户管理的备份和备份恢复的概念请参考
Oracle 冷备份
Oracle 热备份
Oracle 备份恢复概念
Oracle 实例恢复
Oracle 基于用户管理恢复的处理
SYSTEM 表空间管理及备份恢复
SYSAUX表空间管理及恢复
Oracle 基于备份控制文件的恢复(unsing backup controlfile)
有关RMAN的备份恢复与管理请参考
RMAN 概述及其体系结构
RMAN 配置、监控与管理
RMAN 备份详解
RMAN 还原与恢复
RMAN catalog 的创建和使用
基于catalog 创建RMAN存储脚本
基于catalog 的RMAN 备份与恢复
RMAN 备份路径困惑
使用RMAN实现异机备份恢复(WIN平台)
使用RMAN迁移文件系统数据库到ASM
linux 下RMAN备份shell脚本
使用RMAN迁移数据库到异机
有关ORACLE体系结构请参考
Oracle 表空间与数据文件
Oracle 密码文件
Oracle 参数文件
Oracle 联机重做日志文件(ONLINE LOG FILE)
Oracle 控制文件(CONTROLFILE)
Oracle 归档日志
Oracle 回滚(ROLLBACK)和撤销(UNDO)
Oracle 数据库实例启动关闭过程
Oracle 10g SGA 的自动化管理
Oracle 实例和Oracle数据库(Oracle体系结构)