根据自然语言或模式描述生成 Ecto 迁移文件。支持处理表、列、索引、约束、引用、枚举及分区。具备可逆迁移、数据迁移及多租户模式支持。适用于在 Elixir 项目中创建或修改数据库模式、添加索引、修改表结构、创建枚举或执行数据迁移。
EctoMigration 13.是一项面向实际任务的技能,主要用于General Transmigrations. from Natural Language.;Parse 用户描述并生成一个迁移文件.;
从功能定位来看,该技能强调把分散的操作要求整理成清晰、可复用的处理流程,使用户能够围绕既定目标快速准备输入、选择执行方式并获得结构化结果。实际使用前应先确认任务范围、数据来源、运行环境、必要权限和关键参数,再依据技能说明逐步执行;若输入条件不完整,应先补齐信息或采用保守配置,避免因错误假设导致结果偏离需求。
执行过程中需要关注工具调用是否成功、接口或依赖是否可用、输出格式是否符合预期,并对异常提示、缺失字段和边界情况进行处理;涉及批量任务时,还应保存进度,避免中断后重复操作。该技能适合用于一次性任务,也可以接入自动化工作流,与其他技能或上层代理配合完成更完整的业务链路;在组合使用时,应明确每一步的输入输出关系,并避免不同步骤之间出现参数冲突。
解析用户描述并生成迁移文件。常见模式如下:
| 用户表述 | 迁移操作 |
|---|---|
| “创建 users 表,包含 email 和 name 字段” | create table(:users) 并定义字段 |
| “为 users 表添加 phone 字段” | alter table(:users), add :phone |
| “在 users 表上使 email 唯一” | create unique_index(:users, [:email]) |
| “为所有表添加 tenant_id 字段” | 多个 alter table 操作,并为每个表添加索引 |
| “将 orders 表中的 status 字段重命名为 state” | rename table(:orders), :status, to: :state |
| “从 users 表中移除 legacy_id 字段” | alter table(:users), remove :legacy_id |
| “为 orders 表的 amount 字段添加检查约束(amount > 0)” | create constraint(:orders, ...) |
mix ecto.gen.migration# 生成路径:priv/repo/migrations/YYYYMMDDHHMMSS_ .exs
命名惯例: 完整类型映射与使用建议请参阅 关键决策: 详细索引设计指南请参阅 以下情况必须添加索引: 规则:禁止在同一个迁移文件中混合模式变更(schema changes)与数据变更(data changes)。 以下操作可由 Ecto 自动推导反向操作: 必须显式定义正向与反向逻辑: Phoenix 1.7+ 支持通过 优先使用 警告:向 PostgreSQL enum 中新增值需使用 相关专题 热门下载 相关下载 精品课程 共6课时 | 54.7万人学习 共89课时 | 134.1万人学习 共49课时 | 82.6万人学习 最新文章create_、
add_、
create__
alter__add_
迁移模板
defmodule MyApp.Repo.Migrations.CreateUsers do
use Ecto.Migration
def change do
create table(:users, primary_key: false) do
add :id, :binary_id, primary_key: true
add :email, :string, null: false
add :name, :string, null: false
add :role, :string, null: false, default: "member"
add :metadata, :map, default: %{}
add :tenant_id, :binary_id, null: false
add :team_id, references(:teams, type: :binary_id, on_delete: :delete_all)
timestamps(type: :utc_datetime_usec)
end
create unique_index(:users, [:tenant_id, :email])
create index(:users, [:tenant_id])
create index(:users, [:team_id])
end
end
字段类型
references/column-types.md。
:binary_id(UUID)—— 表定义需设 primary_key: false,手动添加 :id 字段。:integer(单位为分)或 :decimal —— 禁止使用 :float。timestamps(type: :utc_datetime_usec)。:string 配合应用层的 Ecto.Enum —— 避免使用 PostgreSQL 原生 enum(难以迁移)。:map(对应 PostgreSQL 的 jsonb 类型)。{:array, :string} 等形式。索引策略
references/index-patterns.md。何时添加索引
_id 结尾的列)tenant_id(复合索引中应作为首列)WHERE 子句的字段ORDER BY 的字段索引类型
# 标准 B-tree 索引
create index(:users, [:tenant_id])
# 唯一索引
create unique_index(:users, [:tenant_id, :email])
# 部分索引(条件索引)
create index(:orders, [:status], where: "status != 'completed'", name: :orders_active_status_idx)
# GIN 索引(用于 jsonb 字段)
create index(:events, [:metadata], using: :gin)
# GIN 索引(用于数组字段)
create index(:posts, [:tags], using: :gin)
# 复合索引
create index(:orders, [:tenant_id, :status, :inserted_at])
# 并发索引(不锁表 —— 应单独放在一个迁移中)
@disable_ddl_transaction true
@disable_migration_lock true
def change do
create index(:users, [:email], concurrently: true)
end
约束条件
# 检查约束
create constraint(:orders, :amount_must_be_positive, check: "amount > 0")
# 排他约束(需启用 btree_gist 扩展)
execute "CREATE EXTENSION IF NOT EXISTS btree_gist", ""
create constraint(:reservations, :no_overlapping_bookings,
exclude: ~s|gist (room_id WITH =, tstzrange(starts_at, ends_at) WITH &&)|
)
# 唯一约束(多数场景下与 unique_index 功能一致)
create unique_index(:accounts, [:slug])
引用关系(外键)
add :user_id, references(:users, type: :binary_id, on_delete: :delete_all), null: false
add :team_id, references(:teams, type: :binary_id, on_delete: :nilify_all)
add :parent_id, references(:categories, type: :binary_id, on_delete: :nothing)
on_delete适用场景
:delete_all子记录不能脱离父记录独立存在(例如 memberships、line items)
:nilify_all子记录应在父记录被删除后继续存在(可选关联)
:nothing由应用代码处理(默认行为)
:restrict当存在子记录时,禁止删除父记录
多租户模式
每张表均需包含 tenant_id
def change do
create table(:items, primary_key: false) do
add :id, :binary_id, primary_key: true
add :name, :string, null: false
add :tenant_id, :binary_id, null: false
timestamps(type: :utc_datetime_usec)
end
# 复合索引中 tenant_id 必须置于首位
create index(:items, [:tenant_id])
create unique_index(:items, [:tenant_id, :name])
end
为已有表添加 tenant_id
def change do
alter table(:items) do
add :tenant_id, :binary_id
end
# 在独立的数据迁移中完成数据填充(backfill),之后再执行:
# alter table(:items) do
# modify :tenant_id, :binary_id, null: false
# end
end
数据迁移
安全的数据迁移模式
defmodule MyApp.Repo.Migrations.BackfillUserRoles do
use Ecto.Migration
# 不要使用 schema 模块 —— 它们可能在此迁移运行后发生变更
def up do
execute """
UPDATE users SET role = 'member' WHERE role IS NULL
"""
end
def down do
# 数据迁移通常不可逆
:ok
end
end
分批数据迁移(适用于大表)
def up do
execute """
UPDATE users SET role = 'member'
WHERE id IN (
SELECT id FROM users WHERE role IS NULL LIMIT 10000
)
"""
# 对于超大表,建议改用 Task 或 Oban job 执行
end
可逆 vs 不可逆迁移
可逆迁移(使用
change)
create table ↔ drop tableadd column ↔ remove columncreate index ↔ drop indexrename ↔ rename不可逆迁移(使用
up/down)
modify 字段类型 —— Ecto 无法自动推断原始类型execute 原生 SQLdef up do
alter table(:users) do
modify :email, :citext, from: :string # 添加 from: 可提升可逆性
end
end
def down do
alter table(:users) do
modify :email, :string, from: :citext
end
end
使用
modify 时指定 from:from: 实现可逆的 modify 操作:def change do
alter table(:users) do
modify :email, :citext, null: false, from: {:string, null: true}
end
end
PostgreSQL 扩展
def change do
execute "CREATE EXTENSION IF NOT EXISTS citext", "DROP EXTENSION IF EXISTS citext"
execute "CREATE EXTENSION IF NOT EXISTS pgcrypto", "DROP EXTENSION IF EXISTS pgcrypto"
execute "CREATE EXTENSION IF NOT EXISTS pg_trgm", "DROP EXTENSION IF EXISTS pg_trgm"
end
枚举类型(PostgreSQL 原生 —— 谨慎使用)
:string 字段配合 Ecto.Enum。若必须使用 PostgreSQL 原生 enum:def up do
execute "CREATE TYPE order_status AS ENUM ('pending', 'confirmed', 'shipped', 'delivered')"
alter table(:orders) do
add :status, :order_status, null: false, default: "pending"
end
end
def down do
alter table(:orders) do
remove :status
end
execute "DROP TYPE order_status"
end
ALTER TYPE ... ADD VALUE,该语句无法在事务内执行。因此更推荐使用 :string + Ecto.Enum 方案。检查清单
primary_key: false,并手动添加 add :id, :binary_id, primary_key: truenull: falsetimestamps(type: :utc_datetime_usec)on_delete 行为tenant_id 字段必须建索引(且在复合索引中作为首列)@disable_ddl_transaction true