在Ecto v2.0

时间:2016-05-10 22:58:14

标签: sql postgresql elixir phoenix-framework ecto

我正在尝试加入关于帖子记录的最新评论,如下:

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

谢谢!

1 个答案:

答案 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的原始响应。