集群中 table 的执行计划需要一行,而它应该需要多行
Execution plan of table in cluster expects one row when it should expect multiple
我创建了一个集群,并在集群中创建了一个 table,定义如下:
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)
;
值得注意的是,这个索引不是用唯一约束创建的,很有可能将重复值插入 table 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);
然后我收集有关 table 和索引的统计信息,并查看使用索引的执行计划:
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 / ROALD_DAHL_NOVELS@SEL
2 - SEL / ROALD_DAHL_NOVELS@SEL
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' 预计只在集群中出现一次。执行计划还表示,对于该操作,它预计 return 只有一行。
列投影信息告诉我它期望 return 单个列(这将是 table ROALD_DAHL_NOVELS 的 ROWID),所以这告诉我总从该操作编辑的 ROWID 数 return 将为 1(1 行,每行 1 个 ROWID)。由于 table ROALD_DAHL_NOVELS 中的每一行都有不同的 ROWID,那么这个操作只能用于 return 来自 table.[=16 的单个行=]
当执行 TABLE ACCESS CLUSTER 操作时,执行计划然后(正确地)期望两行被 returned,这让我感到困惑。如果 ROWID 正在访问这些行,那么我希望之前的操作 return (至少)两个 ROWID。如果它们没有被 ROWID 访问,我不希望之前的操作对 return 和 ROWIDs.
此外,在TABLE ACCESS CLUSTER 中,table ROALD_DAHL_NOVELS 的ROWID 列在列投影信息部分。我没有尝试 select ROWID,所以我不希望它从该操作中被 returned。如果有的话,我希望它在谓词信息部分。
补充调查
我尝试将同一行反复插入 table,直到它包含同一行的 65536 个相同副本。在收集统计数据并查询 USER_INDEXES 索引 I_ROALD_DAHL_TITLES 后,我们得到以下信息:
UNIQUENESS DISTINCT_KEYS AVG_DATA_BLOCKS_PER_KEY
UNIQUE 1 109
据我了解,这告诉我们:
- 索引是唯一的,所以我们期望每个键在索引中出现一次
- 索引只有一个不同的键('Esio Trot'),所以必须只有一个条目
- 索引期望我们的一个键与 table 中的多行匹配,跨越 109 个块
这似乎是自相矛盾的——一个键匹配 table 中的多行将意味着该键的索引中必须有多个条目(每个匹配到不同的 ROWID),这与索引是唯一的。
检查USER_EXTENTS时,索引只使用了一个65536字节的extent,这不足以space保存table中每个ROWID的信息。
实际执行计划(19.5测试)也存在同样的问题。
可能是显示的集群对象执行计划的限制或错误。我会在 asktom.oracle.com 上问这个问题,以获得来自 Oracle 的某种官方(免费)答案。
PLAN_TABLE_OUTPUT
----------------------------------------------------------------------------------------------------------------------------------------------------------------
SQL_ID f41cf1x2zdyyr, child number 0
-------------------------------------
select published_year from roald_dahl_novels where title = 'Esio
Trot'
Plan hash value: 2187850431
--------------------------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows |E-Bytes| Cost (%CPU)| E-Time | A-Rows | A-Time | Buffers |
--------------------------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | | 1 (100)| | 2 |00:00:00.01 | 3 |
| 1 | TABLE ACCESS CLUSTER| ROALD_DAHL_NOVELS | 1 | 2 | 28 | 1 (0)| 00:00:01 | 2 |00:00:00.01 | 3 |
|* 2 | INDEX UNIQUE SCAN | I_ROALD_DAHL_TITLES | 1 | 1 | | 0 (0)| | 1 |00:00:00.01 | 1 |
--------------------------------------------------------------------------------------------------------------------------------------
Query Block Name / Object Alias (identified by operation id):
-------------------------------------------------------------
1 - SEL / ROALD_DAHL_NOVELS@SEL
2 - SEL / ROALD_DAHL_NOVELS@SEL
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("TITLE"='Esio Trot')
Column Projection Information (identified by operation id):
PLAN_TABLE_OUTPUT
----------------------------------------------------------------------------------------------------------------------------------------------------------------
-----------------------------------------------------------
1 - "ROALD_DAHL_NOVELS".ROWID[ROWID,10], "TITLE"[VARCHAR2,100], "PUBLISHED_YEAR"[NUMBER,22]
2 - "ROALD_DAHL_NOVELS".ROWID[ROWID,10]
32 rows selected.
这不是错误。
运行 您数据库中的这个查询:
select UNIQUENESS from dba_indexes where index_name = upper('i_roald_dahl_titles');
UNIQUENES
---------
UNIQUE
原因是B树集群索引只存储存储该数据的集群块的数据库块地址 -- 它不存储完整的rowid
值,就像普通索引一样。
因此,虽然 title = 'Esio Trot'
的各个行可能具有 rowid
值,例如:
select rowid row_id, title from roald_dahl_novels n;
ROW_ID TITLE
------------------ ----------------------------------------------------------------------------------------------------
ABocNnACmAABWsWAAL Esio Trot
ABocNnACmAABWsWAAM Esio Trot
ABocNnACmAABWsWAAN Esio Trot
B-tree簇索引只存储一个条目:"Esio Trot",以及对应的数据库块地址。您可以在您的数据库中确认这一点:
select num_rows from dba_indexes where index_Name = 'I_ROALD_DAHL_TITLES';
NUM_ROWS
----------
1
这就是您收到 UNIQUE SCAN
报告的原因。因为就索引而言,这就是它正在做的事情。
我创建了一个集群,并在集群中创建了一个 table,定义如下:
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)
;
值得注意的是,这个索引不是用唯一约束创建的,很有可能将重复值插入 table 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);
然后我收集有关 table 和索引的统计信息,并查看使用索引的执行计划:
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 / ROALD_DAHL_NOVELS@SEL
2 - SEL / ROALD_DAHL_NOVELS@SEL
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' 预计只在集群中出现一次。执行计划还表示,对于该操作,它预计 return 只有一行。
列投影信息告诉我它期望 return 单个列(这将是 table ROALD_DAHL_NOVELS 的 ROWID),所以这告诉我总从该操作编辑的 ROWID 数 return 将为 1(1 行,每行 1 个 ROWID)。由于 table ROALD_DAHL_NOVELS 中的每一行都有不同的 ROWID,那么这个操作只能用于 return 来自 table.[=16 的单个行=]
当执行 TABLE ACCESS CLUSTER 操作时,执行计划然后(正确地)期望两行被 returned,这让我感到困惑。如果 ROWID 正在访问这些行,那么我希望之前的操作 return (至少)两个 ROWID。如果它们没有被 ROWID 访问,我不希望之前的操作对 return 和 ROWIDs.
此外,在TABLE ACCESS CLUSTER 中,table ROALD_DAHL_NOVELS 的ROWID 列在列投影信息部分。我没有尝试 select ROWID,所以我不希望它从该操作中被 returned。如果有的话,我希望它在谓词信息部分。
补充调查
我尝试将同一行反复插入 table,直到它包含同一行的 65536 个相同副本。在收集统计数据并查询 USER_INDEXES 索引 I_ROALD_DAHL_TITLES 后,我们得到以下信息:
UNIQUENESS DISTINCT_KEYS AVG_DATA_BLOCKS_PER_KEY
UNIQUE 1 109
据我了解,这告诉我们:
- 索引是唯一的,所以我们期望每个键在索引中出现一次
- 索引只有一个不同的键('Esio Trot'),所以必须只有一个条目
- 索引期望我们的一个键与 table 中的多行匹配,跨越 109 个块
这似乎是自相矛盾的——一个键匹配 table 中的多行将意味着该键的索引中必须有多个条目(每个匹配到不同的 ROWID),这与索引是唯一的。
检查USER_EXTENTS时,索引只使用了一个65536字节的extent,这不足以space保存table中每个ROWID的信息。
实际执行计划(19.5测试)也存在同样的问题。 可能是显示的集群对象执行计划的限制或错误。我会在 asktom.oracle.com 上问这个问题,以获得来自 Oracle 的某种官方(免费)答案。
PLAN_TABLE_OUTPUT
----------------------------------------------------------------------------------------------------------------------------------------------------------------
SQL_ID f41cf1x2zdyyr, child number 0
-------------------------------------
select published_year from roald_dahl_novels where title = 'Esio
Trot'
Plan hash value: 2187850431
--------------------------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows |E-Bytes| Cost (%CPU)| E-Time | A-Rows | A-Time | Buffers |
--------------------------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | | 1 (100)| | 2 |00:00:00.01 | 3 |
| 1 | TABLE ACCESS CLUSTER| ROALD_DAHL_NOVELS | 1 | 2 | 28 | 1 (0)| 00:00:01 | 2 |00:00:00.01 | 3 |
|* 2 | INDEX UNIQUE SCAN | I_ROALD_DAHL_TITLES | 1 | 1 | | 0 (0)| | 1 |00:00:00.01 | 1 |
--------------------------------------------------------------------------------------------------------------------------------------
Query Block Name / Object Alias (identified by operation id):
-------------------------------------------------------------
1 - SEL / ROALD_DAHL_NOVELS@SEL
2 - SEL / ROALD_DAHL_NOVELS@SEL
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("TITLE"='Esio Trot')
Column Projection Information (identified by operation id):
PLAN_TABLE_OUTPUT
----------------------------------------------------------------------------------------------------------------------------------------------------------------
-----------------------------------------------------------
1 - "ROALD_DAHL_NOVELS".ROWID[ROWID,10], "TITLE"[VARCHAR2,100], "PUBLISHED_YEAR"[NUMBER,22]
2 - "ROALD_DAHL_NOVELS".ROWID[ROWID,10]
32 rows selected.
这不是错误。
运行 您数据库中的这个查询:
select UNIQUENESS from dba_indexes where index_name = upper('i_roald_dahl_titles');
UNIQUENES --------- UNIQUE
原因是B树集群索引只存储存储该数据的集群块的数据库块地址 -- 它不存储完整的rowid
值,就像普通索引一样。
因此,虽然 title = 'Esio Trot'
的各个行可能具有 rowid
值,例如:
select rowid row_id, title from roald_dahl_novels n;
ROW_ID TITLE ------------------ ---------------------------------------------------------------------------------------------------- ABocNnACmAABWsWAAL Esio Trot ABocNnACmAABWsWAAM Esio Trot ABocNnACmAABWsWAAN Esio Trot
B-tree簇索引只存储一个条目:"Esio Trot",以及对应的数据库块地址。您可以在您的数据库中确认这一点:
select num_rows from dba_indexes where index_Name = 'I_ROALD_DAHL_TITLES';
NUM_ROWS ---------- 1
这就是您收到 UNIQUE SCAN
报告的原因。因为就索引而言,这就是它正在做的事情。