看见微信群有位朋友问:
truncate表,会将统计信息清除么?
有些朋友回复,
数据字典信息都没有了,统计信息就清除了,所以是没有统计信息的。
做个实验,跟踪一下truncate,应该比较清楚。
我做了10g的测试,发现那个表的last_analyzed还是有记录的。
truncate完统计信息还是在的,跟你10g还是11g没有关系,关键在你之前有没有收集统计信息,你之前都没有收集统计信息,last analyzed本来就是空的。
之前有记录,last_analyzed是不为空的,truncate表后,这个变成了空。
第二位朋友说的很对,究竟会不会删除统计信息,做一下实验,就可以了解了。
创建测试表,
SQL> create table test (id number, name varchar2(1));
Table created.
SQL> begin
for i in 1 .. 10000 loop
insert into test values(i, dbms_random.string('a',1));
end loop;
commit;
end;
/
PL/SQL procedure successfully completed.
SQL> create index idx_test on test(id);
Index created.
SQL> select count(*) from test;
COUNT(*)
----------
10000
此时检索表的统计信息,记录是空的,检索索引的统计信息,是有记录的,
SQL> select num_rows, to_char(last_analyzed,'yyyy-mm-dd hh24:mi:ss') last_analyzed from user_tables where table_name='TEST';
NUM_ROWS LAST_ANALYZED
--------------- -----------------------------------------
SQL> select num_rows, sample_size, to_char(last_analyzed,'yyyy-mm-dd hh24:mi:ss') last_analyzed from user_indexes where table_name='TEST';
NUM_ROWS SAMPLE_SIZE LAST_ANALYZED
---------- ----------- -----------------------------
10000 10000 2017-10-08 15:55:42
手工以cascade=false收集统计信息,
SQL> exec dbms_stats.gather_table_stats('BISAL','TEST',cascade=>false);
PL/SQL procedure successfully completed.
可以看出,表的统计信息已近更新了,
SQL> select num_rows, to_char(last_analyzed,'yyyy-mm-dd hh24:mi:ss') last_analyzed from user_tables where table_name='TEST';
NUM_ROWS LAST_ANALYZED
---------- --------------------
10000 2017-10-08 16:04:16
但是由于cascade=false,因此不会自动采集索引,
SQL> select num_rows, sample_size, to_char(last_analyzed,'yyyy-mm-dd hh24:mi:ss') last_analyzed from user_indexes where table_name='TEST';
NUM_ROWS SAMPLE_SIZE LAST_ANALYZED
---------- ----------- -----------------------------
10000 10000 2017-10-08 15:55:42
以cascade=true采集统计信息,表和索引的统计信息更新了,
SQL> exec dbms_stats.gather_table_stats('BISAL','TEST',cascade=>true);
PL/SQL procedure successfully completed.
SQL> select num_rows, to_char(last_analyzed,'yyyy-mm-dd hh24:mi:ss') last_analyzed from user_tables where table_name='TEST';
NUM_ROWS LAST_ANALYZED
---------- --------------------
10000 2017-10-08 16:07:18
SQL> select num_rows, sample_size, to_char(last_analyzed,'yyyy-mm-dd hh24:mi:ss') last_analyzed from user_indexes where table_name='TEST';
NUM_ROWS SAMPLE_SIZE LAST_ANALYZED
---------- ----------- ---------------
10000 10000 2017-10-08 16:07:18
此时执行truncate,清空表数据,
SQL> truncate table test;
Table truncated.
SQL> select count(*) from test;
COUNT(*)
----------
0
可以看出表和索引统计信息,没有被删除,
SQL> select num_rows, to_char(last_analyzed,'yyyy-mm-dd hh24:mi:ss') last_analyzed from user_tables where table_name='TEST';
NUM_ROWS LAST_ANALYZED
---------- --------------------
10000 2017-10-08 16:07:18
SQL> select num_rows, sample_size, to_char(last_analyzed,'yyyy-mm-dd hh24:mi:ss') last_analyzed from user_indexes where table_name='TEST';
NUM_ROWS SAMPLE_SIZE LAST_ANALYZED
---------- ----------- ---------------
10000 10000 2017-10-08 16:07:18
执行一次统计信息采集,此时表和索引的统计信息,已经是最新了,
SQL> exec dbms_stats.gather_table_stats('BISAL','TEST',cascade=>true);
PL/SQL procedure successfully completed.
SQL> select num_rows, to_char(last_analyzed,'yyyy-mm-dd hh24:mi:ss') last_analyzed from user_tables where table_name='TEST';
NUM_ROWS LAST_ANALYZED
--------- --------------------
0 2017-10-08 16:25:06
SQL> select num_rows, sample_size, to_char(last_analyzed,'yyyy-mm-dd hh24:mi:ss') last_analyzed from user_indexes where table_name='TEST';
NUM_ROWS SAMPLE_SIZE LAST_ANALYZED
---------- ----------- --------------------
0 0 2017-10-08 16:25:06
说明执行truncate,表的统计信息不会被删除,除非执行了统计信息采集,truncate table和表和索引的统计信息,没有任何关联。
另一方面,truncate会影响表是否可以被自动采集统计信息的任务触发,mon_mods_all$会记录自上次自动统计信息收集作业完成之后,对所有目标表的insert、delete和update操作所影响的记录数,即DML操作次数,以及目标表是否执行过truncate操作,主要用于每日统计信息采集作业判断是否需要采集此张表,对于这张视图mon_mods_all$的介绍,可以参考eygle的文章,
http://www.eygle.com/archives/2009/09/mon_mods_is_use.html
比如如下表,记录数为10000,mon_mods_all$记录了一条信息,其中插入insert是10000,其他的字段,为空,
SQL> select count(*) from test;
COUNT(*)
----------
10000
SQL> exec dbms_stats.flush_database_monitoring_info();
PL/SQL procedure successfully completed.
SQL> select obj#, inserts, updates, deletes, flags from sys.mon_mods_all$ where obj#=18021;
OBJ# INSERTS UPDATES DELETES FLAGS
------ --------- --------- ---------- -------
18021 10000 0 0 0
此时执行truncate,mon_mods_all$记录未变,
SQL> truncate table test;
Table truncated.
SQL> select obj#, inserts, updates, deletes, flags from sys.mon_mods_all$ where obj#=18021;
OBJ# INSERTS UPDATES DELETES FLAGS
------- ---------- ---------- --------- --------
18021 10000 0 0 0
此时执行一次dbms_stats.flush_database_monitoring_info(),显示FLAGS是1,表示执行过了truncate table,
SQL> exec dbms_stats.flush_database_monitoring_info();
PL/SQL procedure successfully completed.
SQL> select obj#, inserts, updates, deletes, flags from sys.mon_mods_all$ where obj#=18021;
OBJ# INSERTS UPDATES DELETES FLAGS
------- ---------- ---------- --------- --------
18021 10000 0 10000 1
再次执行统计信息采集,此时mon_mods_all$的记录就会被清空,
SQL> exec dbms_stats.gather_table_stats('BISAL','TEST',cascade=>true);
PL/SQL procedure successfully completed.
SQL> select obj#, inserts, updates, deletes, flags from sys.mon_mods_all$ where obj#=18021;
no rows selected
总结:
1. 执行truncate,表的统计信息不会被删除,除非执行了统计信息采集,truncate table和表和索引的统计信息,没有任何关联,对象是否有统计信息记录,取决于是否采集过统计信息,包括手工和自动两种方法。
2. 执行truncate,会将mon_mods_all$视图的FLAGS字段置位,变为1,自动采集统计信息作业,可以据此判断,是否需要采集这张表,当重新采集统计信息,就会删除mon_mods_all$保存的记录。
如果您觉得此篇文章对您有帮助,欢迎关注微信公众号:bisal的个人杂货铺,您的支持是对我最大的鼓励!共同学习,共同进步:)