I have two tables, which are illustrated by the following example.
CREATE TABLE t1( name VARCHAR2(50 CHAR), status VARCHAR2(8 CHAR), CONSTRAINT t1_pk PRIMARY KEY(name));CREATE TABLE t2( id NUMBER NOT NULL, name VARCHAR2(50 CHAR), finished_at DATE, status VARCHAR2(8 CHAR), CONSTRAINT t2_pk PRIMARY KEY(id));CREATE INDEX ix_t2_last ON t2(name, finished_at);
Now I would like to get the values from table T1
with the value of the column status
from table 'T2' for a given 'name' for the record that has the highest value in finished_at
.
The following select statement is to illustrate the question.
SELECT name, status, ( SELECT t2.status FROM t2 WHERE t2.name = t1.name ORDER BY t2.finished_at FETCH FIRST ROW ONLY) FROM t1 WHERE t1.name = :name;
The actual question is the following:Is there a way to formulate the statement in such a way that the index on T2
is not only used for a range scan with the name
, but that the fact that the column finished_at
is also indexed is also used?
The best execution plan I get is:
-----------------------------------------------------------------------------------------------------------------------------------------------------------------| Id | Operation | Name | Starts | E-Rows |E-Bytes| Cost (%CPU)| E-Time | A-Rows | A-Time | Buffers | OMem | 1Mem | O/1/M |-----------------------------------------------------------------------------------------------------------------------------------------------------------------| 0 | SELECT STATEMENT | | 1 | | | 4 (100)| | 1 |00:00:00.01 | 2 | | | ||* 1 | VIEW | | 1 | 1 | 31 | 3 (0)| 00:00:01 | 1 |00:00:00.01 | 107 | | | ||* 2 | WINDOW BUFFER PUSHED RANK | | 1 | 1 | 20 | 3 (0)| 00:00:01 | 1 |00:00:00.01 | 107 | 2048 | 2048 | 1/0/0|| 3 | TABLE ACCESS BY INDEX ROWID| T2 | 1 | 1 | 20 | 3 (0)| 00:00:01 | 9999 |00:00:00.01 | 107 | | | ||* 4 | INDEX RANGE SCAN | IX_T2_LAST | 1 | | | 2 (0)| 00:00:01 | 9999 |00:00:00.01 | 67 | | | || 5 | TABLE ACCESS BY INDEX ROWID | T1 | 1 | 1 | 10 | 1 (0)| 00:00:01 | 1 |00:00:00.01 | 2 | | | ||* 6 | INDEX UNIQUE SCAN | T1_PK | 1 | 1 | | 0 (0)| | 1 |00:00:00.01 | 1 | | | |-----------------------------------------------------------------------------------------------------------------------------------------------------------------Query Block Name / Object Alias (identified by operation id):------------------------------------------------------------- 1 - SEL$1 / from$_subquery$_002@SEL$3 2 - SEL$1 3 - SEL$1 / T2@SEL$1 4 - SEL$1 / T2@SEL$1 5 - SEL$2 / T1@SEL$2 6 - SEL$2 / T1@SEL$2Predicate Information (identified by operation id):--------------------------------------------------- 1 - filter("from$_subquery$_002"."rowlimit_$$_rownumber"<=1) 2 - filter(ROW_NUMBER() OVER ( ORDER BY "T2"."FINISHED_AT")<=1) 4 - access("T2"."NAME"=:B1) 6 - access("T1"."NAME"=:NAME)
Of course I can create a different index, if needed. ;-)
Since there are many rows for a given name
in T2
a range scan will result in too many buffer gets.