yangtingkun
===========================================================
FIRST_ROWS优化模式语言排序模糊匹配问题
===========================================================

标题比较长,不过只有这样才能把问题描述清楚。

问题详细描述为,在FIRST_ROWS优化模式下,将会话排序和比较方式设置为语义模式,即忽略大小写模式,对字段进行LIKE模糊查询,可能导致错误的结果。

关于大小写不敏感的查询的详细描述,可以参考:http://yangtingkun.itpub.net/post/468/460324


下面直接看问题的现象:

SQL> CREATE TABLE T1 (ID NUMBER PRIMARY KEY, NAME VARCHAR2(30));

表已创建。

SQL> CREATE INDEX IND_T1_NAME ON T1(NAME);

索引已创建。

SQL> INSERT INTO T1 SELECT ROWNUM, CHR(64 + ROWNUM)
2 FROM ALL_OBJECTS WHERE ROWNUM <= 26;

已创建26行。

SQL> COMMIT;

提交完成。

SQL> ALTER SESSION SET NLS_COMP = LINGUISTIC;

会话已更改。

SQL> ALTER SESSION SET NLS_SORT = BINARY_CI;

会话已更改。

SQL> SELECT * FROM T1 WHERE NAME LIKE 'a%';

ID NAME
---------- ------------------------------
1 A

SQL> SELECT /*+ FIRST_ROWS */ * FROM T1 WHERE NAME LIKE 'a%';

未选定行

只要修改上面提到的关键点中的任意一个,就不会产生这个错误的现象:

SQL> SELECT /*+ ALL_ROWS */ * FROM T1 WHERE NAME LIKE 'a%';

ID NAME
---------- ------------------------------
1 A

SQL> SELECT /*+ FIRST_ROWS */ * FROM T1 WHERE NAME = 'a';

ID NAME
---------- ------------------------------
1 A

SQL> ALTER SESSION SET NLS_SORT = BINARY;

会话已更改。

SQL> ALTER SESSION SET NLS_COMP = BINARY;

会话已更改。

SQL> SELECT /*+ FIRST_ROWS */ * FROM T1 WHERE NAME LIKE 'A%';

ID NAME
---------- ------------------------------
1 A

SQL> ALTER SESSION SET NLS_COMP = LINGUISTIC;

会话已更改。

SQL> ALTER SESSION SET NLS_SORT = BINARY_CI;

会话已更改。

SQL> SELECT /*+ FIRST_ROWS */ * FROM T1 WHERE NAME LIKE 'A%';

ID NAME
---------- ------------------------------
1 A

SQL> SELECT /*+ FIRST_ROWS */ * FROM T1 WHERE NAME LIKE 'a';

未选定行

通过上面的几个查询可以看到,问题和FIRST_ROWSLIKE操作以及基于语义的排序直接相关,下面看看Oracle在异常情况下采用了何种执行计划:

SQL> SET AUTOT ON EXP
SQL> SELECT /*+ FIRST_ROWS */ * FROM T1 WHERE NAME LIKE 'a';

未选定行

执行计划
----------------------------------------------------------
Plan hash value: 3350237141

-------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 30 | 35 (0)| 00:00:01 |
|* 1 | VIEW | index$_join$_001 | 1 | 30 | 35 (0)| 00:00:01 |
|* 2 | HASH JOIN | | | | | |
|* 3 | INDEX RANGE SCAN | IND_T1_NAME | 1 | 30 | 3 (34)| 00:00:01 |
| 4 | INDEX FAST FULL SCAN| SYS_C006622 | 1 | 30 | 33 (0)| 00:00:01 |
-------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

1 - filter("NAME" LIKE 'a')
2 - access(ROWID=ROWID)
3 - access("NAME" LIKE 'a')

Note
-----
- dynamic sampling used for this statement

由于索引中并不包含语义查询的结果,因此Oracle这里必须访问表才能得到最终的结果,因此这个执行计划是错误的:

SQL> SELECT * FROM T1 WHERE NAME = 'a';

ID NAME
---------- ------------------------------
1 A

