隐式转换是DBA们所不愿意看到的,有人说“I believe implicit conversion to be bad. Badder. Evil. A serious no-no!”
举个例子:
有一条SQL
select * from t_table where id=:1;
说明:t_table记录量在数万条,id varchar2类型,主键。
1.用户用pl/sql等工具执行非常快。
2.在应用中执行该语句非常慢,通过statspack,awr等发现,并且发现是全表扫描。
这种情况很有可能是绑定变量类型不匹配而导致的。
某些情况下,隐式转换会让数据库性能变得很糟糕。有同事处理过这样的案例,在消灭隐式转换后,原本一个需要2小时的业务操作可以在6秒内便可完成。
Oracle也强烈建议使用explicit conversion而不是implicit conversion,explicit conversion相比implicit conversion有如下的好处:
SQL statements are easier to understand when you use explicit datatype conversion functions.
Implicit datatype conversion can have a negative impact on performance, especially if the datatype of a column value is converted to that of a constant rather than the other way around.
Implicit conversion depends on the context in which it occurs and may not work the same way in every case. For example, implicit conversion from a datetime value to a VARCHAR2 value may return an unexpected year depending on the value of the NLS_DATE_FORMAT parameter.
Algorithms for implicit conversion are subject to change across software releases and among Oracle products. Behavior of explicit conversions is more predictable.
在发生隐式转换SQL的诊断过程中,我们使用一些第三方的工具看到的执行计划往往没有异常。因为在使用这些工具的时候,解释的是用户手工输入的而不是应用程序产生的SQL.
那么,如何确定应用程序的SQL是不是发生了隐式转换呢?
在Oracle 10g提供过滤谓词的信息当中,包含了是否发生转换。
查看过滤谓词的方式较多,比如autotrace 、explain plan、utlxpls、dbms_xplan等。
前面3种方法需要手工输入SQL文本,所以看到的同样不是我们需要的结果。而10g中dbms_xplan可以通过指定SQLID来查看用户关心的特定SQL。
SQL> select /*+ hint1 */hiredate from scott.emp where hiredate between ‘17-DE
C-80′ and ‘20-FEB-81′ ;
HIREDATE
———
17-DEC-80
20-FEB-81
SQL> col sql_id for a20
SQL> select sql_id,sql_text from v$sql where sql_text like ‘%hint1%’;
SQL_ID
——————–
SQL_TEXT
——————————————————————————–
2k4kk1zvp8jz1
select /*+ hint1 */hiredate from scott.emp where hiredate between ‘17-DEC-80′
and ‘20-FEB-81′
28n8kr51sv6s6
select sql_id,sql_text from v$sql where sql_text like ‘%hint1%’
SQL> select * from table(dbms_xplan.display_cursor(‘&SQLID’));
Enter value for sqlid: 2k4kk1zvp8jz1
old 1: select * from table(dbms_xplan.display_cursor(‘&SQLID’))
new 1: select * from table(dbms_xplan.display_cursor(‘2k4kk1zvp8jz1′))
PLAN_TABLE_OUTPUT
——————————————————————————-
SQL_ID 2k4kk1zvp8jz1, child number 0
————————————-
select /*+ hint1 */hiredate from scott.emp where hiredate between
‘17-DEC-80′ and ‘20-FEB-81′
Plan hash value: 3896240783
—————————————————————————
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
—————————————————————————
| 0 | SELECT STATEMENT | | | | 3 (100)| |
PLAN_TABLE_OUTPUT
——————————————————————————-
|* 1 | FILTER | | | | | |
|* 2 | TABLE ACCESS FULL| EMP | 1 | 8 | 3 (0)| 00:00:01 |
—————————————————————————
Predicate Information (identified by operation id):
—————————————————
1 – filter(TO_DATE(‘17-DEC-80′)<=TO_DATE(‘20-FEB-81′))
2 – filter((“HIREDATE”<=’20-FEB-81′ AND “HIREDATE”>=’17-DEC-80′))
21 rows selected.
SQL>
然而9i的过滤谓词没这么详细的信息,v$sql 视图中不包含SQLID信息,dbms_xplan中也不提供display_cursor这样的function。就是说,9i不能用以上的方法来辅助判断。
那Oracle 9i下怎么办呢?
试试10046吧,部分级别的10046 trace内容中包含了绑定变量及其变量类型dty信息。
Trace文件可能会包含类似内容:
bind 3: dty=180 mxl=11(11) mal=00 scl=00 pre=00 oacflg=01 oacfl2=0 size=0 offset=72
bfp=ffffffff7f5500f0 bln=11 avl=11 flg=01
value=
Dump of memory from 0xFFFFFFFF7F5500F0 to 0xFFFFFFFF7F5500FB
FFFFFFFF7F5500F0 78640101 0A353619 08B10000 [xd...56.....]
bind 4: dty=180 mxl=11(11) mal=00 scl=00 pre=00 oacflg=01 oacfl2=0 size=0 offset=88
bfp=ffffffff7f550100 bln=11 avl=11 flg=01
value=
下表为Oracle内部的数据类型代码
|
Data Type Numeric Type Codes
|
| 0 |
placeholder for procedures with no arguments |
| 1 |
VARCHAR, VARCHAR2, STRING, NVARCHAR2 |
| 2 |
NUMBER, INTEGER, SMALLINT, REAL, FLOAT, DECIMAL |
| 3 |
BINARY_INTEGER, PLS_INTEGER, POSITIVE, NATURAL |
| 8 |
LONG |
| 11 |
ROWID (old) |
| 12 |
DATE |
| 23 |
RAW |
| 24 |
LONG RAW |
| 58 |
OPAQUE TYPE |
| 69 |
ROWID (new) |
| 96 |
CHAR (ANSI FIXED CHAR), NCHAR, CHARACTER |
| 100 |
BINARY_FLOAT |
| 101 |
BINARY_DOUBLE |
| 106 |
MLSLABEL |
| 121 |
OBJECT |
| 122 |
NESTED TABLE |
| 123 |
VARRAY |
| 178 |
TIME |
| 179 |
TIME WITH TIME ZONE |
| 180 |
TIMESTAMP |
| 181 |
TIMESTAMP WITH TIME ZONE |
| 231 |
TIMESTAMP WITH LOCAL TIME ZONE |
| 250 |
PL/SQL RECORD |
| 251 |
PL/SQL TABLE |
| 252 |
PL/SQL BOOLEAN |
Recent Comments