BFT名古屋 TECH BLOG

日々の業務で得た知識を所属するエンジニアたちがアウトプットしていきます。

【Oracle,PostgreSQL,Ora2pg】Oracle-PostgreSQL非互換対処方法一覧

はじめに

こんにちは!
株式会社BFT名古屋支店・新卒の「なる」と「もな」です!
ついに2023年になってしまいましたね。もうそろそろ新卒と言えなくなってしまします、、泣
社会人、時間の流れが早すぎるを実感している今日この頃です。。

さて、今回はDB間の非互換の対処法に関して学んだことを共有していきたいと思います!

なかなかまとまった資料がないので、私たちの記事が皆さんの参考になればうれしいです!

背景

現在携わっている案件で、OracleからPostgreSQLDDLを移行するお仕事がありました。
Oracleと全く同じ構文のDDLPostgreSQLに移行できればよいのですが、残念ながらそうではありません。。
まず、OraclePostgreSQLDDLには互換性があるものとないものがあります。

<互換性のないものの例>
etc...


今回はOra2pgを使用して、このようなDDLの非互換を埋めようとしました。

Ora2pgとは

OracleMySQLデータベースを PostgreSQLに移行するための無料ツールです。
OracleMySQLからDDL等のデータをエクスポートし、PostgreSQLにデータ等をインポートすることでデータベースを移行することができます。
詳しくはこちらを参照してください。
https://ora2pg.darold.net/(公式サイト)

しかし、Ora2pgでは埋めることができなかった非互換や、Ora2pgによる誤変換がいくつかあったので、それらの対処方法を以下にまとめました。
使用した環境は以下の通りです。

使用環境
Ora2pgサーバ
  • Azure VM
  • イメージ:Red Hat Enterprise Linux 7.4 -Gen1
  • VMサイズ:Standard DS1 v2
  • Ora2pgバージョン:23.1
  • sqlplusバージョン:oracle-instantclient12.2-sqlplus-12.2.0.1.0-1.x86_64
  • psqlバージョン:postgresql14.x86_64
Oracleサーバ
  • Azure VM
  • イメージ:Windows sever 2016 Datacenter Gen2
  • VMサイズ:Standard D2s v3
  • Oracleバージョン:12.2.0.1.0
PostgreSQLサーバ
  • Azure Database for PostgreSQLフレキシブルサーバー
  • PostgreSQLバージョン:14
  • コンピューティングサイズ:Standard_B1ms(1vCore、2GiB、640最大iops)
  • ストレージ:32
  • 接続方法:パブリックアクセス(許可されているIPアドレス)を選択

非互換一覧(DDLの観点から)

NO CACHE(シーケンス)

PostgreSQLではCACHE 1と設定する

モリー上で順序番号を事前に割り当てないように設定したいとき、OracleではNO CACHE、PostgreSQLではCACHE句を削除する、もしくはCASHE 1と設定します。
Ora2pgで自動変換を行った場合はNO CACHE句を削除することで対応しますが、シーケンスの歯抜けを防ぐためにCASHE 1に手動修正することを推奨します。
(以下参考)
https://www.postgresql.org/docs/14/sql-createsequence.html

ORDER

PostgreSQLではCACHE 1と設定する

Oracleで使用しているORDER句は、PostgreSQLには存在しません。
OracleではNO CASHに設定する、もしくは(cacheの値が2以上であっても)ORDERを指定することで要求順通りに採番可能ですが、PostgreSQLでは上記の通りORDER句にあたる句や機能が存在しないため、OracleのNO CACHEと同義であるCACHE 1に設定することで順序通りの採番を可能にします。

PCTFREE

PostgreSQLではWITH(FILLFACTOR= )と設定する

Oracleで利用しているPCTFREE句はPostgreSQLには存在しません。Ora2pgの自動変換でも変換時に削除されます。 ただし、PCTFREE句はFILLFACTORで代用可能です。FILLFACTORは空き領域割合ではなく、挿入で使用可能な領域割合を指定するため、下記の式のように設定します。
FILLFACTOR = 100 - PCTFREE
(例)PCTFREE=10の場合、FILLFACTOR=100-10=90となるので、WITH(FILLFACTOR=90)と追記します。

NUMBER

小数を扱う場合、PostgreSQLではnumericを使用する

Oracleで使用できるNUMBER型はPostgreSQLには存在しないため、Ora2pgを使用すると、自動的に以下のように変換されます。

  • 整数である(スケールが0と指定されている)場合、値に応じて自動的にsmallint, bigint, integerに変換される
  • 小数である(スケールに1以上の数値が指定されている)場合、値に応じて自動的にdouble precision及びrealに変換される

Ora2pgによる整数の変換に関しては問題ありませんが、小数の変換には注意が必要です。double precision及びrealは内部的には二進数で扱われるため、 四則演算等を行った際に計算結果が異なる場合があります。
より正確な値を必要とする小数を扱う際は、double precision及びrealではなくnumericを使用することを推奨します。
※numericを使用する場合は精度とスケールを指定してください。指定しない場合、計算速度が劣化する可能性があります。

DATE