执行计划
----------------------------------------------------------
Plan hash value: 3617692013

--------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 30 | 3 (0)| 00:00:01 |
|* 1 | TABLE ACCESS FULL| T1 | 1 | 30 | 3 (0)| 00:00:01 |
--------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

1 - filter(NLSSORT("NAME",'nls_sort=''BINARY_CI''')=HEXTORAW('6100')
)

Note
-----
- dynamic sampling used for this statement

SQL> SELECT /*+ INDEX(T1) */ * FROM T1 WHERE NAME = 'a';

ID NAME
---------- ------------------------------
1 A

执行计划
----------------------------------------------------------
Plan hash value: 159298173

-------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 30 | 827 (1)| 00:00:10 |
|* 1 | TABLE ACCESS BY INDEX ROWID| T1 | 1 | 30 | 827 (1)| 00:00:10 |
| 2 | INDEX FULL SCAN | SYS_C006622 | 26 | | 26 (0)| 00:00:01 |
-------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

1 - filter(NLSSORT("NAME",'nls_sort=''BINARY_CI''')=HEXTORAW('6100') )

Note
-----
- dynamic sampling used for this statement

上面的两个执行计划已经说明了问题的关键,Oracle对于语义的排序无法通过索引获取,必须要访问表或者相应的函数索引,详细描述可以参考文章开头部分给出的链接。

而采用了FIRST_ROWS优化模式后,当操作为LIKE时,Oracle优化器选择了错误的执行计划进行了优化,采用索引的范围扫描代替了表,从而引发了错误:

SQL> SELECT /*+ INDEX_JOIN(T1 IND_T1_NAME SYS_C006622) */ *
2 FROM T1
3 WHERE NAME LIKE 'a';

未选定行

执行计划
----------------------------------------------------------
Plan hash value: 3350237141

-------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 30 | 35 (0)| 00:00:01 |
|* 1 | VIEW | index$_join$_001 | 1 | 30 | 35 (0)| 00:00:01 |
|* 2 | HASH JOIN | | | | | |
|* 3 | INDEX RANGE SCAN | IND_T1_NAME | 1 | 30 | 3 (34)| 00:00:01 |
| 4 | INDEX FAST FULL SCAN| SYS_C006622 | 1 | 30 | 33 (0)| 00:00:01 |
-------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

1 - filter("NAME" LIKE 'a')
2 - access(ROWID=ROWID)
3 - access("NAME" LIKE 'a')

Note
-----
- dynamic sampling used for this statement

现在没有使用FIRST_ROWS,而采用HINT也达到了相同的效果。而解决这个问题的方法就是通过HINT来避免索引范围扫描的发生。

SQL> ALTER SESSION SET OPTIMIZER_MODE = FIRST_ROWS;

会话已更改。

SQL> SELECT * FROM T1 WHERE NAME LIKE 'a';

未选定行

执行计划
----------------------------------------------------------
Plan hash value: 3350237141

-------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 30 | 35 (0)| 00:00:01 |
|* 1 | VIEW | index$_join$_001 | 1 | 30 | 35 (0)| 00:00:01 |
|* 2 | HASH JOIN | | | | | |
|* 3 | INDEX RANGE SCAN | IND_T1_NAME | 1 | 30 | 3 (34)| 00:00:01 |
| 4 | INDEX FAST FULL SCAN| SYS_C006622 | 1 | 30 | 33 (0)| 00:00:01 |
-------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

1 - filter("NAME" LIKE 'a')
2 - access(ROWID=ROWID)
3 - access("NAME" LIKE 'a')

Note
-----
- dynamic sampling used for this statement

SQL> SELECT /*+ FULL(T1) */ * FROM T1 WHERE NAME LIKE 'a';

ID NAME
---------- ------------------------------
1 A

执行计划
----------------------------------------------------------
Plan hash value: 3617692013

--------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 30 | 3 (0)| 00:00:01 |
|* 1 | TABLE ACCESS FULL| T1 | 1 | 30 | 3 (0)| 00:00:01 |
--------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

1 - filter("NAME" LIKE 'a')

