Postgresを使用したバッチにて、処理終了時に終了日時を挿入するバッチを作成したのですが、処理時間が長いバッチでも終了日時が正しく入力されていなかったので調べてみました。
now()は現在時刻を取得するわけではない
Postgresのドキュメントに記載されていました。
PostgreSQLは、現在の日付時刻に関した値を返す多くの関数を提供します。 これらの標準SQL関数はすべて、現在のトランザクションの開始時刻に基づいた値を返します。
- CURRENT_DATE
- CURRENT_TIME
- CURRENT_TIMESTAMP
- CURRENT_TIME(precision)
- CURRENT_TIMESTAMP(precision)
- LOCALTIME
- LOCALTIMESTAMP
- LOCALTIME(precision)
- LOCALTIMESTAMP(precision)
この中でnow()はtransaction_timestamp()と同じのようなので、トランザクション内で何度now()を実行してもトランザクション開始日時を取得してしまうようです。
例)左がnow()の結果 右が現在時刻を取得するclock_timestamp()の結果
postgres=# select now(),clock_timestamp();
now | clock_timestamp
-------------------------------+-------------------------------
2015-09-25 00:26:05.476531+09 | 2015-09-25 00:26:05.476616+09
(1 row)
postgres=# begin;
BEGIN
postgres=# select now(),clock_timestamp();
now | clock_timestamp
-------------------------------+-------------------------------
2015-09-25 00:26:09.436512+09 | 2015-09-25 00:26:10.605541+09
(1 row)
postgres=# select now(),clock_timestamp();
now | clock_timestamp
-------------------------------+-------------------------------
2015-09-25 00:26:09.436512+09 | 2015-09-25 00:26:19.564585+09
(1 row)
postgres=# commit;
COMMIT
postgres=# begin;
BEGIN
postgres=# select now(),clock_timestamp();
now | clock_timestamp
-------------------------------+-------------------------------
2015-09-25 00:26:24.900512+09 | 2015-09-25 00:26:27.229012+09
(1 row)
postgres=#
トランザクションを開始していない場合はnow()とclock_timestamp()両方ともほぼ同じ値を表示しますが、トランザクション開始することによってnow()はトランザクション開始時間で固定されてしまいます。
対処方法
伝統的なPostgreSQL関数と言われているnow()ですが、トランザクション内で使用しかつ現在時刻を取得したい場合はclock_timestamp()を使用するのがいいようです。
OracleのSYSDATE、SYSTIMESTAMPはトランザクションに関わらず現在時刻を取得するので、Oracle⇔Postgresの移行をする際は注意が必要ですね。