Rails indexのおかげでdb:migrateができなかった話

# はじめに
テーブル作成の際に
~~~
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ってなんっちゃか？」というところからのスタートでした。
こちらの記事がわかりやすかったです。

https://qiita.com/seiya1121/items/fb074d727c6f40a55f22

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
~~~

##### マイグレーションファイルを記述します。
~~~ ruby
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名が長すぎるようです。
# 解決
https://kemarii.com/blog/rails/index-name-too-long/

この記事によるとindexは次の3つのときに追加されます。
- t.references で関連づけた場合
- index: true オプションで関連づけた場合
- add_index で関連づけた場合

つまり今回はt.referencesでindexが貼られていることになります。
ここのindex名が長すぎると怒られていたわけでした。
t.referencesの場合はindexオプションで名前を指定することができます。
~~~ ruby
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;
~~~
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1431393/4229ae94-308f-fd8a-7857-0982357c7adc.png)
ありました！
削除します！
~~~
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名を指定できます。
~~~ ruby
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
~~~

これで解決です！

# おわりに
エンジニアになって６か月。
テーブル設計を任せてもらうようになりましたが、本当に実力不足を痛感しています。
今回のindexもよくわかっていませんでした。
今でもわかっているのかは疑問ですが、日々積み重ねていきたいと思います。