はじめに
Rails 6 に追加されそうな新機能を試す第65段。 今回は、 timestamps precision
編です。
Rails 6 では、 timestamps
にデフォルトで、 precision が設定されるようになりました。
Ruby 2.6.3, Rails 6.0.0.rc1, Rails 5.2.3, MySQL 8.0.16 で確認しました。Rails 6.0.0.rc1 は gem install rails --prerelease
でインストールできます。
$ rails --version
Rails 6.0.0.rc1
今回は、 created_at
を検索条件にして検索してみます。
プロジェクトを作る
rails new rails6_0_0rc1
cd rails6_0_0rc1
model を作る
User モデルを作ります。
bin/rails g model User name
seed データを作る
1件だけですが、 seed データを作成します。
User.create(name: 'Taro')
User モデルを修正する
User モデルに scope を1つ作ります。
- 1件目のデータの created_at カラムを取得します。
- created_at カラムから 秒まで指定した Time オブジェクトを作る。
- Timeオブジェクトを条件にして、 created_at で検索する。
class User < ApplicationRecord
scope :find_first, -> {
created_at = first.created_at
t = Time.new(created_at.year, created_at.month, created_at.day, created_at.hour, created_at.min, created_at.sec)
where(created_at: t)
}
end
seed データを登録します。
$ bin/rails db:create db:migrate db:seed
rails console で確認する
rails console
で確認してみます。
irb(main):001:0> User.find_first
(0.4ms) SET NAMES utf8mb4, @@SESSION.sql_mode = CONCAT(CONCAT(@@sql_mode, ',STRICT_ALL_TABLES'), ',NO_AUTO_VALUE_ON_ZERO'), @@SESSION.sql_auto_is_null = 0, @@SESSION.wait_timeout = 2147483
User Load (0.4ms) SELECT `users`.* FROM `users` ORDER BY `users`.`id` ASC LIMIT 1
User Load (0.3ms) SELECT `users`.* FROM `users` WHERE `users`.`created_at` = '2019-07-26 06:52:38' LIMIT 11
=> #<ActiveRecord::Relation []>
データは見つかりませんでした。
mysql で検索してみる
mysql で検索してみます。
mysql> select * from users;
+----+------+----------------------------+----------------------------+
| id | name | created_at | updated_at |
+----+------+----------------------------+----------------------------+
| 1 | Taro | 2019-07-26 06:52:38.606642 | 2019-07-26 06:52:38.606642 |
+----+------+----------------------------+----------------------------+
1 row in set (0.00 sec)
created_at
と updated_at
の秒に6桁の小数部分が存在していることが確認できます。
schema.rb を確認する
schema.rb
を確認すると precision: 6
が指定されていることがわかります。
...
create_table "users", options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci", force: :cascade do |t|
t.string "name"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
end
...
Rails 5 では
Rails 5.2.3 では、データが検索できます。
irb(main):001:0> User.find_first
(0.7ms) SET NAMES utf8mb4, @@SESSION.sql_mode = CONCAT(CONCAT(@@sql_mode, ',STRICT_ALL_TABLES'), ',NO_AUTO_VALUE_ON_ZERO'), @@SESSION.sql_auto_is_null = 0, @@SESSION.wait_timeout = 2147483
User Load (0.3ms) SELECT `users`.* FROM `users` ORDER BY `users`.`id` ASC LIMIT 1
User Load (0.4ms) SELECT `users`.* FROM `users` WHERE `users`.`created_at` = '2019-07-26 06:42:34' LIMIT 11
=> #<ActiveRecord::Relation [#<User id: 1, name: "Taro", created_at: "2019-07-26 06:42:34", updated_at: "2019-07-26 06:42:34">]>
MySQL で確認すると、秒に小数部分がありません。
mysql> select * from users;
+----+------+---------------------+---------------------+
| id | name | created_at | updated_at |
+----+------+---------------------+---------------------+
| 1 | Taro | 2019-07-26 06:42:34 | 2019-07-26 06:42:34 |
+----+------+---------------------+---------------------+
1 row in set (0.00 sec)
schema.rb
にも precision: 6
がありません。
...
create_table "users", options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci", force: :cascade do |t|
t.string "name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
...
Rails 6 でデータを見つけるには
十分な検証はしてませんが、 created_at.subsec
を考慮してやれば良さそうです。
class User < ApplicationRecord
scope :find_first, -> {
created_at = first.created_at
# t = Time.new(created_at.year, created_at.month, created_at.day, created_at.hour, created_at.min, created_at.sec)
t = Time.new(created_at.year, created_at.month, created_at.day, created_at.hour, created_at.min, created_at.sec) + created_at.subsec
where(created_at: t)
}
end
試したソース
試したソースは以下にあります。
https://github.com/suketa/rails6_0_0rc1/tree/try065_mysql_precision