
文档教程知识库【免费下载链接】til:memo: Today I Learned项目地址https://gitcode.com/gh_mirrors/ti/til点击查看免费下载本篇技术指南聚焦于 EctoElixir 生态中最流行的数据库封装层迁移中的唯一索引创建以 elixir/unique-indexes-with-ecto.md 为核心结合本仓库中 Ecto 与 PostgreSQL 相关的系列笔记完整讲解unique_index/3的用法、与index/3的等价关系以及关注关系表这类复合唯一索引的实战场景。读完本文你将能在自己的 Ecto 迁移文件中正确、可逆地添加单列或复合唯一索引并理解它背后的数据库语义。为什么需要唯一索引数据库层的最后一道闸门在业务系统中不允许重复往往既靠应用层校验也靠数据库兜底。应用层校验例如查询后再插入存在竞态窗口——两个请求同时通过校验并写入就可能在数据库中留下重复记录。而唯一索引是数据库层面的硬约束由数据库引擎强制执行是数据完整性的最后一道闸门同时它本身也是一个可用于加速查询的索引结构。在 Ecto 中创建唯一索引的标准方式是在迁移migration里调用Ecto.Migration.unique_index/3它可以针对一个或多个列建立唯一索引。unique_index/3 的基本形态表名 列名列表unique_index/3的函数签名可以概括为unique_index(table, columns, opts \\ [])table目标数据表的名称原子或字符串columns一个列名列表既可以是单列[:email]也可以是多列[:user_id, :role]opts可选的索引选项关键字列表例如用:name自定义索引名、用:where指定部分索引等这些选项与index/3完全一致。单列唯一索引的一个典型例子是对users表的email列建立唯一约束阻止同一邮箱被注册两次create unique_index(:users, [:email])与普通索引不同的是它额外附加了唯一性约束——任何试图插入与现有索引键重复的数据操作都会被数据库拒绝。实战为 followers 关注关系表创建复合唯一索引原文档给出的场景非常经典一张followers关注关系表join table其中followed_user表示被关注者following_user表示关注者。业务规则是同一个人不能重复关注同一个人。这种组合唯一的需求无法用单列索引表达必须使用复合唯一索引。原文档的完整迁移代码如下create table(:followers) do add :followed_user, references(:users), null: false add :following_user, references(:users), null: false end create unique_index(:followers, [:followed_user, :following_user])这段代码有几个值得拆解的细节references(:users)会在该列上建立指向users表的外键关联null: false则进一步约束两个关联字段都不允许为空create unique_index(:followers, [:followed_user, :following_user])对这两个外键列的组合建立唯一索引从而在数据库层面禁止(followed_user, following_user)这个组合出现重复由于followed_user与following_user指向的是同一张users表自引用关系二者语义不同但类型一致列顺序不能随意调换——索引以[:followed_user, :following_user]的顺序定义查询时也应按相同顺序使用才能命中该索引。配合迁移模块的完整写法通常如下change/0会自动把create unique_index/3识别为可逆操作defmodule MyApp.Repo.Migrations.CreateFollowers do use Ecto.Migration def change do create table(:followers) do add :followed_user, references(:users), null: false add :following_user, references(:users), null: false end create unique_index(:followers, [:followed_user, :following_user]) end end复合索引的列顺序与查询命中复合唯一索引的本质是索引键由多个列拼接而成。本仓库的 postgres/adding-composite-uniqueness-constraints.md 用pokemons_trainers训练师与宝可梦的关系表讲解了同样的语义将约束限定在每个 Trainer 最多拥有每种 Pokemon 一只对应的唯一键就是(pokemon_id, trainer_id)的组合。无论用约束还是唯一索引最终强制的都是组合值只出现一次。unique_index/3 是 index/3 的语法糖原文档特别强调了一个关键点unique_index/3是index/3在设置unique: true时的简写形式。也就是说下面两种写法在迁移中产生的效果完全等价# 写法一unique_index/3 简写 create unique_index(:followers, [:followed_user, :following_user]) # 写法二index/3 显式指定 unique: true create index(:followers, [:followed_user, :following_user], unique: true)理解这一层关系很重要unique_index/3并没有引入任何index/3之外的专属能力它只是让唯一索引这个高频需求在代码里更直白、可读性更好。当你想为一个普通索引额外叠加唯一约束或者反过来想给已有唯一索引去掉unique属性时只需要在index/3的选项里增删unique: true即可。本仓库的姊妹篇 elixir/creating-indexes-with-ecto.md 介绍了普通索引的创建方式可以作为对照阅读# 单列普通索引 create index(:users, [:email]) # 复合普通索引 create index(:posts, [:user_id, :title])可见普通索引与唯一索引的调用约定完全一致差异只在于是否传递unique: true。撤销与回滚保持迁移可逆Ecto 迁移的一个重要设计原则是可逆性。create unique_index/3与create table/2一样都能被Ecto.Migration自动生成对应的回滚操作因此写在change/0里时mix ecto.rollback会自动执行drop index撤销索引无需手工编写down/0。如果需要显式控制撤销也可以使用drop/1drop index(:followers, [:followed_user, :following_user])drop 同样支持复合列列表形式与创建时的调用保持一致。数据库层面的对照唯一约束与唯一索引理解Ecto 生成的唯一索引在数据库里到底生成了什么有助于排错。本仓库的 PostgreSQL 系列笔记对这个问题有细致讨论postgres/adding-composite-uniqueness-constraints.md 指出PostgreSQL 中有两种达成组合唯一的途径直接添加unique约束或创建唯一索引。二者在唯一性强制上等价postgres/enforce-uniqueness-on-column-expression.md 揭示了一个关键区别唯一约束只能作用于列不能包含表达式而唯一索引可以建立在表达式上例如lower(email)因此忽略大小写的邮箱唯一这类需求只能通过唯一索引实现postgres/add-unique-constraint-using-existing-index.md 涉及生产环境运维在大表上直接加唯一约束可能长时间阻塞写入可先create index concurrently再add constraint ... unique using index。这条笔记对 Ecto 迁移同样有启发——当表数据量巨大时Ecto 的create unique_index也会面临同样的锁问题此时可考虑用 elixir/execute-raw-sql-in-an-ecto-migration.md 介绍的方式通过execute/1直接书写原生 SQL 以获得更精细的控制。迁移失败的常见原因存量重复数据需要特别提醒的是在已存在重复数据的表上创建唯一索引迁移会直接失败。正如 postgres/add-unique-constraint-using-existing-index.md 所述如果表中该列或该组合已有重复值唯一约束/索引的创建会被数据库拒绝。因此上线此类迁移前应当先编写数据清理步骤例如使用Repo或原生 SQL 去重再执行建索引迁移。验证迁移与查看索引完成迁移编写后通过以下命令验证效果mix ecto.migrate # 执行迁移创建表与唯一索引 mix ecto.rollback # 回滚最近一批迁移验证可逆性迁移执行时 Ecto 会打印对应的 SQL。基于unique_index/3生成的语句本质上是CREATE UNIQUE INDEX例如本文 followers 示例会生成类似CREATE UNIQUE INDEX followers_followed_user_following_user_index ON followers (followed_user, following_user)之后一旦尝试插入重复的组合值数据库便会抛出唯一约束冲突错误如 PostgreSQL 的unique_violation从底层保证关注关系的唯一性。小结围绕 elixir/unique-indexes-with-ecto.md本文完整覆盖了以下要点唯一索引是数据库层强制数据唯一性的手段Ecto 中用unique_index/3创建支持单列与多列复合两种形态关注关系表这类 join table 是复合唯一索引的典型应用场景references/2搭配null: false定义外键列复合唯一索引保证(followed_user, following_user)组合不重复unique_index/3本质是index/3unique: true的简写二者完全等价迁移应保持可逆create unique_index自动支持回滚也可用drop index/3显式撤销结合本仓库 PostgreSQL 系列笔记可进一步理解唯一约束与唯一索引的异同、表达式索引、生产大表的并发建索引策略等进阶话题。下一篇可以继续阅读 elixir/creating-indexes-with-ecto.md系统掌握 Ecto 中普通索引与复合索引的全部创建方式。赞分享文档教程知识库【免费下载链接】til:memo: Today I Learned项目地址https://gitcode.com/gh_mirrors/ti/til点击查看免费下载相关推荐PyPTO vf.update_mask 详解从标量值生成 VF 掩码寄存器mask_regPyPTO vf.update_mask 详解从标量值生成 VF 掩码寄存器mask_reg 导读 vf.update_mask 是 PyPTO SIMD文档教程知识库告别数据重复TiDB唯一索引守护业务数据一致性告别数据重复TiDB唯一索引守护业务数据一致性 你是否曾因订单编号重复导致财务对账混乱或因用户邮箱重复引发登录异常在分布式数据库中保证数据唯一性比单机数数据库分布式数据库后端OLAP告别重复数据validator唯一性验证实战指南告别重复数据validator唯一性验证实战指南 你是否曾因用户注册时输入重复邮箱而头疼还在手动编写复杂的数据库查询来检查数据唯一性本文将带你掌握vali后端质量保障上一篇3步破解网盘限速难题让你下载速度不再龟速前行下一篇探索Mindcraft如何利用大型语言模型构建智能Minecraft AI代理系统创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考