🐘
PostgreSQLの現在日時を返す関数の違い
PostgreSQLには、現在日時を返す関数が複数あります。
それぞれがどういった値を返すのか、パターン毎に見ていきます。
パターン
大きく分けて2つのパターンになります。
- トランザクション開始時の日時を返すもの
now()CURRENT_TIMESTAMPtransaction_timestamp()LOCALTIMESTAMP
 - 現在の文の実行開始日時を返すもの
statement_timestamp()
 - 現在日時を返すもの
clock_timestamp()
 
それぞれの実行結果を確認してみる
それぞれを実行してみて確認します。確認に利用したPostgreSQLのバージョンは 16.3 です。
トランザクションを開始していない場合
SELECT
  now(),
  statement_timestamp(),
  clock_timestamp()
;
sample=# SELECT
  now(),
  statement_timestamp(),
  clock_timestamp()
;
              now              |      statement_timestamp      |        clock_timestamp        
-------------------------------+-------------------------------+-------------------------------
 2025-01-08 15:15:06.916807+00 | 2025-01-08 15:15:06.916807+00 | 2025-01-08 15:15:06.917161+00
(1 row)
トランザクションを開始していない状態では、
- 
now()とstatement_timestamp()は同じ時間- トランザクションを開始していない場合には、
now()も文の実行開始日時となる 
 - トランザクションを開始していない場合には、
 - 
clock_timestamp()は関数が評価された現在日時なので、文の実行開始日時となるnow()とstatement_timestamp()より少し遅れた時間 
となります。
トランザクションを開始した場合
start transaction;
SELECT
  now(),
  statement_timestamp(),
  clock_timestamp()
;
SELECT
  now(),
  statement_timestamp(),
  clock_timestamp()
;
sample=# start transaction;
SELECT
  now(),
  statement_timestamp(),
  clock_timestamp()
;
SELECT
  now(),
  statement_timestamp(),
  clock_timestamp()
;
START TRANSACTION
              now              |      statement_timestamp      |        clock_timestamp        
-------------------------------+-------------------------------+-------------------------------
 2025-01-08 15:18:55.099745+00 | 2025-01-08 15:18:55.099985+00 | 2025-01-08 15:18:55.100048+00
(1 row)
              now              |      statement_timestamp      |        clock_timestamp        
-------------------------------+-------------------------------+-------------------------------
 2025-01-08 15:18:55.099745+00 | 2025-01-08 15:18:55.100278+00 | 2025-01-08 15:18:55.100306+00
(1 row)
トランザクションを開始した状態では、
- 
now()はトランザクション開始時間となり、トランザクション内では毎回同じ時間を返す - 
statement_timestamp()は文の実行開始日時なので、トランザクション内でもそれぞれ別々の時間を返す - 
clock_timestamp()は関数が評価された現在日時なので、文の実行開始日時となるstatement_timestamp()より少し遅れた時間 
となり、now()が返すものがトランザクション有無で変わることがわかります。
他のRDBMSは?
ここがちょっとやっかいなところで、PostgreSQL以外の主要なRDBMS(MySQL、MariaDB、MS SQL Server、SQLite、Oracleなど)では、トランザクションの開始時間を返すものはありません。
他のRDBMSと同じ感覚でCURRENT_TIMESTAMPやnow()を使ってしまうと、1トランザクションが長いような処理で全てトランザクション開始時間になってしまって問題になる、、、といったことになりかねないので注意です。
個人的にはトランザクション開始時刻で揃えたいみたいなことは今までなかったので、たいていstatement_timestamp()を注意して使っています。
ただ、ORMとかの機能で現在日時を入れるような場合にデフォルトCURRENT_TIMESTAMPやnow()が使われるようなことがあり手間になったことがあるので、PostgreSQL側の設定としてCURRENT_TIMESTAMPやnow()をトランザクション開始日時ではなくstatement_timestamp()と同等に切り替えられる機能があったらいいのにな、、と思うことがありました。
Discussion