実際のサイズは BLOCKS(使用済ブロック数)+EMPTY_BLOCKS(空きブロック数)から計算できます。
本当に必要なサイズは NUM_ROWS(行数)や AVG_ROW_LEN(1行あたりの平均バイト数)から見積もることができます。
(より正確に見積もるにはブロックサイズや各種オーバーヘッドを考慮する必要がありますが...)
実際のサイズと見積もりサイズを比較すれば、領域使用率を知ることもできます。
これらの情報を利用すれば、例えば、定期的にテーブルの領域使用率を監視し、使用率が一定の閾値を下回る場合にテーブルを再構築してテーブルを縮小するとともに、空き領域を増やすというような運用を行うことができるわけです。
ところが、先日、あるシステムで問題が発生しました。
実際には少なくとも領域使用率が 80% 以上はあると思われるテーブルの領域使用率がわずか 数% と計算されてしまい、テーブルの再構築が実行されてしまったのです。
調べてみると、どうも見積もりサイズが著しく小さく計算されているようです。
詳しく調べてみると、見積もりサイズの計算根拠となる AVG_ROW_LEN の値が著しく小さいことがわかりました。
さらに詳しく調べてみると、統計情報を収集するコマンドによって AVG_ROW_LEN の値が異なるようです。
ANALYZE TABLE <tablename> COMPUTE STATISTICS;とすると正常(と思われる)値となり、
CALL DBMS_STATS.GATHER_TABLE_STATS('<ownername>','<tablename>');とすると著しく小さな値となるようです。
どうもバグくさいと思って、サポートサイトを検索すると、やはり、バグでした。
LONG 列や LOB 列を持つテーブルに対して、DBMS_STATS.GATHER_XXXXXX_STATS プロシージャを使用して統計情報を収集すると、LONG 列や LOB 列のサイズを考慮せずに AVG_ROW_LEN の値を計算してしまうようです。
実際、問題が発生したテーブルには LOB 列が存在しました...
しかも、問題が発生したのは、9.2.0.x なんですが、このバグは 10.2.0 でしか改修されないようです...