简体   繁体   中英

Avoiding 'sort order by' in query plan

I have a table like this:

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

And I have unique B-tree index on fieldB . So, when I execute query like this:

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

I expect to see query plan with index range scan and without sort order by clause (or any other type of sort), because of existing index. In fact, I have sort order by in query plan

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

Result:

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

If I remove order by in my query, i will not get data in needed order (that is strange for me, because by default B-Tree index should build tree of rows in ascending order and in that case select * from A and select * from A order by fieldB should give the same query plan as i thought)

How can I avoid sort order by ?

Oracle version: 12.2

There is no need to use /*+ index(a)*/ hint when you already have the index on your column. Let optimizer choose the best execution plan for your query. See below:

Table:

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

create index indx on A(fieldB);

Query:

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

Plan: Sort by Order is not used.

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

The TABLE ACCESS BY INDEX ROWID BATCHED access path is new in 12c; from the docs :

The database uses the rowids obtained from the index to find the corresponding rows from the ... table, and then retrieve them. The BATCHED access ... means that the database retrieves a few rowids from the index, and then attempts to access rows in block order to improve the clustering and reduce the number of times that the database must access a block.

It's retrieving the actual data more efficiently by reducing the number of times it hits each block (assuming the actual data distribution means there were blocks with relevant data for more than one row). But as a result the data retrieved is not necessarily in index order, so it needs to be sorted explicitly.

As we got the rowid using index, we will get all data from DB in the same order. That is my expectations

That isn't true any more with BATCHED access.

The problem is that it adds additional cost even if it isn't necessary to do.

But it is necessary; and you aren't seeing the cost reduction from the batched access (which depends on the actual data distribution, as mentioned before). You can't really look at one cost value in isolation. The optimiser usually knows what it's doing. (I don't think the hint is affecting this at all, but I'd agree with @XING that you shouldn't have it - unless you're really sure you know something the optimiser doesn't).


It's worth noting that even though the 11g version didn't show an explicit sort (as per @XING's demo), Oracle has never guaranteed the order results will be returned without an order by clause, and with a simple TABLE ACCESS BY INDEX ROWID (ie not BATCHED ) the optimiser is just able to skip the explicit sort because it already knows the data was retrieved in that order. Which is what you alluded to in a comment.

In 11g you'd probably get the rows back in the desired order without the order by clause but only because of the implementation details of how Oracle is getting the indexed rowid's and then the corresponding data row-by-row. But it isn't guaranteed. This sort of under-the-hood change is exactly why it's always been wise to explicitly order by even when it appeared to be redundant. As you are doing in your current code. But it isn't redundant, and never was really.

(There was a similar 'issue' with group-by in an earlier release; it usually implied a sort order (up to 9i I think) and people omitted the order by ; but then the optimiser changed (I think in 10g) and that caught people out...)

The problem was in NLS_LANGUAGE and NLS_TERRITORY parameters of session. By default, my session has

NLS_LANGUAGE= 'RUSSIAN'

NLS_TERRITORY= 'RUSSIA'

And when i try to get the data in particular order using varchar indexing value and order by in query i get additional sort order by in query plan.

After I changed these parameters:

NLS_LANGUAGE= 'AMERICAN'

NLS_TERRITORY= 'AMERICA'

I don't get additional sort order by in my query plan.

Now, queries:

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'

Have the same query plan

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM