使用Postgres UPDATE..FROM in Ecto 中没有原始 SQL



基于去年的Elixir线程,我能够编写一个原始SQL查询,以使用不相关表中的值批量更新记录。但是,我希望能够使用 Ecto 生成此查询。

在下面的示例中,假设有两个表,猫和狗,并且 cats 表有一个外键 (dog_id(。我想将狗与猫联系起来。

下面的代码是我如何使用Elixir和原始SQL手动执行此操作:

cat_ids = [1,2,3] # pretend these are uuids
dog_ids = [4,5,6] # ... uuids
values =
cat_ids
|> Enum.zip(dog_ids)
|> Enum.map(fn {cat_id, dog_id} ->
"('#{cat_id}'::uuid, '#{dog_id}'::uuid)"
end)
|> Enum.join(", ")
sql = """
UPDATE cats as a
SET dog_id = c.dog_id
from (values #{values}) as c(cat_id, dog_id)
where c.cat_id = a.id;
"""
Repo.query(sql)

有没有办法将其移动到Repo.update_all或某些片段的使用,这样我就不会手动构建查询?

当然,您可以使用 Ecto 语法,但在我看来并没有太大区别,您必须使用模式,例如在我的应用程序中我有一个用户身份验证,这就是我们更新令牌的方式:

def update_token(user_id, token) do
Repo.transaction(fn ->
from(t in UserAuthentication, where: t.user_id == ^to_string(user_id))
|> Repo.update_all(set: [token: token])
end

用户身份验证架构看起来或多或少类似于:

defmodule MyApp.UserAuthentication do
use Ecto.Schema
import Ecto.Changeset
schema "user_authentication" do
field(:user_id, :integer)
field(:token, :string)
timestamps()
end
def changeset(%__MODULE__{} = user, attrs) do
user
|> cast(attrs, [:user_id, :token])
|> validate_required([:user_id, :token])
end
end

这适用于数据验证,适用于您连接的任何数据库。

最新更新