You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用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中字符串拼接使用||运算符,你有两种选择:

  1. 使用Ecto内置的concat/2函数(推荐,更符合Ecto语法风格):

    concat(d.body, ^line)
    

    如果需要在追加内容前添加换行,可扩展为:

    concat(d.body, "\n", ^line)
    
  2. 使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.01 11:11:09