我有两个表,通过以下示例进行说明。
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);
现在我想从表
T1
中获取值,以及表 'T2' 中 status
列的值,以获取 finished_at
中具有最高值的记录的给定“名称”。
下面的select语句就是为了说明这个问题。
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;
实际问题如下: 有没有一种方法可以以这样的方式制定语句:
T2
上的索引不仅用于与name
的范围扫描,而且还使用finished_at
列也被索引的事实?
我得到的最佳执行计划是:
-----------------------------------------------------------------------------------------------------------------------------------------------------------------
| 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$2
Predicate 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)
当然,如果需要,我可以创建不同的索引。 ;-)
由于
name
中给定的 T2
有很多行,范围扫描将导致获取太多缓冲区。
您必须创建一个包含所需所有列的多列(串联)索引,从名称(等式谓词)开始,然后是日期(不等式谓词),最后是您选择的列:
create index myindex on t2(name,finished_at,status)
最后一列(状态)的存在不是为了方便查找,而是为了将表的一部分放入索引中,这样如果 Oracle 需要的所有内容都在索引中,则 Oracle 不需要访问表来获取列。显然,您不想忘乎所以地添加几十列,但是对于一两列,如果您的查询将以高频率运行并且需要额外的速度,那么这是一种有用的技术。