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


391

我试图添加从四个关联表的外键创建的唯一索引:

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

数据库对索引名称的限制导致迁移失败。这是错误消息:

表“研究”上的索引名称“ index_studies_on_user_id_and_university_id_and_subject_name_id_and_subject_type_id”太长;限制为64个字符

我该如何处理?我可以指定其他索引名称吗?

Answers:


589

提供的:name选项add_index,例如:

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

如果在块中使用:index选项on ,它将使用与其值相同的选项哈希:referencescreate_tableadd_index

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

2
据APIdock名称必须是一个字符串,而不是一个符号
雅各·比勒陀利乌斯

7
down语法适用remove_index :studies, :name => 'my_index'于需要它的任何人
Kirk

On Rails 4.2.6index: { name: :my_index }。只有name没有工作。
monteirobrena

174

您还可以在create_table块内的列定义中更改索引名称(例如从迁移生成器获取的名称)。

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

5
请注意,这不会根据原始问题创建多列索引;它只是在演示如何从一个短索引名中缩短create_table
Craig Walker

6
这帮助了我,当我试图通过索引创建一个名称间隔表中的多态引用t.references :searchable, polymorphic:true, index: {:name => "index_searches_on_searchable"}在这种情况下,指数实际上是一个多列(searchable_id和searchable_type)和生成的名称,即命名空间的很长成了。
mkrinblk

对我来说是一个不错的紧凑型解决方案。谢谢!
塞尔吉奥·别列夫斯基

3
也应该有foreign_key: true和方式,这是一个很好的解决方案,因为它是最简单的,当你有创建的迁移文件中使用Rails生成model:references格式
亚伯兰

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年

因为索引仍然属于该表,我们是否需要索引名中的表名?只是好奇这是否有益于我从未见过的地方。
约书亚·品特

您可以根据需要命名索引,但是我认为索引名称中的表名称有助于保持索引名称唯一(这是强制性的),范围合理并提高了错误消息的可读性。
生态的


6

与先前的答案类似:只需在常规的add_index行中使用“名称”键:

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

此代码DID对我有用:

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问答:https : //stackoverflow.com/a/30366460/3258059


1

我有这个问题,但timestamps功能。它是在updated_at上自动生成一个超过63个字符限制的索引:

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

表“ toooooooooo_loooooooooooooooooooooooooooooooooooong”上的索引名“ index_toooooooooo_looooooooooooooooooooooooooooooooooooong_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_loooooooooooooooooooooooooooooooooong”上的索引名称“ 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做| t | t.references:学生,索引:{名称:'name_for_studant_index'} t.references:教师,索引:{name:'name_for_teacher_index'}结尾


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项目。如果您决定这样做,请从您的版本中复制源。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.