Help us understand the problem. What is going on with this article?

MySQL 5.6からDATETIMEは小数点以下が四捨五入(round)されるので気を付けよう

More than 5 years have passed since last update.

MySQL 5.6からTIME, DATETIME, TIMESTAMPがマイクロ秒精度をサポートしました。

MySQL 5.6 Reference Manual :: 11.3.6 Fractional Seconds in Time Values

で、ドキュメントにはちゃんとroundされるけどこれはSQL標準に従う挙動だからエラーとかワーニングは出ないよってさらっと書いてあるんですが、5.5では小数点以下は切り捨てだったんでマイクロ秒を付けてクエリ投げてた場合ハマりポイントになってます。

MySQL Bugs: #68760: Datetime rounding problem

ActiveRecordにおけるDATETIMEのマイクロ秒サポート

ここからはジョーカーさんのRails-4.2+MySQL-5.6での時刻オブジェクトのミリ秒の扱いについてへのアンサーエントリになるんですが、ActiveRecord 4.2では以下のPRがマージされたことでMySQLでもマイクロ秒を付けてクエリを送るようになりました(Rebuild: 56でmiyagawaさんがマージされるのに1年半ぐらいかかったって言ってたやつです)。

MySQL 5.6 Fractional Seconds by arthurnn · Pull Request #14359 · rails/rails

これは5.6のためにマイクロ秒付けるけど5.5には影響ないだろう(travis-ciはMySQL 5.5だからマイクロ秒のテストをCIで回せないというのがもともと1年半ぐらい放置されていたときの言い分だった)ということだったんだけど、roundされて切り上がると思った通りに動かないということをみんな甘くみていて、これ関係のissueがいくつか上がってきています。

queries with ActiveSupport::TimeZone have extra fractional time · Issue #17239 · rails/rails

小数点以下をそのカラムが持つマイクロ秒精度以上送ってroundされて切り上がるのは望ましくない挙動であることはコアチームの人も認識はしていて、精度に応じたフォーマットをするべきだよねとは言っています。

で、MySQLでは問題になってるけどPostgreSQLのほうはどうなってるのか、ちゃんと精度に応じたフォーマットがされているのかというと、PostgreSQLでも最大精度でマイクロ秒を送るので当然round問題は存在します。以下のテストはMySQLもPostgreSQLもdateがroundされた結果2014-08-17 12:30:01として保存されるのでFoo.find_by(created_at: date)でレコードがヒットしません。

    def test_formatting_datetime_according_to_precision
      ActiveRecord::Base.connection.create_table(:foos, force: true) do |t|
        t.datetime :created_at, precision: 0
        t.datetime :updated_at, precision: 4
      end
      date = ::Time.utc(2014, 8, 17, 12, 30, 0, 999999)
      Foo.create!(created_at: date, updated_at: date)
      assert foo = Foo.find_by(created_at: date)
      assert_equal date.to_s, foo.created_at.to_s
      assert_equal date.to_s, foo.updated_at.to_s
      assert_equal 000000, foo.created_at.usec
      assert_equal 999900, foo.updated_at.usec
    end

activerecord-mysql-awesome/test/cases/datetime_test.rb#L57-L69

PostgreSQLでこの挙動がこれまで問題とされてこなかったのは、精度を指定しなかった場合の挙動がMySQLとPostgreSQLでは異なるからです。MySQLでは精度を指定しない場合マイクロ秒を持たないとなりますが、PostgreSQLでは精度を指定しない場合マイクロ秒精度に制限を持たない(実質最大の精度を持つ)となるため、ふつうに使ってるひとがハマる機会があまりないということになります。

この問題は以前から認識していたので直す前段階として#17673をPRしてたけど空気的に4.2.0には入れてくれなさそうなんで4.2.0出たら直そうと思ってたんですが、ジョーカーさんがこの問題踏んだのでとりいそぎ修正PRを出しておきました。

Format the datetime string according to the precision of the datetime field. by kamipo · Pull Request #18067 · rails/rails

このまま4.2.0が出るとMySQL使ってるひと阿鼻叫喚だと思うんで上記PRをバックポートしたactiverecord-mysql-awesome 0.0.2をリリースしておいたのでどうぞお納めください。これを使うと4.0, 4.1, まだ出てないけど4.2でマイクロ秒サポートの恩恵を受けることが出来ます。

activerecord-mysql-awesome 0.0.2

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした