Одна вещь выскакивает на меня. В обоих ваших подзапросах есть такая строка.
WHERE YEAR(posts.CreationDate) = 2010
Вы вызываете функцию для значения столбца. Это не sargeable . Он не позволяет MySQL использовать индекс для этого столбца и вместо этого требует полного сканирования. (MySQL и другие СУБД все еще слишком глупы, чтобы знать, что YEAR(timestamp)
может быть удовлетворено сканированием диапазона индекса.)
Поэтому измените эти ГДЕ на подобные вещи.
WHERE posts.CreationDate >= '2010-01-01'
AND posts.CreationDate < '2010-01-01' + INTERVAL 1 YEAR
и поместите индекс в столбцы CreationDate в ваших таблицах сообщений и комментариев. Затем планировщик запросов может выполнить произвольный поиск в индексе для первой соответствующей строки, а затем последовательно прочитать его до последней соответствующей строки. Это называется сканированием диапазона индекса, и оно гораздо более эффективно, чем сканирование полной таблицы.
РЕДАКТИРОВАНИЕ Вам нужны следующие индексы:
CREATE INDEX date_user ON posts ( CreationDate, OwnerUserId );
CREATE INDEX date_user ON comments ( CreationDate, UserID);
Я предлагаю вам рефакторинг вашего запроса, чтобы сделать ваши подзапросы, где вся работа выполняется, быстрее.
Это должны быть подзапросы. Каждый из них выдает количество элементов для каждого пользователя в желаемом диапазоне времени.
SELECT OwnerUserId, COUNT(*) posts
FROM posts
WHERE CreationDate >= '2010-01-01'
AND CreationDate < '2010-01-01' + INTERVAL 1 YEAR
GROUP BY OwnerUserId
SELECT UserId, COUNT(*) comments
FROM comments
WHERE CreationDate >= '2010-01-01'
AND CreationDate < '2010-01-01' + INTERVAL 1 YEAR
GROUP BY UserId
Эти запросы экономят время, агрегируя (суммируя по группам) минимальный объем данных, необходимый для удовлетворения запроса. И их можно удовлетворить, выполнив быстрое сканирование диапазона индексов по предложенным мною индексам.
Затем вы можете использовать эти подзапросы в своем основном запросе, выбирая имена пользователей из таблицы users
, например: this.
SELECT users.Id user_ID, users.Username, c.comments, p.posts
FROM users
JOIN (
SELECT OwnerUserId, COUNT(*) posts
FROM posts
WHERE CreationDate >= '2010-01-01'
AND CreationDate < '2010-01-01' + INTERVAL 1 YEAR
GROUP BY OwnerUserId
) p ON users.ID = p.OwnerUserId
JOIN (
SELECT UserId, COUNT(*) comments
FROM comments
WHERE CreationDate >= '2010-01-01'
AND CreationDate < '2010-01-01' + INTERVAL 1 YEAR
GROUP BY UserId
) c ON users.ID = c.UserId
WHERE c.comments > p.posts
ORDER BY users.ID
LIMIT 50;
Я подозреваю, что вы получите значительное повышение производительности, если добавите составные индексы, которые я упомянул. Вы можете удалить индексы на CreationDate; они избыточны при добавлении составных индексов.
Вот полезная ссылка https://use-the-index-luke.com/