Note
-----
- dynamic sampling used for this statement

SQL> SELECT /*+ NO_INDEX(T1) */ * FROM T1 WHERE NAME LIKE 'a';

ID NAME
---------- ------------------------------
1 A

执行计划
----------------------------------------------------------
Plan hash value: 3617692013

--------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 30 | 3 (0)| 00:00:01 |
|* 1 | TABLE ACCESS FULL| T1 | 1 | 30 | 3 (0)| 00:00:01 |
--------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

1 - filter("NAME" LIKE 'a')

Note
-----
- dynamic sampling used for this statement

查询metalinkOracleDoc ID: Note:5252496.8明确说明了这个bug,这个bug会在Oracle10.2.0.4和11.1.0.6中被Fixed

yangtingkun 发表于:2008.04.22 00:15 ::分类: ( ORACLE , Bug ) ::阅读:(390次) :: 评论 (4)
re: FIRST_ROWS优化模式语言排序模糊匹配问题 [回复]

yangtingkun 你好!
看了你Oracle分页查询语句
http://yangtingkun.itpub.net/post/468/101703
这里我还有个疑问,想请你指点。由于性能关系,以下一段是我收集到的一段会话 trace文件,

SELECT TONECODE, TONENAME, SINGER, TONEINFO, PRICE, UPDATETIME, DOWNLOADTIMES,
WEBLEAFDIRINDEX , SPNAME ,SPCODE , AUDITIONTYPE,TONEVALIDDAY,
SHOCKTONECOUNT
FROM
( SELECT A.*,ROWNUM RN FROM ( SELECT TONECODE, TONENAME, SINGER, TONEINFO,
PRICE, UPDATETIME, DOWNLOADTIMES, WEBLEAFDIRINDEX , SPNAME ,SPCODE ,
AUDITIONTYPE,TO_CHAR(TONEVALIDDAY,'yyyy-mm-dd hh24:mi:ss') AS TONEVALIDDAY,
SHOCKTONECOUNT
FROM T_TEMP_QUERYtable
WHERE CUTFLAG=0
AND CHECKTIME =SYSDATE
AND UPLOADTYPE IN(0,1)
AND PROPERTY

bolun 评论于: 2008.04.22 20:36
re: FIRST_ROWS优化模式语言排序模糊匹配问题 [回复]

不好意思,可能有敏感关键词,留言SQL自动给截断了,具体问题描述请见:
http://cblzm.spaces.live.com/
谢谢。

bolun 评论于: 2008.04.22 21:10
re: FIRST_ROWS优化模式语言排序模糊匹配问题 [回复]

看了一下你的SQL,感觉有可能存在两个问题。

一个是控制分页部分的绑定变量问题,这个绑定变量是系统自动绑定,还是程序设置绑定变量。如果是设置了初始化参数CURSOR_SHARING为FORCE或SIMILAR,则很可能造成分页执行计划的问题。

第二点是ROWNUM的位置不对,其实如果你不排序的话,将ROWNUM N,这样可以少写一层嵌套,而且很可能会避免执行计划前面出现的那个VIEW。

yangtingkun 评论于: 2008.04.23 14:01
re: FIRST_ROWS优化模式语言排序模糊匹配问题 [回复]

最后两点建议,一:如果是问和我文章相关的内容,请尽量在该话题后面提问,而不要在其他地方提问。

二:如果问题比较长,或者比较复杂,请尽量将帖子发到ITPUB,然后这里给出连接。itpub的blog会自动屏蔽其他网站的地址,很可能你的回复我还没有看到就被自动清除了。

最后,你的多个留言我把关键部分留下来了,其余的删除掉了

yangtingkun 评论于: 2008.04.23 14:05

发表评论
标题

在此添加评论
表情符号: smile laughing tongue angry crying sad wassat wink

称呼

邮箱地址(可选)

个人主页(可选)

 authimage


切换风格
新闻聚合
博客日历
文章归档...
最新发表...
最新评论...
最多阅读文章...
最多评论文章...
博客统计...
Blog信息
网站链接...