创建一个测试表:
create table customer (first_name varchar2(20), last_name varchar2(20) not null, address varchar(20));
insert into customer select dbms_random.string('U', 20), dbms_random.string('U', 20), dbms_random.string('U', 20) from dual connect by level <= 100000;
commit;
create index i_ln_fn_0 on customer(last_name, first_name,0); — just to be sure that all rows are indexed
现在解释计划:
explain plan for
select /*+ FIRST_ROWS(20) */ *
from CUSTOMER
where first_name like 'AB%'
and first_name is not null
order by last_name;
select * from table(dbms_xplan.display);
-------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 197 | 12411 | 275 (2)| 00:00:04 |
| 1 | SORT ORDER BY | | 197 | 12411 | 275 (2)| 00:00:04 |
|* 2 | TABLE ACCESS FULL| CUSTOMER | 197 | 12411 | 274 (1)| 00:00:04 |
-------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - filter("FIRST_NAME" LIKE 'AB%' AND "FIRST_NAME" IS NOT NULL)
但是因为我只想要第一行,所以我想避免整个表格。我想有这样的计划:
SELECT STATEMENT
TABLE ACCESS BY ROWID (customer)
INDEX FULL SCAN (i_ln_fn_0)
如何说服数据库以避免排序?
问题更严重。即使我只使用last_name,无处不在:
explain plan for
select /*+ FIRST_ROWS(20) */ last_name
from CUSTOMER
where last_name like 'AB%'
and last_name is not null
order by last_name;
select * from table(dbms_xplan.display);
-------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 17 | 357 | 4 (25)| 00:00:01 |
| 1 | SORT ORDER BY | | 17 | 357 | 4 (25)| 00:00:01 |
|* 2 | INDEX RANGE SCAN| I_LN_FN_0 | 17 | 357 | 3 (0)| 00:00:01 |
-------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("LAST_NAME" LIKE 'AB%')
filter("LAST_NAME" LIKE 'AB%')
这里排序实际上没有必要,但db仍然使用它。为什么呢?
编辑:在
上进行测试Oracle Database 11g Enterprise Edition Release 11.2.0.1.0 - 64bit Production
PL/SQL Release 11.2.0.1.0 - Production
"CORE 11.2.0.1.0 Production"
TNS for 64-bit Windows: Version 11.2.0.1.0 - Production
NLSRTL Version 11.2.0.1.0 - Production
和
Oracle Database 11g Enterprise Edition Release 11.2.0.2.0 - 64bit Production+
PL/SQL Release 11.2.0.2.0 - Production+
"CORE 11.2.0.2.0Production"+
TNS for IBM/AIX RISC System/6000: Version 11.2.0.2.0 - Production+
NLSRTL Version 11.2.0.2.0 - Production+
采用相同的计划。
答案 0 :(得分:2)
原因是NLS_SORT参数值错误。将其改为BINARY后,计划开始看起来像我想要的那样。
来自http://docs.oracle.com/cd/E24693_01/server.11203/e24448/initparams152.htm:
NLS_SORT的值会影响查询的执行计划。因为一个 标准索引不能用作在a中排序的值的来源 语言顺序,通常必须执行显式排序操作 而不是索引范围扫描。 NLSSORT的功能索引 可以定义函数以提供以语言顺序排序的值 并将索引范围扫描重新引入执行计划。
(我在forums.oracle com中得到了Paul Horth的回答。)