一個客戶碰到的具體需求,分區表中有些分區所在的表空間被OFFLINE,導致在刪除統計信息時報錯。
下面通過例子來說明這個問題:
SQL> create table t_part_read (id number)
2 partition by range (id)
3 (partition p1 values less than (10) tablespace ts1,
4 partition p2 values less than (20) tablespace ts2,
5 partition pmax values less than (maxvalue) tablespace users);
Table created.
SQL> insert into t_part_read select rownum from tab;
54 rows created.
SQL> commit;
Commit complete.
SQL> exec dbms_stats.gather_table_stats(user, 'T_PART_READ')
PL/SQL procedure successfully completed.
SQL> alter tablespace ts1 read only;
Tablespace altered.
SQL> exec dbms_stats.gather_table_stats(user, 'T_PART_READ')
PL/SQL procedure successfully completed.
SQL> alter tablespace ts1 offline;
Tablespace altered.
SQL> exec dbms_stats.gather_table_stats(user, 'T_PART_READ')
BEGIN dbms_stats.gather_table_stats(user, 'T_PART_READ'); END;
*
ERROR at line 1:
ORA-00376: file 6 cannot be read at this time
ORA-01110: data file 6: '/u01/app/oracle/oradata/ORCL/datafile/tbo1_mf_ts1_7w8l5fz1_.dbf'
ORA-06512: at "SYS.DBMS_STATS", line 23829
ORA-06512: at "SYS.DBMS_STATS", line 23880
ORA-06512: at line 1
如果將表空間只讀,并不會影響到表空間上的表或分區的統計信息的收集,因為收集過程只是讀取,而收集的結果信息是寫到SYSTEM表空間的。
但是如果分區所在的表空間處于OFFLINE狀態,那么在統計信息收集的過程中就會報錯。
有一個很簡單的方法可以解決這個問題,就是將被OFFLINE影響的分區的統計信息鎖定,這樣Oracle在收集統計信息時就會跳過鎖定的分區,通過這個辦法就可以避免統計信息收集過程中的報錯:
SQL> exec dbms_stats.lock_partition_stats(user, 'T_PART_READ', 'P1')
PL/SQL procedure successfully completed.
SQL> exec dbms_stats.gather_table_stats(user, 'T_PART_READ')
BEGIN dbms_stats.gather_table_stats(user, 'T_PART_READ'); END;
*
ERROR at line 1:
ORA-00376: file 6 cannot be read at this time
ORA-01110: data file 6: '/u01/app/oracle/oradata/ORCL/datafile/o1_mf_ts1_7w8l5fz1_.dbf'
ORA-06512: at "SYS.DBMS_STATS", line 23829
ORA-06512: at "SYS.DBMS_STATS", line 23880
ORA-06512: at line 1
SQL> exec dbms_stats.gather_table_stats(user, 'T_PART_READ', granularity => 'PARTITION')
PL/SQL procedure successfully completed.
即使鎖定分區后,嘗試收集統計信息仍然報錯,這是因為Oracle默認除了要收集分區上的統計信息以外,還要收集表級的統計信息,而這就會造成被OFFLINE影響的分區也要被讀取。
解決方法就是在收集統計信息的時候指定收集的粒度是分區,不收集表上的GLOBAL信息。