优化器总是完全扫描 table 即使什么也没有只有 3 行
optimizer always full scans table eben though fething only 3 rows
我有一个 table foo 是这样创建的。
CREATE TABLE foo AS SELECT * FROM all_objects;
CREATE INDEX foo_I1 ON foo(owner,object_type,status);
exec dbms_stats.gather_table_stats('hr','foo',method_opt=>'FOR ALL COLUMNS size AUTO');
我在 3 列上创建了一个索引并触发了一个如下所示的查询。
select * from foo where status='INVALID';
select * from foo where status='VALID';
status='VALID' 在 71780 行的 table 中获取了大约 71000 行。它会进行完整的 table 扫描。这是可以理解的。但是如果 status='INVALID' 只获取 3 行,它会进行完整的 table 扫描。它也使 A 行和 E 行非常不同。
计划:两个查询相同。
SQL_ID gdhy9j91gu9sm, 子号0
select /*+gather_plan_statistics */ * 来自 foo where status='VALID'
Plan hash value: 1245013993
------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers |
------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 50 |00:00:00.01 | 4 |
|* 1 | TABLE ACCESS FULL| FOO | 1 | 71773 | 50 |00:00:00.01 | 4 |
------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter("STATUS"='VALID')
请解释此行为。数据库版本:11.2g oracle.
缺少直方图可能导致完整 table 扫描。通常仅在数据倾斜时才创建直方图 并且 如果该列已在相关谓词中使用。
有时您需要在收集统计数据之前运行查询,让 Oracle 知道该列非常重要,值得使用直方图。
select * from foo where status='INVALID';
exec dbms_stats.gather_table_stats('hr','foo',method_opt=>'FOR ALL COLUMNS size AUTO');
重新运行 SELECT
现在可以使用直方图了。通过直方图,Oracle 知道 INVALID
returns 少量行,索引会很有用:
explain plan for select * from foo where status='INVALID';
select * from table(dbms_xplan.display);
Plan hash value: 1520589999
---------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 134 | 217 (0)| 00:00:01|
| 1 | TABLE ACCESS BY INDEX ROWID BATCHED| FOO | 1 | 134 | 217 (0)| 00:00:01|
|* 2 | INDEX SKIP SCAN | FOO_I1 | 1 | | 216 (0)| 00:00:01|
---------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("STATUS"='INVALID')
filter("STATUS"='INVALID')
我有一个 table foo 是这样创建的。
CREATE TABLE foo AS SELECT * FROM all_objects;
CREATE INDEX foo_I1 ON foo(owner,object_type,status);
exec dbms_stats.gather_table_stats('hr','foo',method_opt=>'FOR ALL COLUMNS size AUTO');
我在 3 列上创建了一个索引并触发了一个如下所示的查询。
select * from foo where status='INVALID';
select * from foo where status='VALID';
status='VALID' 在 71780 行的 table 中获取了大约 71000 行。它会进行完整的 table 扫描。这是可以理解的。但是如果 status='INVALID' 只获取 3 行,它会进行完整的 table 扫描。它也使 A 行和 E 行非常不同。
计划:两个查询相同。
SQL_ID gdhy9j91gu9sm, 子号0
select /*+gather_plan_statistics */ * 来自 foo where status='VALID'
Plan hash value: 1245013993
------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers |
------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 50 |00:00:00.01 | 4 |
|* 1 | TABLE ACCESS FULL| FOO | 1 | 71773 | 50 |00:00:00.01 | 4 |
------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter("STATUS"='VALID')
请解释此行为。数据库版本:11.2g oracle.
缺少直方图可能导致完整 table 扫描。通常仅在数据倾斜时才创建直方图 并且 如果该列已在相关谓词中使用。
有时您需要在收集统计数据之前运行查询,让 Oracle 知道该列非常重要,值得使用直方图。
select * from foo where status='INVALID';
exec dbms_stats.gather_table_stats('hr','foo',method_opt=>'FOR ALL COLUMNS size AUTO');
重新运行 SELECT
现在可以使用直方图了。通过直方图,Oracle 知道 INVALID
returns 少量行,索引会很有用:
explain plan for select * from foo where status='INVALID';
select * from table(dbms_xplan.display);
Plan hash value: 1520589999
---------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 134 | 217 (0)| 00:00:01|
| 1 | TABLE ACCESS BY INDEX ROWID BATCHED| FOO | 1 | 134 | 217 (0)| 00:00:01|
|* 2 | INDEX SKIP SCAN | FOO_I1 | 1 | | 216 (0)| 00:00:01|
---------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("STATUS"='INVALID')
filter("STATUS"='INVALID')