Re: MySQL の NOW() と SYSDATE()

自分は全然気にしたことなかったんだけど、MySQL の NOW()SYSDATE() は異なるらしい。

sakaik.hateblo.jp

MySQL 8.0 のマニュアル (https://dev.mysql.com/doc/refman/8.0/en/date-and-time-functions.html#function_sysdate) にも確かにちゃんと書かれてる。

SYSDATE() returns the time at which it executes. This differs from the behavior for NOW(), which returns a constant time that indicates the time at which the statement began to execute. (Within a stored function or trigger, NOW() returns the time at which the function or triggering statement began to execute.)

Google翻訳:

SYSDATE()は、それが実行された時刻を返します。 これは、ステートメントの実行が開始された時間を示す一定の時間を返すNOW()の動作とは異なります。 (ストアドファンクションまたはトリガー内で、NOW()は、ファンクションまたはトリガーステートメントが実行を開始した時刻を返します。)

複数行を返すクエリで試してみると、行ごとに値が異なる。

mysql> select now(), sysdate(), sleep(1) from (values row(1), row(2), row(3), row(4)) t;
+---------------------+---------------------+----------+
| now()               | sysdate()           | sleep(1) |
+---------------------+---------------------+----------+
| 2020-07-12 19:14:05 | 2020-07-12 19:14:05 |        0 |
| 2020-07-12 19:14:05 | 2020-07-12 19:14:06 |        0 |
| 2020-07-12 19:14:05 | 2020-07-12 19:14:07 |        0 |
| 2020-07-12 19:14:05 | 2020-07-12 19:14:08 |        0 |
+---------------------+---------------------+----------+
4 rows in set (4.00 sec)

へー、面白い。複数行を処理するようなクエリでは SYSDATE() は使わないほうが良さそうだな。

しかし、この挙動はいつからなんだろう。昔は NOW()SYSDATE() は同じだったはず。

4.1 の日本語マニュアル (https://downloads.mysql.com/docs/refman-4.1-ja.a4.pdf)には

SYSDATE() は NOW() のシノニム

と書かれている。ソースコード上も lex.h に1行だけ。

  { "SYSDATE",        SYM(NOW_SYM)},

5.0 のマニュアル (https://downloads.mysql.com/docs/refman-5.0-en.a4.pdf) に次のようにあった。

As of MySQL 5.0.12, SYSDATE() returns the time at which it executes. This differs from the behaviorfor NOW(), which returns a constant time that indicates the time at which the statement beganto execute. (Within a stored function or trigger, NOW() returns the time at which the function ortriggering statement began to execute.)

As of MySQL 5.0.13, the SYSDATE() function is no longer equivalent to NOW(). Implications are that SYSDATE() is not replication-safe because it is not affected by SET TIMESTAMP statements in thebinary log and is nondeterministic. To avoid this, you can start the server with the --sysdate-is-now option to cause SYSDATE() to be an alias for NOW().

へー、5.0 からなのか。最近だった。自分は知らないわけだ。

どうやら 5.0.12 か 5.0.13 のどちらかかららしい。どっちやねん。

「5.0 の途中から!?」と一瞬思ったけど、5.0 の GA は 5.0.15 なので、パッチレベルで振る舞いを変えるようなことはさすがに無かったようだ(昔はよかった)。

そして非互換なので、--sysdate-is-now オプションで振る舞いを変えることができるようだ(昔はよかった)。なお、このオプションは 8.0 でも存在する。

MySQL 8.0.20 を --sysdate-is-now つきで起動してさっきのクエリを試すと

mysql> select now(), sysdate(), sleep(1) from (values row(1), row(2), row(3), row(4)) t;
+---------------------+---------------------+----------+
| now()               | sysdate()           | sleep(1) |
+---------------------+---------------------+----------+
| 2020-07-12 20:11:15 | 2020-07-12 20:11:15 |        0 |
| 2020-07-12 20:11:15 | 2020-07-12 20:11:15 |        0 |
| 2020-07-12 20:11:15 | 2020-07-12 20:11:15 |        0 |
| 2020-07-12 20:11:15 | 2020-07-12 20:11:15 |        0 |
+---------------------+---------------------+----------+
4 rows in set (4.00 sec)

NOW() と同じになった。

これは mysqld 起動時のオプションだけど、システム変数ではないので SHOW VARIABLES では出てこない。たまにこういうのがある。 システム変数ではないので SET PERSIST でも設定できないけど、my.cnf に書くとちゃんと有効になる。