Ruby on Rails ActiveRecordの移行で長すぎるインデックス名を処理するにはどうすればよいですか?


391

4つの関連付けられたテーブルの外部キーから作成される一意のインデックスを追加しようとしています。

add_index :studies,
  ["user_id", "university_id", "subject_name_id", "subject_type_id"],
  :unique => true

データベースのインデックス名の制限により、移行が失敗します。エラーメッセージは次のとおりです。

テーブル 'studies'のインデックス名 'index_studies_on_user_id_and_university_id_and_subject_name_id_and_subject_type_id'が長すぎます。制限は64文字です

どうすればこれを処理できますか?別のインデックス名を指定できますか?

回答:


589

:nameオプションを提供しますadd_index。例:

add_index :studies,
  ["user_id", "university_id", "subject_name_id", "subject_type_id"], 
  :unique => true,
  :name => 'my_index'

使用している場合:indexにオプションreferencescreate_tableブロックを、それが同じオプションのハッシュをとりadd_index、その値として:

t.references :long_name, index: { name: :my_index }

2
APIdockによると、名前は記号ではなく文字列でなければなりません
Jaco Pretorius

7
これのダウン構文はremove_index :studies, :name => 'my_index'、それを必要とするすべての人のためのものです
Kirk

On Railsはで4.2.6動作しindex: { name: :my_index }ます。のみでname動作しませんでした。
モンテイロブレナ

174

create_tableブロック内の列定義のインデックス名を変更することもできます(移行ジェネレーターから取得する場合など)。

create_table :studies do |t|
  t.references :user, index: {:name => "index_my_shorter_name"}
end

5
これは元の質問から複数列のインデックスを作成しないことに注意してください。これは、長いインデックス名をaから短縮する方法を示すためのものですcreate_table
Craig Walker

6
これはt.references :searchable, polymorphic:true, index: {:name => "index_searches_on_searchable"}、インデックスを使用して名前空間テーブルにポリモーフィック参照を作成しようとしたときに役立ちました。この場合、インデックスは実際には複数列(searchable_idおよびsearchable_type)であり、生成された名前に名前空間を追加するのが非常に長くなりました。
mkrinblk 2017年

私にとっては素晴らしいコンパクトなソリューションです。ありがとうございました!
Sergio Belevskij 2017年

3
またforeign_key: true、レールジェネレーターmodel:references形式で作成された移行ファイルがある場合に最も簡単に使用できるため、これは優れたソリューションです
Abram

39

PostgreSQLではデフォルトの制限は63文字です。インデックス名は一意である必要があるため、少し規則があると便利です。私は使用します(より複雑な構造を説明するために例を微調整しました):

def change
  add_index :studies, [:professor_id, :user_id], name: :idx_study_professor_user
end

通常のインデックスは次のようになります。

:index_studies_on_professor_id_and_user_id

ロジックは次のようになります。

  • index なる idx
  • 特異なテーブル名
  • 参加する言葉はありません
  • 番号 _id
  • アルファベット順

これは通常、仕事をします。


1
共有いただきありがとうございます。制限の事実についてPostgresのドキュメントをリンクできればすばらしいと思います。
JJD 2015年

インデックスはとにかくそのテーブルに属しているので、インデックス名にテーブル名が必要ですか?それが私が見たことのないどこかでそれが有益であるかどうかだけ興味があります。
ジョシュアピンター

インデックスには任意の名前を付けることができますが、インデックス名のテーブル名はインデックス名を一意(必須)に保つのに役立ち、スコープが広く、エラーメッセージの読みやすさが向上すると思います。
ecoologic


6

前の回答と同様:通常のadd_index行で 'name'キーを使用するだけです:

def change
  add_index :studies, :user_id, name: 'my_index'
end

2

これらの解決策はどれもうまくいきませんでした。多分私はbelongs_to多態的な関連付けのために私のcreate_table移行で使用していたためです。

以下に私のコードと、ポリモーフィックな関連付けに関連して「インデックス名が長すぎる」を検索するときに他の誰かが偶然見つけた場合に役立つソリューションへのリンクを追加します。

次のコードは私にとってはうまくいきませんでした:

def change
  create_table :item_references do |t|
    t.text :item_unique_id
    t.belongs_to :referenceable, polymorphic: true
    t.timestamps
  end
  add_index :item_references, [:referenceable_id, :referenceable_type], name: 'idx_item_refs'
end

このコードは私のために働きました:

def change
  create_table :item_references do |t|
    t.text :item_unique_id
    t.belongs_to :referenceable, polymorphic: true, index: { name: 'idx_item_refs' }

    t.timestamps
  end
end

これは私を助けたSO Q&Aです:https : //stackoverflow.com/a/30366460/3258059


1

私はこの問題を抱えていましたが、timestamps機能はありました。63文字の制限を超えるupdated_atでインデックスを自動生成していました。

def change
  create_table :toooooooooo_loooooooooooooooooooooooooooooong do |t|
    t.timestamps
  end
end

テーブル「toooooooooo_loooooooooooooooooooooooooooooooong」のインデックス名「index_toooooooooooo_loooooooooooooooooooooooooooooong_on_updated_at」が長すぎます。制限は63文字です

を使用timestampsしてインデックス名を指定しようとしました:

def change
  create_table :toooooooooo_loooooooooooooooooooooooooooooong do |t|
    t.timestamps index: { name: 'too_loooooooooooooooooooooooooooooong_updated_at' }
  end
end

ただし、これはupdated_atcreated_atフィールドの両方にインデックス名を適用しようとします。

テーブル 'toooooooooo_loooooooooooooooooooooooooooooong'のインデックス名 'too_long_updated_at'は既に存在します

最後に私はあきらめてtimestamps、長い間タイムスタンプを作成しました:

def change
  create_table :toooooooooo_loooooooooooooooooooooooooooooong do |t|
    t.datetime :updated_at, index: { name: 'too_long_on_updated_at' }
    t.datetime :created_at, index: { name: 'too_long_on_created_at' }
  end
end

これは機能しますが、このtimestamps方法で可能かどうか聞いてみたいです!


0

create_table:you_table_name do | t | t.references:studant、index:{name: 'name_for_studant_index'} t.references:teacher、index:{name: 'name_for_teacher_index'} end


0

ジェネレーターを多用するプロジェクトがあり、これを自動化する必要があるため、index_name関数をrailsソースからコピーしてオーバーライドしました。私はこれを追加しましたconfig/initializers/generated_index_name.rb

# make indexes shorter for postgres
require "active_record/connection_adapters/abstract/schema_statements"
module ActiveRecord
  module ConnectionAdapters # :nodoc:
    module SchemaStatements
      def index_name(table_name, options) #:nodoc:
        if Hash === options
          if options[:column]
            "ix_#{table_name}_on_#{Array(options[:column]) * '__'}".slice(0,63)
          elsif options[:name]
            options[:name]
          else
            raise ArgumentError, "You must specify the index name"
          end
        else
          index_name(table_name, index_name_options(options))
        end
      end
    end
  end
end

それはのようなインデックスを作成します ix_assignments_on_case_id__project_id、それがまだ長すぎる場合は63文字に切り捨て。テーブル名が非常に長い場合でも、これは一意ではありませんが、テーブル名を列名とは別に短くしたり、実際に一意性をチェックしたりするなどの複雑さを追加できます。

これはRails 5.2プロジェクトからのものであることに注意してください。これを行う場合は、バージョンからソースをコピーしてください。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.