如何在 Ruby on Rails 迁移中使列唯一并为其编制索引?

Posted

技术标签:

【中文标题】如何在 Ruby on Rails 迁移中使列唯一并为其编制索引?【英文标题】:How do I make a column unique and index it in a Ruby on Rails migration? 【发布时间】:2010-11-29 18:50:49 【问题描述】:

我想在 Ruby on Rails 迁移脚本中创建一个专栏 unique。最好的方法是什么?还有一种方法可以索引表中的列吗?

我想在数据库中强制执行 unique 列,而不是仅使用 :validate_uniqueness_of

【问题讨论】:

【参考方案1】:

旧版本 Rails 的简短答案(请参阅 Rails 4+ 的其他答案):

add_index :table_name, :column_name, unique: true

要同时索引多个列,请传递一个列名数组而不是单个列名,

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

如果您得到“索引名称...太长”,您可以在 add_index 方法中添加 name: "whatever" 以使名称更短。

对于细粒度控制,有一个“execute”方法可以直接执行 SQL。

就是这样!

如果您这样做是为了替代常规的旧模型验证,请检查它是如何工作的。如果没有模型级别的验证,向用户报告的错误可能不会那么好。你总是可以做到这两点。

【讨论】:

+1 建议继续使用 validates_uniqueness_of。使用这种方法的错误处理更加清晰,但我建议他同时做一个索引查询 我试过了,好像不行!我可以使用我定义为唯一的 column_name 插入两条记录!我正在使用 Rails 2.3.4 和 mysql 有什么想法吗? 我使用了你的第二个建议,使用执行:执行“ALTER TABLE users ADD UNIQUE(email)”,它的工作原理!不知道为什么第一个没有兴趣知道 我发现复合索引本身并没有出现任何好的错误,因此也验证了唯一性。干杯! 如果您在尝试创建唯一索引时收到indexed columns are not unique 错误,可能是因为表中的数据已经包含重复项。尝试删除重复数据并再次运行迁移。【参考方案2】:

rails 生成迁移 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

【讨论】:

我对此表示赞成,因为我想要命令行形式。但即使我指定add_index... 而不是add_column...,它也会添加列,这很愚蠢。 是的,可能在下一个版本中。【参考方案3】:

我正在使用 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

【讨论】:

请注意,如果索引已经存在,这将不起作用。【参考方案4】:

您可能希望为唯一键添加名称,因为 rails 的默认唯一键名称可能太长,数据库可能会抛出错误。

要为您的索引添加名称,只需使用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

【讨论】:

【参考方案5】:

由于尚未提及但回答了我在找到此页面时遇到的问题,您还可以指定索引在通过t.referencest.belongs_to 添加时应该是唯一的:

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

  # other columns...
end

(至少从 Rails 4.2.7 开始)

【讨论】:

【参考方案6】:
add_index :table_name, :column_name, unique: true

要将多列索引在一起,请传递一个列名数组而不是单个列名。

【讨论】:

【参考方案7】:

如果要创建新表,可以使用内联快捷方式:

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

【讨论】:

注意可以跳过空定义,即:t.string :title, index: unique: true 所以这会创建一个索引,但它不会是唯一的,对吧? @BurakKaymakci 它将是独一无二的。如果您想要非唯一索引,只需使用 index: true 抱歉,这是我的错误。我只是迷路了,问了一个愚蠢的问题。你回答的正是我想问的哈哈。谢谢!我想问index: true 是否会创建一个唯一索引【参考方案8】:

如果您错过了为 DB 列添加唯一性,只需在模型中添加此验证以检查该字段是否唯一:

class Person < ActiveRecord::Base
  validates_uniqueness_of :user_name
end

refer here 以上仅用于测试目的,请按照@Nate的建议通过更改DB列添加index

请参考with index了解更多信息

【讨论】:

我不建议只添加没有相应索引的验证。更好的选择是清理任何现有的重复项,然后添加索引。否则,您将面临使现有数据无效的风险(这将导致对这些行的任何更新失败),并且如果您有任何跳过 Rails 验证的代码,您仍然可能会出现重复。 (例如,在运行 update_all 或直接 SQL 插入时) 这足以显示一个很好的错误消息,但不能强制数据的完整性。有关说明,请参阅 thoughtbot.com/blog/the-perils-of-uniqueness-validations。

以上是关于如何在 Ruby on Rails 迁移中使列唯一并为其编制索引?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Ruby on Rails ActiveRecord 迁移中处理过长的索引名称?

在 Laravel 5 迁移中使列不可为空

如何在 Ruby on Rails 3 中一次恢复所有迁移?

Ruby on Rails:如何使用迁移向现有列添加非空约束?

如何在 Ruby on Rails 4 中为 post 生成唯一的随机 id?

ruby on rails Heroku 迁移错误