2

我有一个非常简单的 PostgreSQL 查询来检索最新的 50 篇新闻文章:

SELECT id, headline, author_name, body 
 FROM news 
 ORDER BY publish_date DESC 
 LIMIT 50

现在我还想检索每篇文章的最新 10 条评论。我可以想到两种方法来完成检索它们,但我不确定哪一种在 PostgreSQL 的上下文中是最好的:


选项1:

直接对原始查询中的注释进行子查询,并将结果转换为数组:

SELECT headline, author_name, body,
  ARRAY(
   SELECT id, message, author_name,
     FROM news_comments
     WHERE news_id = n.id
     ORDER BY DATE DESC
     LIMIT 10
  ) AS comments
 FROM news n
 ORDER BY publish_date DESC 
 LIMIT 50

显然,在这种情况下,应用程序逻辑需要知道数组中的哪个索引是哪个列,这没有问题。

我在该方法中看到的一个问题是不知道查询计划器将如何执行它。这会有效地变成 51 个查询吗?


选项 2:

使用原始的非常简单的查询:

SELECT id, headline, author_name, body 
 FROM news 
 ORDER BY publish_date DESC 
 LIMIT 50

然后通过应用程序逻辑,收集所有新闻 ID 并在单独的查询中使用它们,这里必须使用 row_number() 以限制每篇新闻文章的结果数量:

SELECT *
FROM (
    SELECT *,
        row_number() OVER(
            PARTITION BY author_id
            ORDER BY author_id DESC
        ) AS rn
    FROM (
        SELECT *
          FROM news_comment
          WHERE news_id IN(123, 456, 789)
    ) s
) s
where rn <= 10

这种方法显然更复杂,我不确定这是否必须首先检索范围内新闻文章的所有评论,然后删除行数大于 10 的评论。


哪个选项最好?或者有没有我忽略的更好的解决方案?

就上下文而言,这是我自己开发的一个新闻聚合网站,我目前有大约 40,000 篇不同类别的新闻文章,大约有 500,000 条评论,所以我正在寻找帮助我保持增长的最佳解决方案。

4

1 回答 1

3

您应该至少使用EXPLAIN ANALYZE. 这将为您提供优化器在执行语句本身时选择的计划,并为您提供实际运行时间和其他统计信息。

另一种解决方案是使用LATERAL子查询在单独的行中为每个新闻检索 10 条评论,但话又说回来 - 您需要调查和比较计划以选择最适合您的方法:

SELECT 
  n.id, n.headline, n.uathor_name, n.body, 
  c.id, c.message, c.author_name
FROM news n
LEFT JOIN LATERAL ( 
  SELECT id, message, author_name
  FROM news_comments nc
  WHERE n.id = nc.news_id
  ORDER BY nc.date DESC
  LIMIT 10
  ) c ON TRUE
ORDER BY publish_date DESC
LIMIT 50

当您的查询包含从news LATERALLATERAL检索到的每一行的交叉引用时,将使用 connection in子句进行评估。从而使其重复执行,并为源表news中的每一行加入从中检索到的信息。WHERE

这种方法将节省您的应用程序逻辑处理来自选项 1的数组所需的时间,而不必像选项 2那样为每个新闻发出许多单独的查询,从而节省您(在这种情况下)打开单独事务所需的时间,建立连接,检索行等...

最好通过创建索引并查看规划器成本常数和规划器方法配置参数来寻找性能改进,您可以尝试这些参数来了解规划器所做的选择。更多关于这里的主题。

于 2016-09-18T18:29:00.313 回答