выберите статьи несколько комментариев для каждой статьи в одном запросе

#javascript #sql #node.js #postgresql #greatest-n-per-group

#javascript #sql #node.js #postgresql #наибольшее число на группу

Вопрос:

У меня есть 2 таблицы в моей базе данных postgresql. Упрощенно они выглядят так:

 // table: articles
 ---- ----------- --------------- 
| id | title     | body          |
 ---- ----------- --------------- 
| 1  | hello     | world         |
| ...                            |
 ---- ----------- --------------- 

// table: comments
 ---- ----------- --------------- 
| id | articleid | comment       |
 ---- ----------- --------------- 
| 1  | 1         | looks cool!   |
| ...                            |
 ---- ----------- --------------- 
  

Я хочу создать обзорную страницу со всеми статьями последние 3 комментария к каждой статье. Возможно ли это с помощью одного запроса?

Мои запросы теперь выглядят так (NodeJS):

 let query, values, resu<

let ret = [];

// query to select all articles, ordered by ID desc
query = 'SELECT id, title FROM articles ORDER BY id DESC';
result = await postgresql.query(query, []);

// store all rows in a constant
const articles = result.rows;

// loop over each article
articles.forEach(article => {
  // fetch the last 3 comments for each article
  query = 'SELECT id, comment FROM comments WHERE articleid = $1 ORDER BY id DESC LIMIT 3';
  values = [article.id];
  result = await postgresql.query(query, values);

  // store the comments in a constant
  const comments = result.rows;

  // push the article information (ID   title) and the comments in the ret array
  ret.push({
    articleID: article.id,
    title: article.title,
    comments: comments
  });
});

// ret now contains an array of objects of articles   comments
return ret;
  

Я хочу знать, возможно ли объединить оба запроса в 1, или (если это невозможно), наиболее эффективный способ сделать это.

Пожалуйста, обратите внимание, что обе таблицы больше, чем упрощенные версии, которые я описал здесь. В настоящее время у меня 1700 статей и 100 000 комментариев.

Ответ №1:

Я считаю, что простой:

 select id, comment from 
(select a.id, c.comment, dense_rank() over PARTITION BY a.id ORDER BY c.id DESC) as rn from articles a join comments c on a.id = c.article_id) t
where t.rn <= 3;
  

Так что да, вам нужно использовать только функцию «DENSE_RANK()»https://www.postgresqltutorial.com/postgresql-dense_rank-function /

Комментарии:

1. Спасибо. Я посмотрю на это.

2. Здесь rank() , dense_rank() и row_number() будут делать то же самое для вывода, но row_number() будут лучше других по производительности

Ответ №2:

Один из вариантов использует боковое соединение:

 select a.id, a.title, c.id, c.comment
from articles a
cross join lateral (
    select c.*
    from comments c
    where c.articleid = a.id
    order by id desc
    limit 3
) c
order by a.id desc, c.id desc