Mysql2::Error: Specified key was too long; max key length is 767 bytes for table having only 87 db fields

Mysql2::Error: Specified key was too long; max key length is 767 bytes for table having only 87 db fields

我有 rails 模型,其中 table 有 87 个数据库列,但是当我尝试添加任何额外的数据库列时,它失败了

ArbitaryDatum.columns.count
 => 87

ArbitaryDatum.table_name
 => "arbitary_data"

ActiveRecord::Migration.add_column :arbitary_data, :attr51, :string

-- add_column(:arbitary_data, :attr51, :string)
   (1.4ms)  ALTER TABLE `arbitary_data` ADD `attr51` varchar(255)
Mysql2::Error: Specified key was too long; max key length is 767 bytes: ALTER TABLE `arbitary_data` ADD `attr51` varchar(255)
ActiveRecord::StatementInvalid: Mysql2::Error: Specified key was too long; max key length is 767 bytes: ALTER TABLE `arbitary_data` ADD `attr51` varchar(255)

我检查了我使用的 mysql 版本,

mysql --version
mysql  Ver 14.14 Distrib 5.5.62, for debian-linux-gnu (i686) using readline 6.3

我不明白为什么它不允许添加额外的 table 列

Q. 为什么我没有得到 Mysql2::Error: Specified key was too long

MySQL在InnoDB中前缀限制为767字节,在MYISAM中为1000字节,

不幸的是,没有真正的解决方案。您唯一的选择是减小列的大小、使用不同的字符集(如 UTF-8)或使用不同的引擎(如 MYISAM)。在这种情况下,我将字符集切换为 UTF-8,从而将最大密钥长度提高到 255 个字符。

在迁移中将字符集设置为 UTF8

reversible do |dir|
      dir.up {
        ActiveRecord::Migration.add_column :data, :attr51, "VARCHAR(255) CHARACTER SET utf8"
      }
    end

在为这个问题奋斗了 3 天之后,@muistooshort 在评论中说的一件事让我找到了正确的解决方案。

你可能想在他的评论中重新审视你的数据库设计,困扰了我几天。所需要的只是正确的方向(在那条评论中)并且我解决了这个问题。

注意:有问题的数据几乎没有被操纵以隐藏项目架构结构。

我在添加新列时遇到问题,如错误日志中所述,但这不是因为在 table 中添加了新列。这是由于数据库中存在 table 结构。

当我从 config/schema.rb 复制 table 模式和索引时,我才知道,以创建相同的新 table arbitary_data_dup

  # replaced `arbitary_data` with `arbitary_data_dup`
  ActiveRecord::Migration.create_table "arbitary_data_dup", force: :cascade do |t|
    # code to add 87 columns for table
  end
  # following index threw error
  ActiveRecord::Migration.add_index "arbitary_data_dup", ["attr1", "arbitary_structure_id"], name: "index_arbitary_data_on_attr1_and_arbitary_structure_id", unique: true, using: :btree
  ActiveRecord::Migration.add_index "arbitary_data_dup", ["attr1"], name: "index_arbitary_data_on_attr1", length: {"attr1"=>191}, using: :btree
  ActiveRecord::Migration.add_index "arbitary_data_dup", ["id"], name: "index_arbitary_data_on_id", using: :btree
  ActiveRecord::Migration.add_index "arbitary_data_dup", ["arbitary_structure_id"], name: "index_arbitary_data_on_arbitary_structure_id", using: :btree

虽然 运行 以上代码在 rails console 中,但它抛出了同样的错误,我确定它不允许添加新列的原因是什么,

ActiveRecord::Migration.add_index "arbitary_data_dup", ["attr1", "arbitary_structure_id"], name: "index_arbitary_data_on_attr1_and_arbitary_structure_id", unique: true, using: :btree
-- add_index("arbitary_data_dup", ["attr1", "arbitary_structure_id"], {:name=>"index_arbitary_data_on_attr1_and_arbitary_structure_id", :unique=>true, :using=>:btree})
   (52.5ms)  CREATE UNIQUE INDEX `index_arbitary_data_on_attr1_and_arbitary_structure_id` USING btree ON `arbitary_data_dup` (`attr1`, `arbitary_structure_id`) 
Mysql2::Error: Specified key was too long; max key length is 767 bytes: CREATE UNIQUE INDEX `index_arbitary_data_on_attr1_and_arbitary_structure_id` USING btree ON `arbitary_data_dup` (`attr1`, `arbitary_structure_id`) 
ActiveRecord::StatementInvalid: Mysql2::Error: Specified key was too long; max key length is 767 bytes: CREATE UNIQUE INDEX `index_arbitary_data_on_attr1_and_arbitary_structure_id` USING btree ON `arbitary_data_dup` (`attr1`, `arbitary_structure_id`)

所以我找到了不允许添加新列的原因 -> 现有索引的名称无效。

所以我删除了那个索引,然后我就可以添加新的列了。

ActiveRecord::Migration.remove_index "arbitary_data", ["attr1", "arbitary_structure_id"]
-- remove_index("arbitary_data", ["attr1", "arbitary_structure_id"])
   (148.6ms)  DROP INDEX `index_arbitary_data_on_attr1_and_arbitary_structure_id` ON `arbitary_data`
   -> 0.1698s
 => nil 
ActiveRecord::Migration.add_column :arbitary_data, :attr51, :string
-- add_column(:arbitary_data, :attr51, :string)
   (523.3ms)  ALTER TABLE `arbitary_data` ADD `attr51` varchar(255)
   -> 0.5238s
 => nil

使用 help

再次添加了相同的索引

创建数据库 utf8_general_ci 然后没问题