画期的な方法を発見した。MySQL 5.5 の UUID()
関数は、100ナノ秒精度の現在時刻を含んだ UUID v1 を返す(12.15. Miscellaneous Functions)。これをパースすればマイクロ秒精度の現在時刻を取得することができる。
詳しい実装についてはmysqldのソースに書いてあるので読めばいい。mysql-5.5.27/sql/item_strfunc.cc
に実装してある。141427日間のオフセットがかかっていたりするので気をつける。
DELIMITER //
CREATE FUNCTION TIMESTAMP_USEC()
RETURNS BIGINT NOT DETERMINISTIC
BEGIN
set @uuid = UUID();
set @low = CONV(SUBSTRING(@uuid, 1, 8), 16, 10);
set @mid = CONV(SUBSTRING(@uuid, 10, 4), 16, 10);
set @hi = CONV(SUBSTRING(@uuid, 15, 4), 16, 10) - 0x1000;
set @hnsec = ((@hi << 48) | (@mid << 32) | @low) - (141427*24*60*60 *1000*1000*10);
RETURN CAST(@hnsec / 10 AS UNSIGNED INTEGER);
END
//
DELIMITER ;
実行結果:
mysql> select timestamp_usec();
+------------------+
| timestamp_usec() |
+------------------+
| 1367612044966701 |
+------------------+
1 row in set (0.00 sec)
mysql> select from_unixtime(timestamp_usec() / 1000/1000);
+---------------------------------------------+
| FROM_UNIXTIME(timestamp_usec() / 1000/1000) |
+---------------------------------------------+
| 2013-05-03 13:26:23 |
+---------------------------------------------+
1 row in set (0.00 sec)
MySQL 5.6 からは NOW(6)
でマイクロ秒精度の現在時刻を取得できるが、この手法を使えばMySQL 5.5でも取得できる。
また、UUIDに含まれるタイムスタンプは、必ず単調に増加するように保証されている。
ただし NOW()
とは異なり、1つのSQL文の中で複数回呼ぶと異なる値を返す点には注意が必要。
mysql> select timestamp_usec(), timestamp_usec();
+------------------+------------------+
| timestamp_usec() | timestamp_usec() |
+------------------+------------------+
| 1367612440310252 | 1367612440310292 |
+------------------+------------------+
1 row in set (0.00 sec)