21. Выгрузить все новости с комментариями при большом объёме данных
Условие задачи:
Даны две таблицы:
news— новости;comments— комментарии к новостям.
Необходимо выгрузить все новости вместе со всеми их комментариями при большом объёме данных, не загружая весь результат целиком в память.
Новости без комментариев также должны попадать в результат.
Код:
news
id INT AUTO_INCREMENT PRIMARY KEY
comments
id INT AUTO_INCREMENT PRIMARY KEY
news_id INT REFERENCES news(id)
Спойлеры к решению
Подсказки
LEFT JOIN, чтобы сохранить новости без комментариев.💡 При большом объёме данных лучше читать новости порциями.
💡
LIMIT/OFFSET на результате после JOIN может разбить комментарии одной новости между страницами.💡 Лучше сначала выбрать очередную порцию новостей, а затем присоединить к ним все комментарии.
💡 Для больших таблиц предпочтительна keyset pagination по
id, а не большой OFFSET.Решение
Для первой порции можно выбрать ограниченное количество новостей и только после этого присоединить комментарии:
WITH news_batch AS (
SELECT *
FROM news
ORDER BY id
LIMIT :batch_size
)
SELECT
n.*,
c.*
FROM news_batch n
LEFT JOIN comments c
ON c.news_id = n.id
ORDER BY n.id, c.id;
Для следующих порций лучше использовать последний обработанный id:
WITH news_batch AS (
SELECT *
FROM news
WHERE id > :last_news_id
ORDER BY id
LIMIT :batch_size
)
SELECT
n.*,
c.*
FROM news_batch n
LEFT JOIN comments c
ON c.news_id = n.id
ORDER BY n.id, c.id;
Например:
batch_size = 1000
last_news_id = 15000
следующая порция будет содержать до 1000 новостей с id > 15000 и все комментарии к этим новостям.
Важно ограничивать количество именно новостей до выполнения JOIN.
Такой вариант:
SELECT n.*, c.*
FROM news n
LEFT JOIN comments c
ON c.news_id = n.id
ORDER BY n.id
LIMIT 1000 OFFSET 1000;
ограничивает количество строк уже после соединения. Если у одной новости много комментариев, её данные могут оказаться разделены между несколькими страницами.
Для производительности также полезен индекс:
CREATE INDEX idx_comments_news_id
ON comments(news_id);
При последовательной обработке всех порций можно выгрузить весь набор данных, не удерживая все новости и комментарии одновременно в памяти.