PostgreSQLではTAIMESTAMP型に修正

OracleのDATE型(7バイト、固定長)は日付と時刻が格納されます。
PostgreSQLにもDATE型は存在しますが、PostgreSQLのDATE型は日付のみ(時刻なし)格納されるため、 日付と時刻の両方が格納されるTIMESTAMP型(8バイト)を用いて修正を行います。

CURRENT_DATE

PostgreSQLではCURRENT_TIMESTAMPに修正

OracleのDATE型は、PostgreSQLのTIMESTAMP型と同じく、システム的には時間(hh:mm:ss)まで保持しています。
OracleのCURRENT_DATEでは時間まで格納しますが、PostgreSQLのCURRENT_DATEでは日付までしか格納しません。
そのため、CURRENT_DATEをCURRENT_TIMESTAMPに手動修正します。ただしCURRENT_TIMESTAMP関数は小数点以下6桁まで扱うため、Oracleと表記を合わせるためにはCURRENT_TIMESTAMPの末尾に精度を記載する必要があります。
(例)CURRENT_TIMESTAMP(0) →2023/2/9 12:34:56
CURRENT_TIMESTAMP(4) →2023/2/9 12:34:56.7890

ON COMMIT

PostgreSQLにおいてもON COMMIT句は存在するため、追記する

OracleのON COMMIT句はPostgreSQLにおいても存在します。ON COMMIT句はOraclePostgreSQLで互換性がありますが、Ora2pgの自動変換により削除されてしまうため、手動で追記する必要があります。 表記の仕方はOracleと同じです。

CYCLE

PostgreSQLにおいてもCYCLE句は存在するため、追記する

OracleのCYCLE句はPostgreSQLにおいても存在します。CYCLE句はOraclePostgreSQLで互換性がありますが、Ora2pgの自動変換により削除されてしまうため、手動で追記する必要があります。 表記の仕方はOracleと同じです。

LOGGING/NO LOGGING

PostgreSQLにはLOGGING/NO LOGGING句に当たる句は存在しないため削除する

REDOログへの記録を許可/禁止するOracleのLOGGING/NO LOGGING句はPostgreSQLでは存在しないため削除します(Ora2pgの自動変換においても削除されます)。 PostgreSQLではREDOログではなく、WALというトランザクション・ログ(データベースに対する変更履歴)を記録しておく機能があります。
更新ログをとらないという点に関しては、PostgreSQLではUNLOGGEDテーブルを作成するという手もあります。
しかし、OracleのNO LOGGING句は最小限の更新ログ(UPDATE・DELETE・通常のINSERT)がREDOに記録されるのに対し、UNLOGGEDテーブルは一切の更新ログをWALに書き出さないため、完全な代替手段とは言えません。

USING INDEX

PostgreSQLでは自動的に一意インデックスを作成するので、USING INDEX句は削除する

OracleのUSING INDEX内でENABLEを指定している場合、制約が有効化されており、これはOracleではデフォルトとなります。 PostgreSQLではALTER TABLE~で制約・インデックスが作成されることで、Oracleと同じ動作になります。 Oracleは、表にUNIQUEキー整合性制約またはPRIMARY KEY整合性制約を規定するために、一意キーまたは主キーの一意索引を作成します。 この索引は、制約を使用可能にしたときに、データベースによって自動的に作成され、必要に応じて、USING INDEX句を指定して索引作成を制御できます。
PostgreSQLでは、テーブルに一意性制約またはPRIMARY KEYが定義されると、自動的に一意インデックスを作成します。このインデックスが、PRIMARY KEYや一意性制約(適切ならば複数列のインデックスで)となる列に対して作成され、この制約を強制する機構となります。
※手作業で一意列に対しインデックスを作成する必要はありません。 これは、単に自動作成されるインデックスを二重にするだけです。
(参考資料) www.postgresql.jp

DISABLE

PostgreSQLでは制約を無効にする

DISABLE句はNOVALIDATEがデフォルトであり、OracleでDISABLE句だけを指定してしている場合、インデックスが削除され制約も無効になります。
DISABLE句とVALIDATE句を両方指定することで「インデックスが削除」and「制約が有効」となります。 OracleでDISABLE句だけを指定してしている場合にOra2pgでDDLを変換すると、「ALTER TABLE xxxx ADD PRIMARY KEY」等の制約の文が別で作成されたままなので、変更後のDDLから制約を作成する文を削除します。
※VALIDATE、NOVALIDATEの詳細については以下を参照してください
データ整合性

おわりに

今回はDDL側から非互換を発見し対処をしましたが、DDL以外の観点で考えるともっと非互換対処が必要になってきます。 OracleからオープンソースPostgreSQLに移行するのはなかなか大変ですね。。リリースの際に何も起こらないことを願うのみです。
ちなみに、PostgreSQL上にOracleと互換性のある関数やデータ型を提供するorafceという拡張モジュールもありますので、そちらもぜひ利用してみください。

また、次回もOracleからPostgreSQLの移行の際に学んだことを伝えていけたらなと思います!
引き続き株式会社BFT名古屋支店・新卒の「なる」と「もな」をよろしくお願いいたします~!