如何使用 ecto 查询表达式在 3 个表之间进行连接和过滤

How to join and filter between 3 tables with ecto query expressions

我有 3 个架构:PostAuthorAssistant

Post belongs_to :author

Author has_many :posts 和 has_one :assistant

Assistant belongs_to :author

我想构建一个只获取 Post 的查询,其中 Author 有一个 Assistant

到目前为止,我只能加入 AuthorPost。我不知道如何根据 AuthorAssistant.

的存在来查询 Posts
Post
|> join(:inner, [p], a in assoc(p, :author))
|> Repo.all()

编辑 1

我想我已经取得了一些进展,这个查询似乎正在处理我现在在数据库中的几条记录,但不确定它是否是传统方式。

Post
|> join(:inner, [p], a in assoc(p, :author), as: :author)
|> join(:left, [author: a], asst in assoc(a, :assistant), as: :assistant)
|> where([assistant: asst], not is_nil(asst.id)))
|> Repo.all()

在这种特殊情况下,这可能有点矫枉过正,但将此类查询分离到 subqueries.

中总是更容易
authors_with_assistants =
  from a in Author, where: not is_nil(a.assistant_id)

authors_with_assistants =
  from a in Author,
  left_join: asst in assoc(a, :assistant)

posts =
  from p in Post,
    join: a in subquery(authors_with_assistants),
    on: p.author_id == a.id

Repo.all(posts)

此外,在后者上使用条件链接连接也可以工作(这意味着您在 Edit1 部分中的查询看起来不错。)

我认为这种情况不需要左连接。您应该能够在所有 table 上进行内部联接,因为内部联接将表明有助手在场,并排除了在没有助手的情况下找到作者的任何可能性。你应该可以这样写:

query = from(p in Post,
  join: a in Author,
  on: a.id == p.author_id,
  join: ast in Assistant,
  on: ast.author_id == a.id,
  # See notes below about the distinct
  distinct: p.id
)

Repo.all(query)

因为您在所有 table 上进行了内部联接,这表明 post 必须有作者,而作者必须有助手才能成为此查询中的结果行。如果你想做一些事情,比如找出所有 post 的作者没有助手,你会想要离开加入。

关于distinct的注意事项:

我放置了 distinct 子句,以防您将多个助手分配给数据库中的一位作者(这表明数据不正确)并且结果集中的 posts 会重复。这是因为 has_one 关系只是一个受约束的 has_many。如果在 assistants table 中的 author_id 列上有唯一索引,则不需要 distinct 子句。如果你没有唯一索引,你可以在迁移中这样写:

create unique_index("assistants", :author_id)

您可能应该为所有 has_one 关系执行此操作。

干杯!