为Mysql/Mariadb创建唯一索引的EXTO失败

为Mysql/Mariadb创建唯一索引的EXTO失败,mysql,mariadb,elixir,ecto,Mysql,Mariadb,Elixir,Ecto,我尝试执行以下迁移: defmodule Shopper.Repo.Migrations.MakeNameUniqueShopper do use Ecto.Migration def change do create unique_index :shoppers, [:name] end end 还尝试了创建唯一索引:购物者,[:name],name::name\u unique,创建唯一索引:购物者,[:name],name:“name\u unique”,以及创建索

我尝试执行以下迁移:

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

  def change do
    create unique_index :shoppers, [:name]
  end
end
还尝试了
创建唯一索引:购物者,[:name],name::name\u unique
创建唯一索引:购物者,[:name],name:“name\u unique”
,以及
创建索引(:购物者,[: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
...
...
如果您能帮我纠正错误,我将不胜感激

注意:我正在使用EXTO 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
信息
名称
字段为utf8mb4,由我的架构指定

更新:我知道解决方案是减少
名称
字段长度,但如何使其与phoenix模型和迁移一起工作?因为它需要一个字符串?

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

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

请记住,在变更集中调用
unique\u constraint/2
时,您需要使用相同的名称。

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

使用以下代码创建新的EXTO迁移脚本:

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

使用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

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

这篇博文详细介绍了以下内容:

正如josé所建议的,将此规则应用于索引当然是一种可能性,但我想说的是,您最好修复varchar列的大小,让
创建索引
一开始就不要抱怨,并拥有适当的表列布局:

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
注意
添加:名称、字符串、大小:191
将不起作用,因为EXTO将
:string
类型直接映射到
varchar(255)


我的意见是,在将
:string
映射到其本机类型时,ecto的mysql适配器应该考虑字符集。

请提供
SHOW CREATE TABLE
,以便我们讨论细节。@RickJames我不确定它在这里是否相关。它是由
mix phoenix.gen.model
?这就是@RickJames所要求的(如果我理解正确的话)。添加了创建脚本,我错过了一个关于字段应小于767字节的点,而
add:name,:string
中的字符串实际上是
varchar(255)
。但是
[“name(20)”]
不是一个正确的字段名,因此导致另一个错误我认为正确的答案应该是首先将其迁移到
name(20)
然后
创建唯一的索引:购物者,[:name],name::shopper\u name\u unique
和。。。我的模式是UTF8MB4。下面是用于添加大小为:
add:name,:string,size:100的字段的语法。如果以
UNIQUE name(20)
结束,它将不是您想要的。这仅将
name
的前20个字符限制为唯一。这些城市名称将被视为相同:
Cortijada El Barranco de Amatisteros
Cortijada El Barranco de las Minas
Cortijada El Barranco de Lobos