Ruby on Rails移行で列を一意にしてインデックスを付けるにはどうすればよいですか?


416

uniqueRuby on Rails移行スクリプトで列を作成したいと思います。それを行うための最良の方法は何ですか?また、テーブルの列にインデックスを付ける方法はありますか?

uniqueだけを使用するのではなく、データベースに列を適用したいと思います:validate_uniqueness_of

回答:


686

古いバージョンのRailsに対する短い回答(Rails 4+に関する他の回答を参照):

add_index :table_name, :column_name, unique: true

複数の列にインデックスを付けるには、単一の列名ではなく、列名の配列を渡します。

add_index :table_name, [:column_name_a, :column_name_b], unique: true

「インデックス名...が長すぎる」と表示された場合はname: "whatever"、add_indexメソッドに追加して名前を短くできます。

きめ細かな制御のために、execute直接SQLを実行する" "メソッドがあります。

それでおしまい!

通常の古いモデル検証の代わりとしてこれを行う場合は、それがどのように機能するかを確認してください。ユーザーへのエラー報告は、モデルレベルの検証がなければ、それほどうまくいきません。いつでも両方を行うことができます。


36
+1は、validates_uniqueness_ofの使用を継続することを提案します。エラー処理は、単一のインデックス付きクエリのコストのためにこの方法を使用するとはるかにクリーンになります。私は彼が両方を実行することをお勧めします
Steve Weet

1
動かないようにしてみました!一意として定義したcolumn_nameを持つ2つのレコードを挿入できます。Rails 2.3.4とMySqlを使用していますか?
タム

私は、executeを使用して2番目の提案を使用しました。execute "ALTER TABLE users ADD UNIQUE(email)"を実行すると、機能します!最初の人がなぜ知り
Tam

1
あなたが得る場合はindexed columns are not unique一意のインデックスを作成しようとすると、エラーをテーブル内のデータがすでに重複が含まれているので、それはあるかもしれません。重複するデータを削除して、もう一度移行を実行してください。
Hartley Brody

5
「インデックス名...が長すぎる」と表示された場合は, :name => "whatever"add_indexメソッドに追加して名前を短くすることができます。
リック・スミス、

129

レールは移行add_index_to_table_name column_name:uniqを生成します

または

railsはマイグレーションadd_column_name_to_table_name column_name:string:uniq:indexを生成します

生成する

class AddIndexToModerators < ActiveRecord::Migration
  def change
    add_column :moderators, :username, :string
    add_index :moderators, :username, unique: true
  end
end

既存の列にインデックスを追加する場合は、add_column行を削除またはコメント化するか、チェックを入れます

add_column :moderators, :username, :string unless column_exists? :moderators, :username

5
コマンドラインフォームが必要だったので、これに賛成しました。しかし、私が指定add_index...しない場合でも列を追加するのはばかげていますadd_column...
タイラーコリアー

1
うん、多分次のバージョンで。
d.danailov 2014年

49

これはまだ言及されていませんが、このページを見つけたときに私が持っていた質問に答えるので、t.referencesまたはを介して追加するときにインデックスが一意であることを指定することもできますt.belongs_to

create_table :accounts do |t|
  t.references :user, index: { unique: true } # or t.belongs_to

  # other columns...
end

(少なくともRails以降4.2.7


42

新しいテーブルを作成する場合は、インラインショートカットを使用できます。

  def change
    create_table :posts do |t|
      t.string :title, null: false, index: { unique: true }
      t.timestamps
    end
  end

14

私はRails 5を使用していますが、上記の答えはうまくいきます。これも私のために働いた別の方法です(テーブル名は:peopleで、列名はです:email_address

class AddIndexToEmailAddress < ActiveRecord::Migration[5.0]
  def change
    change_table :people do |t|
      t.index :email_address, unique: true
    end
  end
end

1

レールによるデフォルトのunique_key名が長すぎてDBがエラーをスローする可能性があるため、一意のキーの名前を追加する必要がある場合があります。

インデックスの名前を追加するには、name:オプションを使用します。移行クエリは次のようになります-

add_index :table_name, [:column_name_a, :column_name_b, ... :column_name_n], unique: true, name: 'my_custom_index_name'

詳細-http ://apidock.com/rails/ActiveRecord/ConnectionAdapters/SchemaStatements/add_index


1
add_index :table_name, :column_name, unique: true

複数の列に一緒にインデックスを付けるには、単一の列名ではなく列名の配列を渡します。


0

DB列に一意を追加するのに失敗した場合は、モデルにこの検証を追加して、フィールドが一意かどうかを確認します。

class Person < ActiveRecord::Base
  validates_uniqueness_of :user_name
end

ここを参照してください 上記はテストのみを目的としています。@ Nateの提案に従ってDB列を変更してインデックスを追加してください

詳細については、インデックスを参照してください。


2
対応するインデックスなしで検証を追加することはお勧めしません。より適切なオプションは、既存の重複をすべてクリーンアップしてからインデックスを追加することです。そうしないと、既存のデータが無効になるリスクがあり(これらの行への更新が失敗する原因になります)、Rails検証をスキップするコードがある場合、重複が発生する可能性があります。(たとえば、update_all、または直接SQL挿入を実行する場合)
Nate
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.