如何使用 ecto 查询表达式在 3 个表之间进行连接和过滤
How to join and filter between 3 tables with ecto query expressions
我有 3 个架构:Post
、Author
、Assistant
。
Post
belongs_to :author
Author
has_many :posts
和 has_one :assistant
Assistant
belongs_to :author
我想构建一个只获取 Post
的查询,其中 Author
有一个 Assistant
。
到目前为止,我只能加入 Author
到 Post
。我不知道如何根据 Author
的 Assistant
.
的存在来查询 Post
s
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
关系执行此操作。
干杯!
我有 3 个架构:Post
、Author
、Assistant
。
Post
belongs_to :author
Author
has_many :posts
和 has_one :assistant
Assistant
belongs_to :author
我想构建一个只获取 Post
的查询,其中 Author
有一个 Assistant
。
到目前为止,我只能加入 Author
到 Post
。我不知道如何根据 Author
的 Assistant
.
Post
s
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
关系执行此操作。
干杯!