在查询计划中避免“排序依据”

时间:2018-01-17 08:50:33

标签: sql oracle sorting optimization indexing

我有一张这样的表:

create table A (
    fieldA numeric,
    fieldB varchar(255),
    fieldC varchar(255),
... fields ... constraints ... 
);

我在fieldB上有唯一的B树索引。所以,当我执行这样的查询时:

select /*+ index(a)*/ * from A a where fieldB > 'LOW' and fieldB < 'HIGH' order by fieldB 

由于现有索引,我希望看到带有索引范围扫描和没有排序顺序子句(或任何其他类型的排序)的查询计划。 实际上,我在查询计划中有sort order by

explain plan for select /*+ index(a)*/ * from A a where fieldB > 'LOW' and fieldB < 'HIGH' order by fieldB

结果:

SELECT STATEMENT                     
SORT ORDER BY                       
TABLE ACCESS BY INDEX ROWID BATCHED
INDEX RANGE SCAN

如果我在查询中删除order by,我将无法获得所需顺序的数据(这对我来说很奇怪,因为默认情况下B-Tree索引应按升序构建行树,在这种情况下{ {1}}和select * from A应该提供与我想的相同的查询计划)

如何避免select * from A order by fieldB

Oracle版本:12.2

3 个答案:

答案 0 :(得分:1)

当您的列上已有索引时,无需使用/*+ index(a)*/提示。让优化器为您的查询选择最佳执行计划。见下文:

表:

create table A (
    fieldA numeric,
    fieldB varchar(255),
    fieldC varchar(255)
);

create index indx on A(fieldB);

查询:

select * from A a where fieldB > 'LOW' and fieldB < 'HIGH' order by fieldB ;

计划:未按顺序排序。

Plan Hash Value  : 4131509220 

--------------------------------------------------------------------------------
| Id  | Operation                      | Name | Rows | Bytes | Cost | Time     |
--------------------------------------------------------------------------------
|   0 | SELECT STATEMENT               |      |    1 |   271 |    0 |          |
| * 1 |   FILTER                       |      |      |       |      |          |
|   2 |    TABLE ACCESS BY INDEX ROWID | A    |    1 |   271 |    1 | 00:00:01 |
| * 3 |     INDEX RANGE SCAN           | INDX |    1 |       |    1 | 00:00:01 |
--------------------------------------------------------------------------------

Predicate Information (identified by operation id):
------------------------------------------
* 1 - filter(NULL IS NOT NULL)
* 3 - access("FIELDB">'LOW' AND "FIELDB"<'HIGH')


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

答案 1 :(得分:1)

TABLE ACCESS BY INDEX ROWID BATCHED访问路径是12c中的新路径; from the docs

  

数据库使用从索引中获取的rowid来查找...表中的相应行,然后检索它们。 BATCHED访问权限...表示数据库从索引中检索一些rowid,然后尝试按块顺序访问行以改进群集并减少数据库必须访问块的次数。 / p>

它通过减少命中每个块的次数来更有效地检索实际数据(假设实际数据分布意味着存在具有多个行的相关数据的块)。但结果是检索到的数据不一定按索引顺序排列,因此需要明确排序。

  

当我们使用索引获取rowid时,我们将以相同的顺序从DB获取所有数据。这是我的期望

BATCHED访问权限不再适用。

  

问题在于,即使没有必要,也会增加额外费用。

但这是必要的;并且您没有从批量访问中看到成本减少(这取决于实际的数据分布,如前所述)。你无法真正孤立地看待一个成本价值。优化器通常知道它在做什么。 (我不认为暗示会影响到这一点,但我同意@XING你不应该拥有它 - 除非你真的确定你知道优化器没有的东西。)

值得注意的是,即使11g版本没有显示明确的排序(根据@XING的演示),Oracle也从未保证在没有order by子句的情况下返回订单结果,并且简单TABLE ACCESS BY INDEX ROWID(即不是BATCHED)优化器只能跳过显式排序,因为它已经知道按该顺序检索了数据。这是您在评论中提到的内容。

在11g中你可能以所需的顺序返回行而没有order by子句,但仅仅是因为Oracle如何获取索引的rowid的实现细节,然后是相应的数据逐行。但它不能保证。这种引擎盖下的变化正是为什么明确order by即使看起来多余也是明智的。正如您在当前的代码中所做的那样。但这并不是多余的,也不是真的。

(早期版本中有一个类似的'问题'与group-by;它通常暗示排序顺序(我认为最多9i)并且人们省略了order by;但随后优化者改变了(I在10g中思考并且让人们知道......)

答案 2 :(得分:0)

问题出在会话的NLS_LANGUAGENLS_TERRITORY参数中。 默认情况下,我的会话

NLS_LANGUAGE= 'RUSSIAN'

NLS_TERRITORY= 'RUSSIA'

当我尝试使用varchar索引值和查询中的order by按特定顺序获取数据时,我会在查询计划中获得额外的sort order by

我更改了这些参数后:

NLS_LANGUAGE= 'AMERICAN'

NLS_TERRITORY= 'AMERICA'

我的查询计划中没有额外的sort order by

现在,查询:

EXPLAIN PLAN FOR select * from SIEBEL_CT.A a where fieldB > 'LOW' and fieldB < 'HIGH' order by fieldB

EXPLAIN PLAN FOR select * from SIEBEL_CT.A a where fieldB > 'LOW' and fieldB < 'HIGH'

具有相同的查询计划