ChatGPT解决这个技术问题 Extra ChatGPT

How do I make a column unique and index it in a Ruby on Rails migration?

I would like to make a column unique in Ruby on Rails migration script. What is the best way to do it? Also is there a way to index a column in a table?

I would like to enforce unique columns in a database as opposed to just using :validate_uniqueness_of.


n
ndp

The short answer for old versions of Rails (see other answers for Rails 4+):

add_index :table_name, :column_name, unique: true

To index multiple columns together, you pass an array of column names instead of a single column name,

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

If you get "index name... is too long", you can add name: "whatever" to the add_index method to make the name shorter.

For fine-grained control, there's a "execute" method that executes straight SQL.

That's it!

If you are doing this as a replacement for regular old model validations, check to see how it works. The error reporting to the user will likely not be as nice without model-level validations. You can always do both.


+1 for suggesting continuing to use the validates_uniqueness_of. The error handling is much cleaner using this method for the cost of a single indexed query I would suggest he does both
I tried that it doesn't seem to work! I could insert two record with the column_name that I defined as unique! I'm using Rails 2.3.4 and MySql any ideas?
I used you second suggestion by using execute: execute "ALTER TABLE users ADD UNIQUE(email)" and it works! not sure why the first one didn't would be interested in knowing
I found that the composite index alone didn't present any nice errors, therefore validated uniqueness as well. Cheers!
If you get an indexed columns are not unique error when trying to create a unique index, it might be because the data in the table already contains duplicates. Try removing the duplicate data and running the migration again.
g
go2null

rails generate migration add_index_to_table_name column_name:uniq

or

rails generate migration add_column_name_to_table_name column_name:string:uniq:index

generates

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

If you're adding an index to an existing column, remove or comment the add_column line, or put in a check

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

I upvoted this because I wanted the command line form. But it's silly that it adds the column even when I specify add_index... and not add_column....
Yeap, maybe in next version.
P
Pioz

If you are creating a new table, you can use the inline shortcut:

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

Note that you can skip null definition, i.e.: t.string :title, index: { unique: true }
So this will create an index but it's not going to be unique, right?
@BurakKaymakci it will be unique. If you want non unique index use simply index: true.
Oh sorry that's my mistake. I just got lost and asked a stupid question. You answered exactly what I wanted to ask haha. Thank you! I had wanted to ask whether index: true would create a unique index
S
Steve Grossi

Since this hasn't been mentioned yet but answers the question I had when I found this page, you can also specify that an index should be unique when adding it via t.references or t.belongs_to:

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

  # other columns...
end

(as of at least Rails 4.2.7)


N
Nicholas Nelson

I'm using Rails 5 and the above answers work great; here's another way that also worked for me (the table name is :people and the column name is :email_address)

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

Note this does not work if the index already exists.
S
Swaps

You might want to add name for the unique key as many times the default unique_key name by rails can be too long for which the DB can throw the error.

To add name for your index just use the name: option. The migration query might look something like this -

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

More info - http://apidock.com/rails/ActiveRecord/ConnectionAdapters/SchemaStatements/add_index


P
Peter Haddad
add_index :table_name, :column_name, unique: true

To index multiple columns together, you pass an array of column names instead of a single column name.


R
Ravistm

If you have missed to add unique to DB column, just add this validation in model to check if the field is unique:

class Person < ActiveRecord::Base
  validates_uniqueness_of :user_name
end

refer here Above is for testing purpose only, please add index by changing DB column as suggested by @Nate

please refer this with index for more information


I would not recommend just adding the validation without a corresponding index. The better option is to clean up any existing duplicates and then add the index. Otherwise you risk invalidating existing data (which will cause any updates to those rows to fail), and you could still end up with duplicates if you have any code that skips Rails validations. (e.g., when running an update_all, or direct SQL inserts)
This is good enough to show a nice error message but not to enforce integrity of your data. See thoughtbot.com/blog/the-perils-of-uniqueness-validations for an explanation.