Amazon Web Services ブログ
SYSDATE 関数を Oracle から PostgreSQL に変換する
AWS クラウドで Oracle から PostgreSQL に移行するプロセスは何段階もあって複雑になりがちです。評価ステージから切り替えステージまで、さまざまなテクノロジーとスキルが必要になります。移行プロセスの詳細については、「データベースの移行 — 開始する前に知っておくべきこと」、「移行プロセスとインフラストラクチャの考慮事項」、「ソースデータベースの考慮事項」と「PostgreSQL 環境でのターゲットデータベースの考慮事項」を参照してください。
Oracle から Amazon RDS または Amazon Aurora PostgreSQL に移行する際に最もよくある問題の 1 つが、SYSDATE
関数にまつわる問題です。SYSDATE
は、アプリケーションまたはストアドプロシージャとトリガーで最も一般的に使用される日付/時刻関数です。creation_date、last_updated_date、approved_date のような列を持つテーブルは、列のデフォルト値を SYSDATE
にすることにより、または、列の値を挿入または更新するトリガーを SYSDATE
として定義することにより、SYSDATE
値として更新されます。
この記事は、PostgreSQL データベースの Oracle SYSDATE
関数の代替案を示します。
Oracle および PostgreSQL の日付/時刻関数を評価する際に考慮すべき 3 つの異なる側面があります。それは、次の側面です。
- ステートメントレベルまたはトランザクションレベルの時刻
- 時刻関数におけるクライアント側設定の影響
- 夏時間 (DST) に関する考慮事項
PostgreSQL 日付/時刻関数の概要
PostgreSQL には、現在の日時に関連する値を返す関数がいくつか用意されています。これらの関数の一部は SQL 標準であり、その他は非 SQL 標準関数です。
サポートされる SQL 標準関数
サポートされている SQL 標準関数は次のとおりです。
CURRENT_DATE
CURRENT_TIMESTAMP/CURRENT_TIMESTAMP(precision)
CURRENT_TIME/CURRENT_TIME(precision)
LOCALTIME/LOCALTIME(precision)
LOCALTIMESTAMP/LOCALTIMESTAMP(precision)
これらの関数は、現在のトランザクションの開始時刻を返します。同じトランザクションでこれらの関数を複数回実行しても、値に変化はありません。この動作は意図的なもので、機能と見なされます。トランザクション全体で一貫したタイムスタンプが必要な場合は、これらの関数を使用できます。
CURRENT_DATE
CURRENT_DATE
関数は、現在の日付を yyyy-mm-dd 形式で表示します。次のコードを参照してください。
CURRENT_TIMESTAMP/CURRENT_TIMESTAMP(precision) and CURRENT_TIME/CURRENT_TIME(precision)
関数 CURRENT_TIMESTAMP/CURRENT_TIMESTAMP (precision)
と CURRENT_TIME/CURRENT_TIME (precision)
は、タイムゾーンとともに現在の日付と時刻を返します。オプションで、この関数の精度パラメータを使用できます。この精度は、小数桁 (ミリ秒) を四捨五入します。次のコードを参照してください。
LOCALTIME/LOCALTIME(precision) and LOCALTIMESTAMP/LOCALTIMESTAMP(precision)
関数 LOCALTIME/LOCALTIME (precision)
と LOCALTIMESTAMP/LOCALTIMESTAMP (precision)
は、タイムゾーンなしで現在の日付と時刻を返します。オプションで、この関数の精度パラメータを使用できます。この精度は、小数桁 (ミリ秒) を四捨五入します。次のコードを参照してください。
サポートされる非 SQL 標準関数
サポートされている非 SQL 標準関数は次のとおりです。
transaction_timestamp()
statement_timestamp()
clock_timestamp()
timeofday()
now()
PostgreSQL は、現在のステートメントの開始時刻と、関数が呼び出された時の実際の現在時刻を返す関数も用意しています。
transaction_timestamp() と statement_timestamp()
transaction_timestamp
関数は、current_timestamp
と同じように動作します。ただし、名前が示すように、トランザクションの開始時刻を返し、トランザクション全体で一貫しています。statement_timestamp
関数は、トランザクションに関係なく、ステートメントの開始時刻を返します。
statement_timestamp()
と transaction_timestamp()
は、トランザクションの最初のコマンド中に同じ値を返しますが、後続のコマンド中には異なる値を返すかもしれません。次のコードを参照してください。
clock_timestamp() と statement_timestamp() の比較
clock_timestamp()
関数は実際の現在時刻を返すため、単一の SQL コマンド内でその値が変更されます。
次のコード例は、clock_timestamp
は同じコマンドで異なるタイムスタンプ値を返すが、statement_timestamp()
は同じ値を返すことを示しています。
timeofday () と clock_timestamp () の比較
関数 timeofda()
と clock_timestamp()
はどちらも同じように動作します。唯一の違いは、timeofday()
がテキストデータタイプを返し、clock_timestamp()
がタイムゾーン付きのタイムスタンプを返すことです。次のコード例の pg_typeof
列では、clock_timestamp()
関数ではタイムゾーン付きのタイムスタンプを、timeofday
ではテキストを示しています。
now()
now()
関数は、transaction_timestamp()
と同等の従来の PostgreSQL です。次のコード例では、両方の関数が同じタイムスタンプを示しています。
使用する PostgreSQL 関数を選択する
Oracle SYSDATE
関数は、ステートメントの実行時に DB サーバーの日付/時刻を返します。したがって、長期実行トランザクションでは、複数の SYSDATE
関数がある場合、ステートメントを実行するたびに異なる時刻が返されます。
次の Oracle コード例では、SYSDATE
関数実行の 2 つの異なる値を確認できます。Oracle SYSDATE
関数は、トランザクションがいつ開始されたかに関係なく、ステートメントの開始時刻を返します。
次の PostgreSQL コード例では、単一のトランザクションを使用して、さまざまな間隔でさまざまな PostgreSQL 日付および時刻関数による時刻の戻りをキャプチャできます。次のコードは 15 秒間スリープし、前後の結果を比較します。出力は、次のように、どの関数が SYSDATE
と同じ動作をするかを判断するための評価を行います。
次のコードは結果を示しています。
clock_timestamp()
関数のみが、単一のトランザクションで異なるタイミング情報を返します。したがって、PostgreSQL の Oracle SYSDATE
関数の代わりになる最も近い一致は、clock_timestamp()
です。しかし、これらの値は単なるタイムスタンプなので、これだけでは不十分です。異なるタイムゾーンには同じ時点で異なるタイムスタンプ値があるため、タイムゾーンと DST も考慮する必要があります。そうしないと、予想とは異なる値が表示される場合があります。
クライアント側のタイムゾーン設定の影響
SYSDATE()
や SYSTIMESTAMP()
などの Oracle の日付/時刻関数は、クライアントやセッションのタイムゾーン設定に関係なく、データベースサーバーのタイムゾーンの現在の日付と時刻を返します。ただし、PostgreSQL の日付/時刻関数は、クライアントまたはセッションのタイムゾーン設定に従って時刻を返します。PostgreSQL では、タイムゾーン値のタイムスタンプは UTC に内部的に保存され、クライアントに表示されるときにタイムゾーン設定パラメータで指定されたゾーンの現地時間に変換されます。
Oracle での影響
Oracle では、クライアントレベルまたはセッションレベルのタイムゾーン設定に関して、SYSDATE
関数への影響はありません。次のコード例では、タイムゾーン設定を変更しても、SYSDATE
の結果には影響しませんでした。
セッションのタイムゾーンを変更した後も、クライアントまたはセッションのタイムゾーンの設定に関係なく、SYSDATE
はサーバーのタイムスタンプを返します。
PostgreSQL での影響
PostgreSQL では、clock_timestamp()
とその他の時刻関数は、クライアントセッションのタイムゾーンの現在の日付と時刻を返します。次のコード例を参照してください。
これらのタイムスタンプが異なるタイムゾーンの異なるクライアントによって返され、TIMESTAMP WITHOUT TIME ZONE
列に格納されている場合、データは誤解を招く可能性があります。
DST に関する考慮事項
タイムゾーン名とオフセットの実装が、Oracle データベースと PostgreSQL データベースで一致していません。UTC から一致するタイムゾーン名またはタイムオフセットを取得できない場合があります。さらに、PostgreSQL のタイムゾーン設定の動作は、完全なタイムゾーン名を使用するか、タイムゾーン省略形を使用するかによって異なります。PostgreSQL のタイムゾーンの省略形は、UTC からの特定のオフセットを定義しますが、完全なタイムゾーン名は、一連の DST 日付規則を意味する場合があります。
pg_timezone_names
カタログテーブルには完全なタイムゾーン名の詳細が含まれており、pg_timezone_abbrevs
にはタイムゾーンの省略形があります。
例: Oracle データベースの移行
次の例では、Oracle データベース (DB タイムゾーン MET) を PostgreSQL データベースに移行する必要があります。その際、アプリケーションコードは、SYSDATE
を使用します。PostgreSQL にも同様の時刻関数が必要です。
Oracle では、MET タイムゾーンは DST 対応であり、UTC オフセットは +02:00:00 です。PostgreSQL にはタイムゾーン名 MET (UTS オフセット +02:00:00 および DST 対応) があり、タイムゾーン省略形 MET (UTC オフセット +01:00:00 および DST 非対応) もあります。
PostgreSQL データベースでは、セッションレベルまたは AT TIME ZONE
でタイムゾーンを設定するときに、一致するフルネームと省略形がある場合、その省略形を使用します。MET を timezone
DB パラメータとして設定した場合、オフセットは+01:00:00 で、DST は有効ではありません。
次のコード例には、さまざまなオフセットの一致するタイムゾーン名と省略形があります。
セッションのタイムゾーンを MET に設定すると、UTC と MET の時差は 1 時間になります。これは、タイムゾーンの省略形に関連するオフセットです。また、このタイムゾーンは DST に対応していません。次のコードを使用して手動で間隔を追加することで確認できます。
MET タイムゾーンの場合、正しいタイムゾーンはヨーロッパ/ベルリンであるはずです。次のコードを参照してください。
DST がタイムゾーンに影響するかどうかを検証するには、次の手順を実行します。
- 過去または今後の DST の変更を見つけます。詳細については、「今後の夏時間の時計の変更」を参照してください。
- Oracle で、DST 変更の直前と直後に UTC オフセットが変更されているかどうかを確認します。
- PostgreSQL で、DST の変更の直前と直後に UTC オフセットが変更されているかどうかを確認します。
例: DST の変更
次の例では、MET (中央ヨーロッパ時間) の DST の変更は、現地時間が 1 時間戻った時、2018 年 10 月 28 日 03:00:00 AM に発生します。元の DST UTC オフセットは 02:00:00 で、新しい DST UTC オフセットは 01:00:00 であるはずです。
次の Oracle サンプルコードでは、この時計は、この DST の変更時に 1 時間前に設定されています。
次のコードは、PostgreSQL で同じ効果があることを示しています。
お勧め
PostgreSQL の日付と時刻関数のさまざまな側面 (クライアント側のタイムゾーン設定や DST の変更による影響など) を検討した後、次の関数を使用して、PostgreSQL の Oracle SYSDATE
関数をシミュレートできます。これはステートメントレベルのタイムスタンプを提供し、クライアント側の設定に副作用はありません。
次のコード例では、クライアント側のタイムゾーン設定を変更すると、一貫した結果が返されます。
まとめ
この記事では、Amazon RDS と Aurora PostgreSQL で使用できる日付/時刻関連の関数について説明し (詳細については、「PostgreSQL の日付/時刻関数の概要」セクションを参照)、clock_timestamp()
関数が Oracle の SYSDATE
関数に最も近いと判断しました。「推奨事項」セクションで示したように、clock_timestamp
に基づいて、Oracle データベースサーバーのタイムゾーンを使用して関数を作成できます。
著者について
Baji Shaik は、インドの AWS ProServe チームのコンサルタントです。彼のバックグラウンドは、SQL/NoSQL データベーステクノロジーの幅広い専門知識と経験に及びます。彼は、困難なビジネス要件に対応する多くのデータベースソリューションのアーキテクチャと設計を成功させてきました。レポート、ビジネスインテリジェンス、データウェアハウジング、アプリケーション、および開発サポートに PostgreSQL を使用したソリューションを提供しています。また、クラウド環境での自動化、オーケストレーション、DevOps についても十分な知識を持っています。
Sudip Acharya は、インドの AWS ProServe チームのコンサルタントです。彼は社内顧客と社外の Amazon のお客様と連携して、データベースプロジェクトに関するガイダンスと技術支援を提供し、AWS を使用する際のソリューションの価値を向上させるお手伝いをしています。