truncate表,会将统计信息清除么?

>>强大,10k+点赞的 SpringBoot 后台管理系统竟然出了详细教程!

看见微信群有位朋友问:

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

truncate表,会将统计信息清除么?


比如如下表,记录数为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的个人杂货铺,您的支持是对我最大的鼓励!共同学习,共同进步:)

truncate表,会将统计信息清除么?


truncate表,会将统计信息清除么?