web-dev-qa-db-ja.com

Oracleテーブルに割り当てられたスペースの点で優れているものは何ですか:NUMBERまたはBINARY_FLOAT?

100億のレコードテーブルがあるので、スペースの割り当てを改善する必要があります。私のテーブルには、浮動小数点型(単精度)のフィールドしかありません。 NUMBERまたはBINARY_FLOATまたは他の何かの最良の選択は何ですか?

1
João Paraná

それはあなたのデータに依存します(そしてこれはあなたが2つのデータ型の異なる振る舞いを気にしないことを前提としています)。実際に保存しているものと同様のサンプルデータを使用してテストをまとめ、使用するスペースが少ないアプローチを見つけることができます。

2つの異なる列を持つテーブルnum_testを作成します。1つはnumberとして宣言され、もう1つはbinary_floatとして宣言されます。

create table num_test (
  num_col number,
  flt_col binary_float
);

0.001刻みで100,000行のデータを入力すると

begin
  for i in 1 .. 100000
  loop
    insert into num_test( num_col, flt_col )
      values( i/1000, i/1000 );
  end loop;
end;

次に、値を格納するために必要な最小、最大、および平均のサイズを確認できます

select min( vsize(num_col) ), max( vsize(num_col) ), avg( vsize(num_col) ),
       min( vsize(flt_col) ), max( vsize(flt_col) ), avg( vsize(flt_col) )
  from num_test;

これを実行すると、数値列の平均が1行あたり3.89バイト(最小2、最大4)であるのに対し、binary_floatごとに4バイトのストレージが必要であることがわかります。したがって、この場合、numberを使用することでわずかな節約ができます。

しかし、それが全体の話ですか?いいえ。異なるサンプルデータを使用すると、非常に異なる結果が得られます。同じテストを実行しますが、すべてを3で割ります

begin
  for i in 1 .. 100000
  loop
    insert into num_test( num_col, flt_col )
      values( i/1000/3, i/1000/3 );
  end loop;
end;

ここで、同じvsizeクエリを実行すると、number列が最大21バイトのストレージを使用し、200,000行で平均7.69バイトになることがわかります。 flt_colは、すべての行に固定の4バイトを使用しています。

データによっては、どちらかのアプローチがより多くのスペースを使用する場合があります。もちろん、スペース使用率に大きな違いがある場合、浮動小数点数に移動すると、より大きな丸めの問題が発生する可能性があります。

結果はバージョンに依存する可能性があることにも注意してください。私は11.2データベースでテストを行いました。ドキュメントには、 binary_floatは常に4バイトのストレージを使用します と記載されています。 10.2では、ドキュメントには binary_floatは余分な長さインジケーターが含まれているため5バイトかかった と示されています。 32ビット値に長さバイトが必要な理由は私にはわかりません。そのため、以前のバージョンのOracleで5番目のバイトが必要だった理由を理解するのは難しいですが、ドキュメントには一貫性があります。

3
Justin Cave

INDEXで使用されるスペースも必要になる可能性があるため、TABLE およびINDEXで使用されるスペースを計算するには、次のステートメントを実行できます。

SELECT T, table_name, sum(bytes)  as sum_bytes FROM 
(
    SELECT 'TABLE' as T, segment_name table_name, owner, bytes 
    FROM dba_segments WHERE segment_type = 'TABLE' 
) 
WHERE owner in UPPER('&owner') 
GROUP BY T, table_name, owner
HAVING SUM(bytes) > 100000
UNION ALL
  SELECT T, table_name, sum(bytes)  as sum_bytes FROM 
(
  SELECT 'INDEX' as T, i.table_name, i.owner, s.bytes 
  FROM dba_indexes i, dba_segments s 
  WHERE s.segment_name = i.index_name AND s.owner = i.owner AND s.segment_type = 'INDEX' 
) 
WHERE owner in UPPER('&owner') 
GROUP BY T, table_name, owner
HAVING SUM(bytes) > 100000; 
0
João Paraná