Oracleの数値型はどう選ぶ?NUMBER・INTEGER・BINARY_DOUBLE・PLS_INTEGERの使い分け
まず結論|Oracleの主要な数値型をどう選ぶか
Oracleの数値型は、名前の印象ではなく、保存する値の性質と処理する場所から選ぶことが大切です。
テーブル列で正確な10進数を扱うならNUMBER、近似値を使う大量計算ならBINARY_DOUBLE、PL/SQL内の整数演算ならPLS_INTEGERが基本候補になります。
さらに、整数だけを保存したい場合でも、OracleではINTEGERが独立した固定長整数型になるわけではない点を理解しておく必要があります。
型選びで最初に確認したいのは、整数か小数かという分類だけではありません。
10進数としての正確性が必要か、近似誤差を許容できるか、SQLの列として保存するのか、PL/SQL内だけで使用するのかを順番に確認します。
この判断順序を守ると、型名のイメージだけで選んでしまう失敗を防ぎやすくなります。
NUMBER・INTEGER・BINARY_DOUBLE・PLS_INTEGERの早見表
最初に主要な型の役割を並べると、選択の迷いを減らせます。
| 型 | 主な利用場所 | 数値の扱い | 主な用途 | 注意点 |
|---|---|---|---|---|
| NUMBER | SQL列・PL/SQL | 10進精度 | 金額、数量、ID、汎用数値 | 精度とスケールの設計が必要 |
| INTEGER | SQL列・PL/SQL | NUMBER(38)相当の整数 | 整数である意図を示す列 | 他DBの固定幅整数とは性質が異なる |
| BINARY_DOUBLE | SQL列・PL/SQL | 64ビットの2進浮動小数点 | 科学計算、統計処理、近似集計 | 10進小数を厳密に表せない場合がある |
| PLS_INTEGER | PL/SQLのみ | 32ビット符号付き整数 | ループ、カウンタ、配列添字 | 範囲外の演算でオーバーフローする |
INTEGERは独立した固定幅型として保存されるのではなく、OracleではNUMBER(38)へ対応付けられます。
そのため、INTEGERという型名を選んでも、他のデータベースにある32ビット整数型と同じ容量や範囲になるとは限りません。
NUMBERは用途が広く、整数、小数、金額、割合など、多くの業務データに利用できます。
一方で、用途が広いからこそ、必要な精度やスケールを決めずに使うと、想定外の値まで登録できる可能性があります。
BINARY_DOUBLEは大きな範囲の値や近似計算を扱いやすい型ですが、厳密な10進値を保存する用途とは相性がよくありません。
PLS_INTEGERはテーブル列ではなく、PL/SQLプログラム内で使う整数型である点が重要です。
迷ったときの基本的な選択手順
最初に、金額や税率のように10進数を正確に保つ必要があるかを確認します。
正確性が必要ならNUMBERを選び、許容できる最大値と小数桁からNUMBER(p,s)を決めます。
たとえば、最大999999.99までの金額を保存するなら、整数部6桁と小数部2桁を合わせてNUMBER(8,2)が候補になります。
近似誤差を許容でき、浮動小数点演算の特性が処理に合う場合はBINARY_DOUBLEを検討します。
科学計算や統計処理のように、非常に広い範囲の値を扱い、わずかな誤差を許容できる処理では候補になります。
処理対象がテーブル列ではなくPL/SQL内の整数変数なら、値の範囲を確認したうえでPLS_INTEGERを候補にします。
ループのカウンタや配列の添字など、値が32ビット符号付き整数の範囲に収まる処理では使いやすい型です。
一方で、累積件数や大きな計算途中の値を保持する場合は、上限を超えないかを確認する必要があります。
最初に避けたい選び方
INTEGERという名前だけを見て、4バイト整数や8バイト整数だと決めつけるのは危険です。
他DBのINTEGERと同じ感覚で設計すると、移行時に範囲や型変換の違いが問題になることがあります。
BINARY_DOUBLEを常に高速な型と考え、計測せずにNUMBERから置き換える方法も避けるべきです。
実際の処理時間は、型だけでなく、SQLの構造、データ件数、I/O、索引、暗黙変換、キャッシュの状態にも左右されます。
NUMBERは汎用的だからという理由だけで、すべての列を桁数指定なしにする方法にも注意が必要です。
保存できる値の範囲が広すぎると、アプリケーションの不具合や入力ミスをデータベース側で防ぎにくくなります。
型を選ぶときは、保存できるかどうかだけでなく、誤った値を拒否できるかという視点も必要です。
NUMBERとINTEGERの違いを正しく理解する
OracleのINTEGERを理解する要点は、INTEGERがNUMBERとは別の保存形式ではないことです。
両者の違いは主に定義の表現と制約の意図にあり、他DBの整数型と同じ感覚では判断できません。
INTEGERという名前から、小さな固定長整数を想像する人は少なくありません。
しかし、OracleではINTEGERを指定しても、内部的にはNUMBER系の型として扱われます。
この違いを知らないまま設計すると、ストレージ、値の範囲、移行性について誤った前提を持つ可能性があります。
INTEGERはOracle内部でどう扱われるか
OracleはANSI SQLのINTEGER、INT、SMALLINTを受け付けますが、列定義ではNUMBER(38)へ変換します。
そのため、SMALLINTという名前を使っても、小さな固定範囲の整数型が自動的に選ばれるわけではありません。
NUMBER(38)は最大38桁の精度を持つ整数として扱われ、一般的な32ビット整数より大きな値も表現できます。
一般的な32ビット符号付き整数の上限より大きな値も保存できるため、アプリケーション側のint型へそのまま受け取れないケースがあります。
ただし、INTEGERという表記は整数用途であることを読み手へ伝えやすいため、チームの命名方針として採用する意味はあります。
たとえば、数量、連番、回数など、小数を使わない列であることをSQL定義から読み取りやすくできます。
一方で、保存できる最大桁数を業務要件に合わせて制限したい場合は、NUMBER(10)やNUMBER(19)のように精度を明示するほうが分かりやすい場合があります。
型名が意図を示す方法と、精度が制約を示す方法のどちらを優先するかは、チームで統一しておくと保守しやすくなります。
NUMBERとINTEGERの実務上の使い分け
整数列に必要な最大桁数が明確なら、NUMBER(10)のように必要な精度を指定すると意図が伝わります。
たとえば、最大10桁までの管理番号であれば、NUMBER(10)とすることで11桁以上の値をデータベース側で拒否できます。
他DBとの移行を予定している場合は、OracleのINTEGERと移行先のINTEGERで範囲や格納形式が一致するとは限りません。
PostgreSQLやMySQLなどのINTEGERは固定された範囲を持つため、Oracle側のNUMBER(38)相当の値をそのまま移せない可能性があります。
新規設計では、名称の好みだけでなく、許容値をデータベース側でどこまで制限したいかを基準に選びます。
主キーであっても、将来どの程度まで採番されるかを考えずに必要以上に大きな精度を設定すると、外部システムとの型対応が分かりにくくなります。
逆に、現在の件数だけを基準に精度を狭くすると、長期運用で上限へ到達する可能性があります。
設計時は、現在値、年間増加量、運用予定年数、移行先の型を合わせて確認すると判断しやすくなります。
データディクショナリで定義を確認する
実際の列定義はUSER_TAB_COLUMNSのDATA_TYPE、DATA_PRECISION、DATA_SCALEで確認できます。
CREATE TABLEでINTEGERと書いた列が、データディクショナリではNUMBERとして表示されることを確認すると理解しやすくなります。
たとえば、INTEGER列とNUMBER(38)列を同じテーブルに作成し、USER_TAB_COLUMNSを参照すると、両者の定義がどのように格納されるかを比較できます。
確認時は表名が通常大文字で登録される点にも注意し、対象スキーマが異なる場合はALL_TAB_COLUMNSやDBA_TAB_COLUMNSを使い分けます。
DATA_PRECISIONが38、DATA_SCALEが0として表示されるかを確認すると、整数として扱われていることを判断できます。
一方で、桁数指定なしのNUMBERでは、DATA_PRECISIONやDATA_SCALEの表示が、明示指定した列とは異なる場合があります。
設計書の記載と実際の定義が一致しているかを確認する習慣は、移行や改修時の思い込みを防ぎます。
特に、長期間運用されているデータベースでは、設計書と実テーブルの定義が一致していないケースがあります。
変更前には必ずデータディクショナリを確認し、実際の値の最大桁数や小数桁も合わせて調査することが重要です。
NUMBERの精度とスケールを設計する
NUMBER(p,s)では、pが精度、sがスケールを表します。
この2つを正しく決めると、保存できる値を制御し、予期しない丸めや登録エラーを減らせます。
精度とスケールは、単に格納できる桁数を決めるだけの設定ではありません。
業務上許可される値をデータベース側で表現するための制約としても機能します。
precisionとscaleの意味
精度は数値全体で保持する有効桁数を表し、スケールは小数点から右側の桁数を表します。
たとえばNUMBER(8,2)は全体で8桁、小数部で2桁を使うため、整数部は最大6桁が目安になります。
商品価格を最大999999.99まで保存するならNUMBER(8,2)が候補になります。
ただし、負数を保存する場合でも、符号は精度の桁数には含めません。
小数点そのものも精度には含まれず、数字として保持する有効桁数が対象になります。
税率を0から100.000まで保存するなら、NUMBER(6,3)などが候補になります。
割合を0から1の小数として保存する場合は、必要な小数桁に応じてNUMBER(6,5)など別の定義が必要です。
同じ「税率」でも、10.000と保存するのか、0.10000と保存するのかによって適切な精度とスケールは変わります。
列定義の前に、値の単位と表現方法を決めておくことが重要です。
桁数指定ありと指定なしの違い
NUMBERだけを指定した列は、NUMBER(p,s)より柔軟に値を受け入れます。
柔軟性は便利ですが、アプリケーションが想定していない桁数の値まで登録できる可能性があります。
NUMBER(p)はスケールが0の整数として使えるため、件数や連番など小数を許可しない値に向きます。
たとえば、最大99999までの在庫数を想定するならNUMBER(5)が候補になります。
NUMBER(p,s)は業務ルールを列定義へ反映しやすく、入力値の誤りを早い段階で検出できます。
アプリケーション側の入力チェックを通過した値でも、データベース側の制約で不正値を拒否できるため、データ品質を保ちやすくなります。
一方で、将来の最大値や小数桁を狭く見積もると、仕様変更時に列定義の拡張が必要になります。
列定義の変更では、対象データ量、索引、制約、アプリケーション停止時間への影響を考慮する必要があります。
桁数指定なしのNUMBERは、外部システムから多様な値を受け取る一時テーブルや、用途が明確に限定されていない汎用領域で使われることがあります。
ただし、本番の業務テーブルで使う場合は、なぜ精度を制限しないのかを説明できる状態にしておくことが望ましいです。
丸めとORA-01438を防ぐ考え方
小数部が指定したスケールを超える値は、保存時に丸められる場合があります。
NUMBER(5,2)へ123.456を入れると、小数第3位以降が丸められ、123.46として扱われます。
この丸めが業務上許容されるかどうかは、値の種類によって異なります。
表示用の割合では問題にならなくても、会計金額や税計算の途中値では差異の原因になる可能性があります。
一方で、丸め後の値が許容される精度を超える場合は、ORA-01438が発生します。
たとえば、NUMBER(5,2)で保持できる整数部は3桁までであるため、1000.00のような値は保存できません。
エラーを防ぐには、入力値だけでなく、加算や乗算を行った途中結果の桁数も見積もる必要があります。
単価と数量を掛ける処理では、それぞれの桁数が範囲内でも、計算結果が保存先の精度を超える場合があります。
金額計算では、保存列のスケールと計算時の丸め規則を別々に決め、どの段階で丸めるかを明文化します。
税額を明細単位で丸めるのか、合計後に丸めるのかによって最終結果が変わることがあります。
ORA-01438が発生した場合は、対象列の定義だけでなく、INSERTやUPDATEで渡される式全体の結果を確認します。
暗黙変換や関数の戻り値によって、想定より大きな精度の値が生成されている場合もあります。
FLOAT・BINARY_FLOAT・BINARY_DOUBLEの違い
FLOAT、BINARY_FLOAT、BINARY_DOUBLEは名前が似ていますが、同じ保存形式ではありません。
FLOATはNUMBER系であり、BINARY_FLOATとBINARY_DOUBLEは2進浮動小数点型です。
名称だけを見ると、FLOATとBINARY_FLOATを同じ種類だと考えやすいため、内部表現の違いを意識する必要があります。
特に、他DBやプログラミング言語から移行する場合は、移行元のFLOATがどの精度と形式を持つかを確認することが重要です。
OracleのFLOATはNUMBER系の型
OracleのFLOATはNUMBERのサブタイプとして内部表現され、精度は10進桁ではなく2進精度で指定します。
FLOATの精度には1から126までを指定でき、格納サイズはNUMBERと同様に値に応じた範囲になります。
BINARY_FLOATという名前に近くても、FLOATをIEEE 754の32ビット型として扱うことはできません。
FLOAT(126)は高い2進精度を指定できますが、BINARY_DOUBLEと同じ固定長8バイト型になるわけではありません。
移行時は、移行元DBのFLOATがどの精度と表現を持つかを確認してから対応付けます。
アプリケーション側がfloatやdoubleとして値を送信していても、Oracle側の列がFLOATであれば、内部ではNUMBER系として扱われます。
逆に、Oracle側がBINARY_FLOATやBINARY_DOUBLEであれば、2進浮動小数点としての丸めや特殊値の扱いを考慮する必要があります。
BINARY_FLOATとBINARY_DOUBLEの特徴
BINARY_FLOATは32ビット単精度で4バイトを使い、BINARY_DOUBLEは64ビット倍精度で8バイトを使います。
| 型 | 精度の考え方 | サイズ | 特徴 |
|---|---|---|---|
| BINARY_FLOAT | 2進単精度 | 4バイト | 範囲と速度を重視する近似計算向け |
| BINARY_DOUBLE | 2進倍精度 | 8バイト | BINARY_FLOATより高い精度を持つ近似計算向け |
| NUMBER | 10進精度 | 1から22バイト | 10進数の正確性を重視する業務データ向け |
BINARY_DOUBLEはBINARY_FLOATより多くの有効桁を保持できるため、近似誤差を小さくしたい場合に向いています。
ただし、精度が高くても、10進小数を必ず正確に表現できるわけではありません。
2進浮動小数点型は無限大やNaNなどの特殊値を扱えるため、科学技術系の計算で役立つ場合があります。
ゼロ除算や定義できない演算を含む処理では、例外ではなく特殊値として結果が残るケースを考慮する必要があります。
ただし、特殊値を含む可能性がある処理では、集計や比較の前に値を検査する設計が必要です。
NaNを含む値の並び順や比較結果は、一般的な数値だけを扱う場合と異なるため、テストデータへ特殊値を含めることが重要です。
金融計算や厳密な一致判定に向かない理由
0.1のような10進小数は、2進浮動小数点では有限桁で正確に表現できない場合があります。
そのため、同じ計算式でも期待した10進表現とわずかに異なる結果が得られる可能性があります。
たとえば、0.1を複数回加算した結果が、表示上は同じでも内部的には期待値と完全一致しない場合があります。
金額、税額、残高、請求額のように端数規則と一致判定が重要な値には、通常はNUMBERを選びます。
BINARY_DOUBLEを使う場合は、等号だけで比較せず、用途に合った許容誤差を設ける必要があります。
許容誤差は固定値だけでなく、値の大きさに応じた相対誤差で判定する方法もあります。
ただし、業務データで許容誤差を導入すると、なぜ一致と判断したのかを説明しにくくなる場合があります。
厳密な照合、残高確認、請求金額の一致判定では、最初からNUMBERを利用するほうが設計を単純にできます。
PLS_INTEGERとSIMPLE_INTEGERの使いどころ
PLS_INTEGERとSIMPLE_INTEGERは、主にPL/SQLブロック内の変数や引数で使う型です。
テーブル列の型を選ぶ話と、PL/SQL内の演算型を選ぶ話を分けると混乱を防げます。
どちらも整数演算向けですが、NULLの扱いやオーバーフロー時の動作に違いがあります。
置き換えによる性能改善を期待する場合でも、処理全体のボトルネックが整数演算にあるかを確認する必要があります。
PLS_INTEGERが向いている処理
PLS_INTEGERは32ビットの符号付き整数を保持し、値の範囲はマイナス2147483648から2147483647です。
ループ回数、配列添字、処理件数、短い整数演算など、範囲が明確な処理に向きます。
たとえば、FORループの制御値や、コレクションの添字、バッチ内の処理済み件数などに利用できます。
PLS_INTEGER同士の演算が範囲を超えるとオーバーフロー例外が発生するため、大きな中間結果にも注意が必要です。
結果をNUMBER変数へ代入する場合でも、先にPLS_INTEGER同士で計算すると代入前にオーバーフローすることがあります。
たとえば、大きな値を掛け合わせる場合は、一方をNUMBERへ変換してから演算する方法を検討します。
ただし、すべてをNUMBERへ変換すると、PLS_INTEGERを選ぶ目的が薄れる場合があります。
値の範囲が明確で、演算回数が多い部分に限定して使うと、意図を保ちやすくなります。
SIMPLE_INTEGERの特徴と制約
SIMPLE_INTEGERはPLS_INTEGERのサブタイプで、NOT NULL制約を持ちます。
NULLを取らず、オーバーフロー検査を必要としない性能重視の処理では候補になります。
初期値を設定せずに宣言できないため、変数が常に有効な整数を持つ処理に向いています。
オーバーフロー時の扱いはPLS_INTEGERと異なるため、値が必ず範囲内に収まることを確認せずに置き換えるべきではありません。
境界値付近の演算では、期待していない折り返しが発生する可能性があります。
入力値が外部データやテーブル内容に依存する場合は、範囲を保証できるかを慎重に確認します。
性能だけを理由にSIMPLE_INTEGERへ変更すると、エラーとして検出できていた問題を見逃す可能性があります。
NUMBERから置き換える前の確認事項
最初に、変数がPLS_INTEGERの範囲を超える可能性がないかを確認します。
次に、NULLを保持する必要があるか、SQLとの受け渡しで暗黙変換が増えないかを確認します。
テーブルのNUMBER列をPLS_INTEGER変数へ取得する場合は、列に範囲外の値が存在しないことを保証する必要があります。
変数の型だけを変えても、SQL実行時間やI/Oが支配的な処理では体感できる改善が出ない場合があります。
大量の行を取得する処理では、型の演算速度よりもSQLの実行計画やネットワーク転送のほうが大きな影響を持つ場合があります。
置き換え前後で処理時間と例外動作を測定し、保守性を損なわない範囲で採用します。
正常系だけでなく、最大値、最小値、NULL、範囲外の入力を使ったテストも必要です。
用途別に最適な数値型を選ぶ
数値型は、保存する値と計算方法を具体的にすると選びやすくなります。
ここでは実務で迷いやすい用途ごとに、優先する判断基準を整理します。
同じ整数や小数でも、利用目的が異なれば必要な精度、範囲、制約は変わります。
用途を決めずに型だけを選ぶのではなく、値がどこから入り、どこで計算され、どこへ渡されるかを確認します。
金額・税率・割合を保存する場合
金額は10進数としての正確性が必要になるため、基本はNUMBER(p,s)を選びます。
pは最大金額と小数部を含めた総桁数から決め、sは通貨や業務ルールで必要な小数桁から決めます。
日本円だけを扱う場合でも、税計算や外貨換算の途中値に小数が必要かを確認します。
保存時に丸めるのか、計算途中では多めの桁を保持するのかを決めると、端数差異を説明しやすくなります。
たとえば、画面表示は小数2桁でも、計算途中では小数4桁や6桁を保持する設計があります。
最終的に保存する列と、計算途中で使う変数や式の精度を同じにする必要はありません。
BINARY_DOUBLEは近似誤差があるため、会計上の確定値をそのまま保存する用途には向きません。
割引率や換算率でも、計算結果が請求額へ影響する場合はNUMBERを使うほうが安全です。
主キー・件数・在庫数を保存する場合
NUMBER(10)やNUMBER(19)のように必要範囲を明示すると、外部APIやアプリケーション型との対応を整理しやすくなります。
INTEGERを使う場合もOracleではNUMBER(38)へ対応付けられるため、固定幅整数として容量が小さくなるとは限りません。
主キーでは現在の件数だけでなく、将来の採番数、データ移行、分散環境での採番方法を確認します。
外部キーは参照先の主キーと同じ精度で定義し、暗黙変換が発生しないようにします。
在庫数や処理件数では、負数を許可するかどうかも重要な判断材料です。
型だけでは負数を禁止できないため、必要に応じてCHECK制約と組み合わせます。
数量の上限が業務上決まっている場合は、必要以上に大きなNUMBERを使わず、適切な精度を指定するとデータ品質を保ちやすくなります。
科学計算・統計処理・大量集計を行う場合
近似値で問題がなく、浮動小数点演算を大量に行う処理ではBINARY_DOUBLEを検討できます。
ただし、型を変えれば必ず速くなるとは限らず、SQL、データ量、CPU、暗黙変換の有無で結果は変わります。
NUMBER列を毎回BINARY_DOUBLEへ変換する処理では、変換コストが利点を打ち消す可能性があります。
入力列、計算式、集計結果の型をそろえると、不要な変換を減らしやすくなります。
採用前に代表的なデータで精度差と処理時間を測り、許容誤差を満たすかを確認します。
平均、分散、標準偏差などの計算では、入力値の範囲や件数によって誤差の蓄積が変わる可能性があります。
少量のテストデータだけで判断せず、本番に近い件数と値の分布で検証することが重要です。
PL/SQLバッチで整数演算を行う場合
PL/SQL内のカウンタや配列添字にはPLS_INTEGERが分かりやすい候補です。
NULLが不要で値が範囲内に収まることを保証できる場合は、SIMPLE_INTEGERも検討できます。
一方で、非常に大きな件数や累積値を扱う場合は、PLS_INTEGERの上限を超えないか確認が必要です。
日次処理では問題がなくても、月次や全期間の累積処理で上限へ近づく可能性があります。
SQL列との受け渡しが多い処理では、変換回数を含めて全体を測定してから選びます。
処理時間の大部分がSELECT、UPDATE、COMMITに使われている場合は、変数型の変更だけでは大きな改善が得られないことがあります。
型の最適化より先に、SQL実行回数、バルク処理、索引、コミット単位を確認するほうが効果的な場合もあります。
ストレージと性能を比較するときの注意点
数値型の性能は、型の仕様だけでは決まりません。
値の分布、行数、演算、索引、実行計画、変換処理を含めて比較する必要があります。
ストレージサイズが小さい型を選べば必ず速くなるわけでもありません。
データがメモリやキャッシュに収まるか、どの程度I/Oが発生するかによって影響は変わります。
可変長のNUMBERと固定長の2進型
NUMBERは値に応じて1から22バイトを使用する可変長型です。
BINARY_FLOATは4バイト、BINARY_DOUBLEは8バイトの固定長です。
小さな整数が多いNUMBER列では、BINARY_DOUBLEより常に大きな領域を使うとは限りません。
一方で、桁数の大きなNUMBERや小数桁の多い値では、1件あたりの格納サイズが大きくなる可能性があります。
実際の格納サイズを確認する場合は、VSIZEなどを使って代表的な値を測定できます。
ただし、列値のサイズだけでは、行全体、ブロック、索引、圧縮による影響までは判断できません。
テーブル全体の容量を比較するときは、NULL率、行オーバーヘッド、索引数、データの偏りも考慮します。
性能差を左右する条件
単純な浮動小数点演算では2進型が有利になる可能性がありますが、実際のSQLではI/Oや結合が支配的な場合があります。
索引を使う条件式で列側へ型変換をかけると、既存索引を効率よく使えない可能性があります。
たとえば、NUMBER列をBINARY_DOUBLEへ変換して比較すると、通常のNUMBER列索引をそのまま利用できない場合があります。
NUMBERとBINARY_DOUBLEを混在させる式では暗黙変換が発生し、精度と速度の両方へ影響します。
バインド変数の型も実行計画や変換へ関係するため、アプリケーション側まで含めて確認します。
同じSQLでも、JavaのBigDecimalを渡す場合とdoubleを渡す場合で、Oracle側の変換が異なる可能性があります。
統計情報、データ分布、パラレル実行、ハードウェア構成も性能差へ影響します。
比較テストで確認する項目
比較では実行時間だけでなく、実行計画、論理読取り、物理読取り、CPU時間、メモリ使用量を確認します。
同じデータ量と同じキャッシュ条件で複数回測り、初回だけの差を結論にしないことが大切です。
初回実行では、ディスク読取り、SQL解析、キャッシュ準備の影響を受ける可能性があります。
BINARY_DOUBLEを含む比較では、期待値との差、集計後の誤差、境界値の比較結果も記録します。
性能が向上しても、許容できない精度差が発生するなら採用できません。
性能改善が小さい場合は、型変更による移行コストや可読性低下に見合うかを判断します。
テーブル定義の変更、データ変換、索引再作成、アプリケーション修正、テスト工数まで含めて評価する必要があります。
他DB・Rails・Java・Pythonとの型対応
外部システムと連携する場合は、Oracle内の定義だけでは型を決められません。
移行元DB、ORM、ドライバ、アプリケーション言語で扱える範囲と精度をそろえる必要があります。
同じINTEGERやFLOATという名前でも、製品や言語によって意味が異なる点に注意が必要です。
型名を直接対応付けるのではなく、最小値、最大値、有効桁数、小数桁、NULL、符号の有無を比較します。
PostgreSQLやMySQLから移行する場合
他DBのINTEGERやBIGINTは固定幅の整数として扱われることが多く、OracleのINTEGERとは範囲の考え方が異なります。
| 移行元の代表的な型 | Oracle側の候補 | 確認する点 |
|---|---|---|
| SMALLINT | NUMBER(5)など | 符号と実際の最大値 |
| INTEGER | NUMBER(10)など | 32ビット範囲との整合 |
| BIGINT | NUMBER(19)など | 64ビット範囲との整合 |
| DECIMAL(p,s) | NUMBER(p,s) | 精度、スケール、丸め規則 |
| FLOAT・DOUBLE | BINARY_FLOAT、BINARY_DOUBLE、NUMBER | 元DBの精度と誤差要件 |
移行ツールが自動生成した型も確認し、名称が似ているだけで採用しないことが重要です。
MySQLのUNSIGNED整数を移行する場合は、符号付き整数より大きな正数を保持できる点を考慮します。
Oracle側でNUMBERへ移すことはできても、Javaや他システムの符号付き整数型へ受け取れない可能性があります。
DECIMALやNUMERICの移行では、精度とスケールだけでなく、丸め規則や演算結果の扱いも検証します。
既存データの最大値と最大小数桁を調査し、定義上の上限だけでなく実データに基づいて移行先を決めることが大切です。
Railsのマイグレーションで確認すること
Railsではinteger、bigint、decimalなどの抽象型を使いますが、Oracle上の最終定義はアダプタの実装やバージョンに左右されます。
金額にはdecimalを使い、precisionとscaleを明示して、生成されたNUMBER定義を確認します。
precisionやscaleを省略すると、意図した列定義にならない可能性があります。
IDや外部キーでは、主キー側と参照側の桁数が一致しているかを確認します。
片方だけbigint相当で、もう片方が小さな精度になっていると、将来の値で登録できなくなる可能性があります。
マイグレーション実行後は、Railsの定義ファイルだけでなく、Oracleのデータディクショナリも確認します。
アダプタやRailsのバージョンを変更した場合は、新規作成される列定義が以前と同じかをテスト環境で確認することが重要です。
Java・Pythonで精度を失わない受け取り方
JavaでNUMBERの10進精度を保ちたい場合は、JDBCのgetBigDecimalとjava.math.BigDecimalが基本候補になります。
整数として受け取る場合は、列の最大値がintやlongの範囲に収まるかを確認します。
OracleのINTEGER列がNUMBER(38)相当であることを知らずにintへ変換すると、範囲外の値でエラーになる可能性があります。
PythonではNUMBERをfloatへ変換すると2進表現になるため、金額などではdecimal.Decimalの利用を検討します。
python-oracledbにはNUMBERをDecimalとして取得する設定があるため、必要な精度と処理速度を確認して使い分けます。
大量データを処理する場合は、すべてをDecimalへ変換することで処理時間やメモリ使用量が増える可能性があります。
列ごとに精度要件を分け、金額はDecimal、近似計算値はfloatというように使い分ける方法があります。
アプリケーションとデータベースの境界では、暗黙変換へ任せず、期待する型を明示するほうが問題を発見しやすくなります。
よくある失敗とトラブルの対処法
数値型の問題は、列定義だけでなく、計算途中やアプリケーションとの境界で発生します。
原因を切り分けるときは、値、列定義、式の型、変換先の順に確認します。
エラーが発生したSQLだけを見るのではなく、値が生成された過程を追うことが重要です。
同じ列へ正常に登録できる値と失敗する値を比較すると、精度や変換の問題を特定しやすくなります。
精度超過や予期しない丸めが起きる
ORA-01438が出たら、対象列のDATA_PRECISIONとDATA_SCALEを確認します。
次に、登録値だけでなく、式の計算結果が何桁になるかを確認します。
乗算、除算、SUMなどを含む式では、元の列より多くの桁が必要になる場合があります。
予期しない丸めでは、列のスケール、ROUNDの位置、暗黙変換、アプリケーション側の丸め規則を比較します。
データベース側とアプリケーション側で異なるタイミングに丸めると、最終結果が一致しない可能性があります。
文字列からNUMBERへ変換している場合は、小数点や桁区切りの形式も確認します。
セッションの数値書式設定によって、同じ文字列が異なる解釈になる可能性があります。
BINARY_DOUBLEの計算結果がずれる
表示上は同じ小数でも、内部の近似値が異なると等号比較が期待どおりにならない場合があります。
比較では用途に合った許容誤差を決め、絶対差や相対差で判定します。
非常に小さな値と非常に大きな値を同じ固定誤差で比較すると、適切に判定できない場合があります。
金額など厳密な10進値へ戻す場合は、どの桁で丸めてNUMBERへ変換するかを明示します。
変換前にROUNDを行うのか、NUMBERへ変換した後にROUNDするのかによって結果が変わる可能性があります。
NaNや無限大が入り得る処理では、通常の数値だけを前提にせず、特殊値を先に検査します。
集計結果に特殊値が混ざると、後続処理やアプリケーション側で予期しない動作が起こる可能性があります。
移行・CAST・PL/SQLでエラーが発生する
CASTのエラーでは、変換元の最大値、小数桁、文字列形式、変換先の精度を確認します。
他DBからの移行では、移行元の整数範囲がOracle側の定義へ収まるかを実データで検証します。
定義上は収まるように見えても、移行ツールが途中で別の型へ変換している場合があります。
一時ファイルやCSVを経由する場合は、指数表記、桁区切り、小数点記号、空文字の扱いも確認します。
PLS_INTEGERのオーバーフローでは、計算結果をNUMBERへ代入していても、演算の両辺がPLS_INTEGERなら先に例外が起こり得ます。
計算前に一方をNUMBERとして扱うか、最初からNUMBER変数を使うかを処理要件に合わせて選びます。
NULLをSIMPLE_INTEGERへ代入しようとしていないか、初期値を設定しているかも確認が必要です。
境界値を使った単体テストを追加すると、運用開始後のオーバーフローを防ぎやすくなります。
数値型選びのFAQと最終チェック
最後に、数値型を決めるときに残りやすい疑問を整理します。
型名ではなく、精度、範囲、処理場所、連携先を確認すれば、多くの設計ミスを防げます。
一度決めた型を後から変更するには、データ移行、索引再作成、アプリケーション修正が必要になる場合があります。
設計段階で確認項目を整理しておくことが、長期的な保守コストの削減につながります。
INTEGERとNUMBERはどちらを使うべきか
整数である意図を簡潔に示したいならINTEGERを使えますが、OracleではNUMBER(38)へ対応付けられます。
許容する桁数を明確に制限したいなら、NUMBER(p)のほうが設計意図を表しやすい場合があります。
たとえば、32ビット整数相当の範囲へ合わせたい場合は、NUMBER(10)を候補にしつつ、実際の上限をCHECK制約で補う方法があります。
チーム内で表記を統一し、他DB移行時にOracle固有の対応関係を見落とさないことが重要です。
既存システムとの一貫性を重視する場合は、現在使われている表記と制約方法も確認します。
桁数指定なしのNUMBERを使ってよいか
外部から届く値の範囲が広く、列側で狭い制約を設けにくい場合は、桁数指定なしのNUMBERが役立ちます。
一方で、金額、数量、コード値のように上限や小数桁が決まっているなら、NUMBER(p,s)で制約を明示したほうが安全です。
柔軟性を優先する理由がないままNUMBERだけを使うと、誤った値をデータベースが受け入れる可能性があります。
一時的な取込テーブルでは桁数指定なしのNUMBERを使い、検証後に本番テーブルのNUMBER(p,s)へ登録する設計もあります。
用途に応じて、受け入れ用の柔軟な列と、業務ルールを守る厳密な列を分ける考え方が有効です。
設計前に確認するチェックリスト
数値型を確定する前に、次の項目を一つずつ確認します。
- 保存する最小値と最大値を確認する。
- 現在値だけでなく将来の増加量を確認する。
- 小数を使うか、必要な小数桁はいくつかを確認する。
- 計算途中で必要になる桁数を確認する。
- 10進数としての正確性が必要かを確認する。
- 近似誤差を許容できるかを確認する。
- SQL列かPL/SQL変数かを確認する。
- NULLと負数を許可するかを確認する。
- 他DB、ORM、Java、Pythonでの受け取り方を確認する。
- 主キーと外部キーの精度が一致しているかを確認する。
- 暗黙変換や索引への影響を確認する。
- 丸めを行う場所と規則を確認する。
- 性能が目的なら実データで測定する。
- 最大値、最小値、NULL、範囲外の値でテストする。
- データディクショナリで実際の定義を確認する。
迷った場合は、一般的な業務データにはNUMBERを基準にし、近似計算やPL/SQL内の整数演算だけを別の型として検討する考え方が安全です。
型の特徴だけで決めず、保存精度、処理場所、外部連携、運用期間をまとめて確認することが、失敗しにくい数値型設計につながります。