我有一个非常简单的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条评论,所以我正在寻找最好的解决方案来帮助我继续增长。
答案 0 :(得分: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 检索的每一行的LATERAL
交叉引用,则使用WHERE
子句中的连接评估LATERAL。从而使其重复执行并加入从源表 news 中为每行检索的信息。
这种方法可以节省应用程序逻辑处理来自选项1 的数组所需的时间,而不必为选项2中的每个新闻发出许多单独的查询保存你(在这种情况下)打开单独的事务,建立连接,检索行等所需的时间......
通过创建索引并查看规划器成本常量和规划器方法配置参数来查找性能改进是很好的,您可以通过实验来了解选择规划器所做的事情。有关该主题的更多信息here。