我的猜测是,有人只收集了一张临时表的统计信息,这导致只有一个系统上的计划不好。
统计数据可能对临时表有害——因为数据非常不稳定,很难创建有代表性的统计数据。这就是为什么 Oracle 通常不会在临时表上收集统计信息,而是使用动态采样在运行时估计统计信息。例如,DBMS_STATS.GATHER_SCHEMA_STATS
不会收集临时表的统计信息。但是DBMS_STATS.GATHER_TABLE_STATS
会。
如果没有统计信息,LAST_ANALYZED 时间戳将为空:
select table_name, last_analyzed from dba_tables where table_name = 'GTT1';
如果是这种情况,请删除统计信息并锁定表,以免再次发生这种情况。
begin
dbms_stats.delete_table_stats(user, 'GTT1');
dbms_stats.lock_table_stats(user, 'GTT1');
end;
/
现在,如果有人试图收集统计信息,他们将收到一条错误消息:
begin
dbms_stats.gather_table_stats(user, 'GTT1');
end;
/
ORA-20005: object statistics are locked (stattype = ALL)
ORA-06512: at "SYS.DBMS_STATS", line 23829
ORA-06512: at "SYS.DBMS_STATS", line 23880
ORA-06512: at line 2
如果您的系统依赖于动态采样,您需要确保参数已启用并合理设置。默认值 2 通常足够好:
select value from v$parameter where name = 'optimizer_dynamic_sampling'
这是一个脚本,展示了如何明确地收集统计信息会导致错误的基数估计。
创建表,用 100K 行填充它们。仅收集其中一张表的统计信息:
create global temporary table test_with_stats3(a number) on commit preserve rows;
create global temporary table test_no_stats3(a number) on commit preserve rows;
insert into test_with_stats3 select level from dual connect by level <= 100000;
insert into test_no_stats3 select level from dual connect by level <= 100000;
commit;
begin
dbms_stats.gather_schema_stats(user);
end;
/
begin
dbms_stats.gather_table_stats(user, 'TEST_WITH_STATS3');
end;
/
删除所有行。对于有统计信息的表,Oracle 仍然认为它有 100K 行:
delete from test_with_stats3;
delete from test_no_stats3;
commit;
explain plan for select * from test_with_stats3;
select * from table(dbms_xplan.display);
Plan hash value: 467959123
--------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 100K| 488K| 44 (3)| 00:00:01 |
| 1 | TABLE ACCESS FULL| TEST_WITH_STATS3 | 100K| 488K| 44 (3)| 00:00:01 |
--------------------------------------------------------------------------------------
没有统计信息的表有更准确的估计:
explain plan for select * from test_no_stats3;
select * from table(dbms_xplan.display);
Plan hash value: 2315614086
------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 13 | 43 (0)| 00:00:01 |
| 1 | TABLE ACCESS FULL| TEST_NO_STATS3 | 1 | 13 | 43 (0)| 00:00:01 |
------------------------------------------------------------------------------------
Note
-----
- dynamic sampling used for this statement (level=2)