Railsで検索条件を指定するときはプレースホルダーではなくHashにしたほうがいい
Ruby2.6 + Rails5.2の組み合わせでMySQLのdate型のデータに↓で実行したときにクエリが異なるのはなんでだろ?
— Eiji Hachiya (@hachi_eiji) February 12, 2019
d = DateTime.parse('2019-02-13')
<中略>.where("some_date=?",d).where(some_date: d)→<中略> WHERE (some_date = '2019-02-13 09:00:00') AND `some_dates`.`some_date` = '2019-02-13'
文字列の場合
下記の場所で タイムゾーン考慮処理と、 DateTime#to_s
を呼び出しているので時間まで残る
def quoted_date(value) if value.acts_like?(:time) zone_conversion_method = ActiveRecord::Base.default_timezone == :utc ? :getutc : :getlocal if value.respond_to?(zone_conversion_method) value = value.send(zone_conversion_method) end end result = value.to_s(:db) if value.respond_to?(:usec) && value.usec > 0 "#{result}.#{sprintf("%06d", value.usec)}" else result end end
rails/quoting.rb at e53430fa9af239e21e11548499d814f540d421e5 · rails/rails · GitHub で呼ばれているメソッドより抜粋
Hashの場合
こちらは DateTime#to_date
を呼び出しているので、時間は切り落とされて日付だけが返却される
def cast_value(value) if value.is_a?(::String) return if value.empty? fast_string_to_date(value) || fallback_string_to_date(value) elsif value.respond_to?(:to_date) value.to_date else value end end
rails/date.rb at e53430fa9af239e21e11548499d814f540d421e5 · rails/rails · GitHubより抜粋
いつから入ったのか?
どうもこれっぽい。
本来はどうあるべきなのだろうか
MySQL5.7で検証しているが 2019-02-13
を 2019-02-13 00:00:00
にしても検索結果はヒットする。しかし、しかし、MySQL5.6時点の
MySQL :: MySQL 5.6 リファレンスマニュアル :: 9.1.3 日付リテラルと時間リテラル を見ると 2019-02-13
== 2019-02-13 00:00:00
とはどこにも書いてない。Date型は日付だけを持っており時間を持っていないので当たり前だが。
DBという固く設計・実装すべき方に合わせるほうが良いので、プレースホルダーの方を日付型にすれば良いと思ったが、下記で話をされている通り、そちらはカラムの型を持っていないのでDatetimeなのかDateなのかわからないため変換ができない。
アプリケーションエンジニアができることはなんだろうか?
テストかけ