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

How do I handle too long index names in a Ruby on Rails ActiveRecord migration?

提问人:JJD 提问时间:3/27/2011 最后编辑:Jai Kumar RajputJJD 更新时间:9/27/2022 访问量:137139

问:

我正在尝试添加一个唯一的索引,该索引是从四个相关表的外键创建的:

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 个字符

我该如何处理?我可以指定不同的索引名称吗?

Ruby-on-Rails 索引 迁移

评论


答:

737赞 fl00r 3/27/2011 #1

提供add_index选项,例如::name

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

如果对块中的引用使用该选项,则它采用与其值相同的选项哈希值::indexcreate_tableadd_index

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

评论

2赞 Jaco Pretorius 12/31/2013
根据 APIdock 的说法,名称必须是字符串,而不是符号
12赞 Kirk 6/24/2016
它的 down 语法适用于任何需要它的人remove_index :studies, :name => 'my_index'
0赞 monteirobrena 12/4/2019
On Rails 与 .只是没有用。4.2.6index: { name: :my_index }name
0赞 Bret Weinraub 8/24/2022
我不认为 ApiDock 是准确的,如果您查看代码 [schema_statements.rb],则 :name 字段to_s在其上运行 [因为它真的应该]
8赞 Nadeem Yasin 9/8/2014 #2

与上一个答案类似:只需将“name”键与常规add_index行一起使用:

def change
  add_index :studies, :user_id, name: 'my_index'
end
25赞 tomascharad 9/15/2014 #3

你也可以做

t.index([:branch_id, :party_id], unique: true, name: 'by_branch_party')

就像在 Ruby on Rails API 中一样。

48赞 ecoologic 5/26/2015 #4

在 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赞 JJD 5/26/2015
感谢分享。如果您可以链接 Postgres 文档以获取限制事实,那就太好了。
0赞 Joshua Pinter 7/16/2018
我们是否需要索引名称中的表名,因为索引无论如何都属于该表?只是好奇这是否有益于我从未见过的地方。
0赞 ecoologic 7/16/2018
您可以根据需要命名索引,但我认为索引名称中的表名有助于保持索引名称的唯一性(这是强制性的),范围明确并提高了一些错误消息的可读性。
211赞 Craig Walker 8/26/2015 #5

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

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

评论

5赞 Craig Walker 8/26/2015
请注意,这不会从原始问题创建多列索引;它只是演示如何缩短长索引名称create_table
10赞 mkrinblk 1/4/2017
当我尝试在带有索引的名称间隔表上创建多态引用时,这帮助了我,在这种情况下,索引实际上是多列(searchable_id 和 searchable_type),并且在生成的名称中添加命名空间变得非常长。t.references :searchable, polymorphic:true, index: {:name => "index_searches_on_searchable"}
0赞 Sergio Belevskij 1/16/2017
对我来说,漂亮、紧凑的解决方案。谢谢!
3赞 Abram 2/14/2017
顺便说一句,这是一个很好的解决方案,因为当您使用 rails 生成器格式创建迁移文件时,它最容易使用foreign_key: truemodel:references
1赞 Fred Willmore 10/8/2019 #6

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

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

表“toooooooooo_loooooooooooooooooooooooooooooong”上的索引名称“index_toooooooooo_loooooooooooooooooooooooooooooong_on_updated_at”太长;限制为 63 个字符

我尝试用于指定索引名称:timestamps

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

但是,这会尝试将索引名称应用于 and 字段: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

1赞 Adário Muatelembe 3/18/2020 #7

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' } 结束

8赞 whatapalaver 4/30/2020 #8

恐怕这些解决方案都不适合我。也许是因为我在create_table迁移中使用了多态关联。belongs_to

我将在下面添加我的代码和一个指向解决方案的链接,该解决方案可以帮助我,以防其他人在搜索与多态关联相关的“索引名称太长”时偶然发现。

以下代码对我不起作用:

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

5赞 Jerph 6/6/2020 #9

我有一个经常使用生成器的项目,需要它是自动的,所以我从 rails 源代码复制了该函数来覆盖它。我把它添加到:index_nameconfig/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

它会创建索引,如果它仍然太长,则将其截断为 63 个字符。如果表名很长,这仍然是非唯一的,但您可以添加复杂功能,例如将表名与列名分开缩短或实际检查唯一性。ix_assignments_on_case_id__project_id

请注意,这是来自 Rails 5.2 项目;如果您决定这样做,请从您的版本复制源代码。