我正试图加入一个最新的评论后记录,如:
comment = from c in Comment, order_by: [desc: c.inserted_at], limit: 1
post = Repo.all(
from p in Post,
where: p.id == 123,
join: c in subquery(comment), on: c.post_id == p.id,
select: [p.title, c.body],
limit: 1
)
生成此SQL的:
SELECT p0."title",
c1."body"
FROM "posts" AS p0
INNER JOIN (SELECT p0."id",
p0."body",
p0."inserted_at",
p0."updated_at"
FROM "comments" AS p0
ORDER BY p0."inserted_at" DESC
LIMIT 1) AS c1
ON c1."post_id" = p0."id"
WHERE ( p0."id" = 123 )
LIMIT 1
它只是返回
nil
。如果我删除on: c.post_id == p.id
它将返回数据,但显然它将返回所有帖子的最新评论,而不是所讨论的帖子。我做错什么了?修复方法可能是使用
LATERAL
联接子查询,但我无法确定是否可以将p
引用传递到subquery
中。谢谢!
最佳答案
这个问题是由这里的limit: 1
引起的:
comment = from c in Comment, order_by: [desc: c.inserted_at], limit: 1
由于结果查询是
SELECT * FROM "comments" AS p0 ORDER BY p0."inserted_at" DESC LIMIT 1
,它只返回任何帖子的最新评论,而不是我正在查询的帖子。仅供参考,查询大于150ms,包含约200000个注释行,但通过一个简单的索引将其减少到约12ms:
create index(:comments, ["(inserted_at::date) DESC"])
值得注意的是,虽然这个查询可以返回有问题的post和最近的注释,但如果删除
$number_of_comments
,它实际上会返回limit: 1
行。假设您想要检索数据库中的所有100篇文章,其中包含每个文章的最新评论,并且数据库中有200000条评论,那么这个查询将返回200000行。相反,您应该使用LATERAL
连接,如下所述。.
更新
不幸的是ecto doesn't support
LATERAL
joins right now。一个exto
fragment
在这里工作得很好,但是join
查询将片段包装在附加的括号中(即INNER JOIN (LATERAL (SELECT …))
),这不是有效的SQL,所以现在必须使用原始SQL:sql = """
SELECT p."title",
c."body"
FROM "posts" AS p
INNER JOIN LATERAL (SELECT c."id",
c."body",
c."inserted_at"
FROM "comments" AS c
WHERE ( c."post_id" = p."id" )
ORDER BY c."inserted_at" DESC
LIMIT 1) AS c
ON true
WHERE ( p."id" = 123 )
LIMIT 1
"""
res = Ecto.Adapters.SQL.query!(Repo, sql, [])
此查询在同一数据库上以小于1毫秒的速度返回。
注意,这不会返回你的exto模型结构,只是Postgrex的原始响应。