如何用Ecto在PostgreSQL的text列追加字符串?
解决Ecto更新PostgreSQL指定类型最新行及字符串拼接问题
问题1:update_all不支持last()的解决方法
你遇到的错误是因为Repo.update_all()仅允许配合where、join这类过滤表达式,无法直接使用last()这类排序取数的构造。要更新指定kind的最新行,有两种可行方案:
方案1:先查询最新行ID再更新
先获取目标kind对应的最新行主键,再通过主键精准更新:
def append_dictionary(kind, line) do latest_id = from(d in Dictionary, where: d.kind == ^kind, select: d.id) |> last() |> Repo.one() if latest_id do from(d in Dictionary, where: d.id == ^latest_id) |> update([d], set: [body: concat(d.body, ^line)]) |> Repo.update_all([]) else # 无对应行时插入新行,可根据业务需求调整逻辑 Repo.insert!(%Dictionary{kind: kind, body: line}) end end
方案2:用子查询直接匹配最新行
通过子查询在where条件中定位最新行,避免两次查询,更适合存在并发操作的场景:
def append_dictionary(kind, line) do latest_subquery = from(d in Dictionary, where: d.kind == ^kind, select: d.id) |> last() |> subquery() from(d in Dictionary, where: d.id in subquery(latest_subquery)) |> update([d], set: [body: concat(d.body, ^line)]) |> Repo.update_all([]) end
问题2:Ecto字符串拼接的正确方式
Ecto不支持用+拼接字符串,PostgreSQL中字符串拼接使用||运算符,你有两种选择:
使用Ecto内置的
concat/2函数(推荐,更符合Ecto语法风格):concat(d.body, ^line)如果需要在追加内容前添加换行,可扩展为:
concat(d.body, "\n", ^line)使用
fragment调用数据库原生运算符:fragment("? || ?", d.body, ^line)带换行的写法:
fragment("? || '\n' || ?", d.body, ^line)
额外注意
如果存在并发写入同kind最新行的场景,建议在查询时添加行锁,避免更新错误。比如在子查询中加入lock: "FOR UPDATE":
latest_subquery = from(d in Dictionary, where: d.kind == ^kind, select: d.id, lock: "FOR UPDATE") |> last() |> subquery()
内容的提问来源于stack exchange,提问作者vaer-k
相关产品推荐
相关产品推荐

