【问题标题】:Using LATERAL joins in Ecto v2.0在 Ecto v2.0 中使用 LATERAL 连接
【发布时间】:2016-05-21 23:38:21
【问题描述】:

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

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

谢谢!

【问题讨论】:

    标签: sql postgresql elixir phoenix-framework ecto


    【解决方案1】:

    问题是由这里的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"])
    

    值得注意的是,虽然此查询可用于返回相关帖子和最近的评论,但如果您删除 limit: 1,它实际上会返回 $number_of_comments 行。因此,假设您想检索数据库中所有 100 条帖子以及每个帖子的最新评论,并且数据库中有 200,000 个 cmets,则此查询将返回 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, [])
    

    此查询在

    请注意,这不会返回您的 Ecto 模型结构,只是来自 Postgrex 的原始响应。

    【讨论】:

    • 作为索引的替代方案,您可以在posts 表中拥有一个列last_comment_id,您可以使用它来加入评论。
    • 我已经更新了我的答案,提到使用 LATERAL 加入,这极大地加快了速度:)
    猜你喜欢
    • 2018-06-08
    • 1970-01-01
    • 2023-04-09
    • 1970-01-01
    • 2017-02-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多