はじめに
テーブル作成の際に
rails db:migrate
を実行したのにも関わらず、次のようなエラーでテーブルが作成されないという事象がありました。
ArgumentError: Index name 'index_long_long_long_long_long_long_long_tests_on_long_long_long_user_id' on table
'long_long_long_long_long_long_long_tests' is too long; the limit is 64 characters
index名が長すぎるというエラーでした。
しかし、異なる部分でも時間を溶かしてしまったので備忘録として残したいと思います。
いつものようにおかしな点や誤っている点はご指摘お待ちしています!
環境
ruby 2.7,1
rails 6.0.5
indexとは
今回のテーブル設計では、「そもそもindexってなんっちゃか?」というところからのスタートでした。
こちらの記事がわかりやすかったです。
indexとは
特定のカラムからデータを取得する際に、テーブルの中の特定のカラムのデータを複製し検索が行いやすいようにしたものです。
- メリット:データの読み込み・取得が早くなる。
- デメリット:書き込みの速度が倍かかる。
indexは
ある程度多くのデータを格納するテーブルの、格納される値がそれぞれ異なるようなカラムの中で、検索がよく行われるカラム
に使用するべきとのことです。なるほど!
前提
今回は長いテーブル名をつけたときにこのindexを貼ったことが原因でエラーになりました。
順を追っていきます。
long_long_long_long_long_long_long_testモデルを作ります。
rails g model long_long_long_long_long_long_long_test
次のように様々なファイルが作成されます。
Running via Spring preloader in process 7663
invoke active_record
create db/migrate/20221006024900_create_long_long_long_long_long_long_long_tests.rb
create app/models/long_long_long_long_long_long_long_test.rb
invoke rspec
create spec/models/long_long_long_long_long_long_long_test_spec.rb
マイグレーションファイルを記述します。
class CreateLongLongLongLongLongLongLongTests < ActiveRecord::Migration[5.1]
def change
create_table :long_long_long_long_long_long_long_tests do |t|
t.string :long_long_long_name, null: false
t.references :long_long_long_user, foreign_key: true
t.timestamps
end
add_index :long_long_long_long_long_long_long_tests, :long_long_long_name
end
end
add_indexでindexを貼り、rails db:migrateを実行しました。
課題
index名が長い!
マイグレーションを実行すると、ArgumentErrorが出ました。
ArgumentError: Index name 'index_long_long_long_long_long_long_long_tests_on_long_long_long_user_id' on table
'long_long_long_long_long_long_long_tests' is too long; the limit is 64 characters
どうやらindex名が長すぎるようです。
解決
この記事によるとindexは次の3つのときに追加されます。
- t.references で関連づけた場合
- index: true オプションで関連づけた場合
- add_index で関連づけた場合
つまり今回はt.referencesでindexが貼られていることになります。
ここのindex名が長すぎると怒られていたわけでした。
t.referencesの場合はindexオプションで名前を指定することができます。
class CreateLongLongLongLongLongLongLongTests < ActiveRecord::Migration[5.1]
def change
create_table :long_long_long_long_long_long_long_tests do |t|
t.string :long_long_long_name, null: false
t.references :long_long_long_user, foreign_key: true, index: { name: 'index_name' } # ここを追加
t.timestamps
end
add_index :long_long_long_long_long_long_long_tests, :long_long_long_name
end
end
これで一件落着!rails db:migrateを実行します。
そのテーブルはすでにあるよ!
ArgumentError: Index name 'index_long_long_long_long_long_long_long_tests_on_user_id' on table
'long_long_long_long_long_long_long_tests' already exists
なんだと・・・すでにテーブルがあるだと・・・
確認してみます。
次のコマンドを実行します。
rails db
パスワードを求められますので入力します。
MySQL [ai_brid_development]>
mysqlにログインできました。テーブルを見てみます。
MySQL [ai_brid_development]> SHOW TABLES;
MySQL [ai_brid_development]> DROP TABLE long_long_long_long_long_long_long_tests
これで今度こそできるはずです!
SQLからexitで抜け、rails db:migrateを実行します。
index名が長い再び!
ArgumentError: Index name 'index_long_long_long_long_long_long_long_tests_on_long_long_long_name' on table
'long_long_long_long_long_long_long_tests' is too long; the limit is 64 characters
おやまたエラーになりました。
今度はlong_long_long_nameと出ています!
違う箇所でエラー発生??
・・・そうか!
add_indexについても当然indexが貼られるわけなので、名前が長すぎると言われるのは当然ですね!
index名が長すぎるのであれば、add_indexについてはname属性でindex名を指定できます。
class CreateLongLongLongLongLongLongLongTests < ActiveRecord::Migration[5.1]
def change
create_table :long_long_long_long_long_long_long_tests do |t|
t.string :long_long_long_name, null: false
t.references :long_long_long_user, foreign_key: true, index: { name: 'index_name' }
t.timestamps
end
add_index :long_long_long_long_long_long_long_tests, :long_long_long_name, name: 'index_name2' # ここを追加
end
end
これで解決です!
おわりに
エンジニアになって6か月。
テーブル設計を任せてもらうようになりましたが、本当に実力不足を痛感しています。
今回のindexもよくわかっていませんでした。
今でもわかっているのかは疑問ですが、日々積み重ねていきたいと思います。