我正在尝试加入关于帖子记录的最新评论,如下:
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
。
谢谢!
答案 0 :(得分:3)
问题是由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
,因此它只返回最新评论的任何帖子,而不是我查询的帖子。
仅供参考,查询大于150毫秒,大约有200,000条评论行,但是使用简单索引将其降低到~12毫秒:
create index(:comments, ["(inserted_at::date) DESC"])
值得注意的是,虽然此查询可用于返回相关帖子且仅返回最新评论,但如果您删除$number_of_comments
,它实际上会返回limit: 1
行。因此,如果您想要使用每个帖子的最新评论检索数据库中的所有100个帖子,并且您在数据库中有200,000条评论,则此查询将返回200,000行。相反,您应该使用LATERAL
联接,如下所述。
不幸的是ecto doesn't support LATERAL
joins right now。
ecto 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, [])
此查询在同一数据库中返回< 1ms。
请注意,这不会返回您的Ecto模型结构,只返回Postgrex的原始响应。