仅追加 table 查询的 Ecto JOIN 并发症

Ecto JOIN complications for append-only table query

我正在尝试查询具有仅附加语义的 Ecto table,所以我想要 最新 版本的完整行ID。该技术是 described here,但简而言之:我想加入一个 table 本身,并使用一个子查询获取 ID 的最近时间。在 SQL 这看起来像:

SELECT r.*
FROM rules AS r
JOIN (
  SELECT id, MAX(inserted_at) AS inserted_at FROM rules GROUP BY id
) AS recent_rules
ON (
  recent_rules.id = r.id
  AND recent_rules.inserted_at = r.inserted_at)

我在 Ecto 中无法表达这一点。我试过这样的事情:

maxes =
  from(m in Rule,
    select: {m.id, max(m.inserted_at)},
    group_by: m.id)

from(r in Rule,
  join: m in ^maxes, on: r.id == m.id and r.inserted_at == m.inserted_at)

但是尝试运行这个,我遇到了一个限制:

queries in joins can only have where conditions in query

建议 maxes 必须只是一个 SELECT _ FROM _ WHERE 形式。

如果我尝试在 JOIN 中切换 maxesRule

maxes =
  from(m in Rule,
    select: {m.id, max(m.inserted_at)},
    group_by: m.id)

from(m in maxes,
  join: r in Rule, on: r.id == m.id and r.inserted_at == m.inserted_at)

那么我无法 SELECT 整行,只能 idMAX(inserted_at)

有谁知道如何做这个 JOIN?或者在 Ecto 中查询仅追加的更好方法?谢谢

执行 m in ^maxes 不是 运行 子查询,而是查询组合(如果在 from 中)或将查询转换为连接(在连接中)。在这两种情况下,您都在更改相同的查询。鉴于您的初始查询,我相信您需要子查询。

另请注意,子查询需要 select 到 return 映射,因此我们稍后可以引用这些字段。按照这些思路应该可以工作:

maxes =
  from(m in Rule,
    select: %{id: m.id, inserted_at: max(m.inserted_at)},
    group_by: m.id)

from(r in Rule,
  join: m in ^subquery(maxes), on: r.id == m.id and r.inserted_at == m.inserted_at)

PS:我已经向 Ecto 推送了一个提交,澄清了像你这样的情况下的错误消息。

invalid query was interpolated in a join.
If you want to pass a query to a join, you must either:

  1. Make sure the query only has `where` conditions (which will be converted to ON clauses)
  2. Or wrap the query in a subquery by calling subquery(query)