【发布时间】:2020-09-28 21:15:00
【问题描述】:
我创建了一个集群和集群中的一个表,定义如下:
create cluster roald_dahl_titles (
title varchar2(100)
);
create index i_roald_dahl_titles
on cluster roald_dahl_titles
;
create table ROALD_DAHL_NOVELS (
title varchar2(100),
published_year number
)
cluster roald_dahl_titles (title)
;
值得注意的是,这个索引不是使用唯一约束创建的,很可能会在 ROALD_DAHL_NOVELS 表中插入重复值:
insert into roald_dahl_novels (title, published_year) values ('Esio Trot', 1990);
insert into roald_dahl_novels (title, published_year) values ('Esio Trot', 1990);
然后我收集表和索引的统计信息,并查看使用索引的执行计划:
begin
dbms_stats.gather_table_stats(user, 'ROALD_DAHL_NOVELS');
dbms_stats.gather_INDEX_stats(user, 'I_ROALD_DAHL_TITLES');
end;
/
explain plan for
select published_year
from roald_dahl_novels
where title = 'Esio Trot';
select *
from table(dbms_xplan.display(format => 'ALL'));
执行计划的内容我觉得有点混乱:
Plan hash value: 2187850431
--------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 2 | 28 | 1 (0)| 00:00:01 |
| 1 | TABLE ACCESS CLUSTER| ROALD_DAHL_NOVELS | 2 | 28 | 1 (0)| 00:00:01 |
|* 2 | INDEX UNIQUE SCAN | I_ROALD_DAHL_TITLES | 1 | | 0 (0)| 00:00:01 |
--------------------------------------------------------------------------------------------
Query Block Name / Object Alias (identified by operation id):
-------------------------------------------------------------
1 - SEL$1 / ROALD_DAHL_NOVELS@SEL$1
2 - SEL$1 / ROALD_DAHL_NOVELS@SEL$1
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("TITLE"='Esio Trot')
Column Projection Information (identified by operation id):
-----------------------------------------------------------
1 - "ROALD_DAHL_NOVELS".ROWID[ROWID,10], "TITLE"[VARCHAR2,100],
"PUBLISHED_YEAR"[NUMBER,22]
2 - "ROALD_DAHL_NOVELS".ROWID[ROWID,10]
作为操作 2 的一部分,它执行索引唯一扫描,这告诉我“Esio Trot”预计只会在集群中出现一次。执行计划还规定,对于该操作,它预计只返回一行。
列投影信息告诉我它期望返回单个列(这将是表 ROALD_DAHL_NOVELS 的 ROWID),因此这告诉我从该操作返回的 ROWID 总数将为 1(1 行位于每行 1 个 ROWID)。由于表 ROALD_DAHL_NOVELS 中的两行中的每一行都有不同的 ROWID,因此该操作只能用于从表中返回单行。
当执行 TABLE ACCESS CLUSTER 操作时,执行计划(正确地)期望返回两行,这让我感到困惑。如果 ROWID 正在访问这些行,那么我希望前面的操作返回(至少)两个 ROWID。如果它们没有被 ROWID 访问,我不希望前面的操作返回 ROWID。
此外,在 TABLE ACCESS CLUSTER 中,表 ROALD_DAHL_NOVELS 的 ROWID 列在列投影信息部分中。我没有尝试选择 ROWID,所以我不希望它从该操作中返回。如果在任何地方,我希望它出现在谓词信息部分。
额外调查
我尝试将同一行重复插入到表中,直到它包含同一行的 65536 个相同副本。在收集统计信息并查询索引 I_ROALD_DAHL_TITLES 的 USER_INDEXES 后,我们得到以下信息:
UNIQUENESS DISTINCT_KEYS AVG_DATA_BLOCKS_PER_KEY
UNIQUE 1 109
据我了解,这告诉我们:
- 索引是唯一的,因此我们希望每个键在索引中出现一次
- 索引只有一个不同的键('Esio Trot'),因此必须只有一个条目
- 索引要求我们的一个键匹配表中的多行,跨越 109 个块
这似乎是自相矛盾的——一个键与表中的多行匹配意味着该键的索引中必须有多个条目(每个条目都与不同的 ROWID 匹配),这与索引的唯一性相矛盾。
在检查 USER_EXTENTS 时,索引仅使用 65536 字节的单个扩展区,这不足以容纳表中每个 ROWID 的信息。
【问题讨论】:
-
@MatthewMcPeak 即使它添加了 ROWID 以使其唯一,它仍然不能为表中的每一行都有一个条目,否则它会占用比 USER_EXTENTS 描述的更多空间
-
我认为我的评论会令人困惑/不完整,所以我删除了它并写了一个答案。
-
我不得不说看到一个关于集群的问题给我留下了深刻的印象。我已经使用 Oracle 30 年了,这是第一次有人使用它。
-
@WilliamRobertson 这更像是一个概念证明而不是一个主要项目 - 我有多个表表示存在于同一级别的数据并且有很多重叠的列,但是在不同的时间计算并且会不适合存放在一张宽大的桌子上。
标签: sql oracle sql-execution-plan