5

我尝试进行以下迁移:

defmodule Shopper.Repo.Migrations.MakeNameUniqueShopper do
  use Ecto.Migration

  def change do
    create unique_index :shoppers, [:name]
  end
end

也尝试过create unique_index :shoppers, [:name], name: :name_unique,create unique_index :shoppers, [:name], name: "name_unique"create index(:shoppers, [:name], unique: true)

但他们因类似错误而失败:

[info]  == Running Shopper.Repo.Migrations.MakeNameUniqueShopper.change/0 forward

[info]  create index shoppers_name_index
** (Mariaex.Error) (1071): Specified key was too long; max key length is 767 bytes
    (ecto) lib/ecto/adapters/sql.ex:172: Ecto.Adapters.SQL.query!/5
    (elixir) lib/enum.ex:1261: Enum."-reduce/3-lists^foldl/2-0-"/3
...
...

任何帮助将不胜感激,以帮助我解决错误。

注意:我正在使用 ecto 1.02

以下是创建的第一个迁移mix phoenix.gen.model

defmodule Shopper.Repo.Migrations.CreateV1.Shopper do
  use Ecto.Migration

  def change do
    create table(:shoppers) do
      add :name, :string
      add :oauth_token, :string

      timestamps
    end
  end
end

信息:该name字段是 utf8mb4,由我的架构指定

更新:我知道解决方案是减少name字段长度,但是如何使它与凤凰模型和迁移一起工作?因为它期望一个字符串?

4

4 回答 4

3

“名称”字段太长。您应该通过在声明它时传递 size 选项来确保它的大小小于 767 字节,或者只索引字段的一部分:

create unique_index :shoppers, ["name(20)"], name: :shoppers_name_unique

unique_constraint/2请记住,在调用变更集时需要提供相同的名称。

于 2015-09-09T12:44:22.873 回答
1

感谢 José Valim 帮助我完成他的回答,尽管这个答案是我的问题的确切解决方案。

使用以下代码创建一个新的 ecto 迁移脚本:

defmodule Shopper.Repo.Migrations.MakeNameUniqueShopper do
  use Ecto.Migration

  def change do
    alter table(:shoppers) do
      modify :name, :string, size: 100
    end

    create unique_index :shoppers, [:name], name: :shopper_name_unique
  end
end
于 2015-09-09T16:58:25.747 回答
1

这里的问题是 InnoDB 的密钥大小(767 字节),它直接映射到varchar()相对于列字符集的列的可能大小。如果您使用 charset utf8,一varchar列最多可以存储 255 个字符。如果使用utf8mb4,一varchar列只能存储 191 个字符。

这篇博文详细介绍了:https ://mathiasbynens.be/notes/mysql-utf8mb4

像 josé 建议的那样,将这条规则应用于索引当然是一种可能性,我会说你最好修复你的 varchar 列大小,并且CREATE INDEX首先不要抱怨,并且有一个适当的表列布局:

defmodule Shopper.Repo.Migrations.CreateV1.Shopper do
  use Ecto.Migration

  def change do
    create table(:shoppers) do
      add :name, :varchar, size: 191
      add :oauth_token, :varchar, size: 191

      timestamps
    end
    create unique_index(:shoppers, [:name])
  end
end

注意add :name, :string, size: 191不会像 ecto 将:string类型直接映射到varchar(255) mysql 适配器中一样工作

:string我的观点是,ecto 的 mysql 适配器在映射到其本机类型时应该考虑字符集,就像 rails 一样

于 2016-08-03T16:30:37.507 回答
0

使用 utf8mb4 编码创建更短的 varchar/text 列大小的替代方法是配置 MySQL 以将最大 InnoDB 索引前缀大小增加到 3072 字节。

defmodule Shopper.Repo.Migrations.CreateV1.Shopper do
  use Ecto.Migration

  def change do
    # just needs to be done once
    execute "SET GLOBAL innodb_file_format = BARRACUDA"
    execute "SET GLOBAL innodb_file_per_table = ON"
    execute "SET GLOBAL innodb_large_prefix = ON"

    # in MySQL 5.7.9 or higher, this sets the default row format
    # otherwise for all new tables you create, you must manually 
    # alter row_format to dynamic before adding any string/text columns
    execute "SET GLOBAL innodb_default_row_format = DYNAMIC"

    # change existing shoppers row format to dynamic
    execute "ALTER TABLE shoppers ROW_FORMAT = DYNAMIC"

    create unique_index :shoppers, [:name], name: :shopper_name_unique         
  end
end
于 2016-06-02T23:31:35.440 回答