我有一个Ecto迁移,我想修改一些列,但也要迁移一些数据。例如:
import Ecto.Query
defmodule MyApp.Repo.Migrations.AddStatus do
alter table(:foo) do
add(:status, :text)
end
foos = from(f in MyApp.Foo, where: ...)
|> MyApp.Repo.all
Enum.each(foos, fn(foo) ->
# There's then some complex logic here to work
# out how to set the status based on other attributes of `foo`
end)
end
现在,问题在于,通过调用MyApp.Repo.all
,迁移实际上使用了与alter table...
语句使用的单独数据库连接(编辑:这个假设是错,看到接受的答案)。因此,没有status
列,因此整个迁移都会爆炸!注意,我们使用postgres
数据库,因此DDL语句是事务性的。
我可以执行此操作作为两个单独的迁移或mix
任务来设置数据,只迁移模式进行迁移,但不希望为了确保数据一致性。
有关如何以这种方式为MyApp.Repo
查询使用相同数据库连接的任何想法吗?
编辑:注意,我正在处理一小组数据,在我的使用案例中可以接受停机时间。如果情况并非如此,请参阅下面的José的回复以获得一些好的建议。
答案 0 :(得分:3)
您可以通过调用Ecto.Migration.flush/0
来执行迁移中的当前待处理更改。之后的任何代码都将具有状态字段。
defmodule MyApp.Repo.Migrations.AddStatus do
alter table(:foo) do
add(:status, :text)
end
flush()
foos = from(f in MyApp.Foo, where: ...)
|> MyApp.Repo.all
...
end
答案 1 :(得分:2)
一般来说,进行数据迁移和同时更改DDL是一种不好的做法。如果这是一个实时系统并且迁移需要很长时间,则可能会在很长一段时间内产生大量争用。
如果您的应用程序仍处理请求,则可以在处理数据时添加新条目,并且不会处理这些条目!
根据使用情况,有不同的方法可以解决这个问题,但它们通常需要一步一步的方法。例如,如果要添加新列:
第一步是在数据库中引入新列,并确保在创建新条目时填充新列。此步骤仅用于保证将来所有条目都已正确填充。
然后第二步是迁移旧数据
最后,您可以将新数据设为实时,因为您可以假设所有条目都已正确填充