优化器总是完全扫描 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')