Оптимизация встроенного запроса SELECT в MySQL
-
06-09-2019 - |
Вопрос
Хорошо, вот запрос, который я выполняю прямо сейчас для таблицы, содержащей 45 000 записей и имеющей размер 65 МБ...и вот-вот станет все больше и больше (так что я должен подумать и о будущем выступлении здесь).:
SELECT count(payment_id) as signup_count, sum(amount) as signup_amount
FROM payments p
WHERE tm_completed BETWEEN '2009-05-01' AND '2009-05-30'
AND completed > 0
AND tm_completed IS NOT NULL
AND member_id NOT IN (SELECT p2.member_id FROM payments p2 WHERE p2.completed=1 AND p2.tm_completed < '2009-05-01' AND p2.tm_completed IS NOT NULL GROUP BY p2.member_id)
И, как вы можете себе представить, а можете и не представить, это приводит к остановке сервера mysql...
Что он делает - он просто извлекает количество новых пользователей, которые зарегистрировались, имеют по крайней мере один "завершенный" платеж, значение tm_completed не пустое (поскольку оно заполняется только для завершенных платежей), и (встроенный Select) у этого участника никогда раньше не было "завершенного" платежа - это означает, что он новый участник (просто потому, что система выполняет ребиллы и еще много чего, и это единственный способ провести различие между существующим участником, который только что получил ребиллинг, и новым участником, которому выставили счет в первый раз).
Итак, есть ли какой-либо возможный способ оптимизировать этот запрос, чтобы использовать меньше ресурсов или что-то в этом роде, и перестать ставить мои ресурсы mysql на колени ...?
Мне не хватает какой-либо информации, чтобы прояснить это еще больше?Дай мне знать...
Редактировать:
Вот индексы, которые уже есть в этой таблице:
ПЕРВИЧНЫЙ ПЕРВИЧНЫЙ 46757 payment_id
member_id ИНДЕКС 23378 member_id
payer_id ИНДЕКС 11689 payer_id
coupon_id ИНДЕКС 1 coupon_id
tm_added ИНДЕКС 46757 tm_added, product_id
tm_completed ИНДЕКС 46757 tm_completed, product_id
Решение
Такого рода IN
подзапросы в MySQL работают немного медленно.Я бы перефразировал это так:
SELECT COUNT(1) AS signup_count, SUM(amount) AS signup_amount
FROM payments p
WHERE tm_completed BETWEEN '2009-05-01' AND '2009-05-30'
AND completed > 0
AND NOT EXISTS (
SELECT member_id
FROM payments
WHERE member_id = p.member_id
AND completed = 1
AND tm_completed < '2009-05-01');
Чек 'tm_completed IS NOT NULL
- в этом нет необходимости, поскольку это подразумевается вашим BETWEEN
состояние.
Также убедитесь, что у вас есть индекс на:
(tm_completed, completed)
Другие советы
Мне было весело собирать это решение, которое не требует подзапроса:
SELECT count(p1.payment_id) as signup_count,
sum(p1.amount) as signup_amount
FROM payments p1
LEFT JOIN payments p2
ON p1.member_id = p2.member_id
AND p2.completed = 1
AND p2.tm_completed < date '2009-05-01'
WHERE p1.completed > 0
AND p1.tm_completed between date '2009-05-01' and date '2009-05-30'
AND p2.member_id IS NULL;
Избегайте использования IN с подзапросом;MySQL не очень хорошо их оптимизирует (хотя в 5.4 и 6.0 есть незавершенные оптимизации по этому поводу (см. здесь).Переписав это как join, вы, вероятно, повысите производительность:
SELECT count(payment_id) as signup_count, sum(amount) as signup_amount
FROM payments p
LEFT JOIN (SELECT p2.member_id
FROM payments p2
WHERE p2.completed=1
AND p2.tm_completed < '2009-05-01'
AND p2.tm_completed IS NOT NULL
GROUP BY p2.member_id) foo
ON p.member_id = foo.member_id AND foo.member_id IS NULL
WHERE tm_completed BETWEEN '2009-05-01' AND '2009-05-30'
AND completed > 0
AND tm_completed IS NOT NULL
Во-вторых, мне нужно было бы увидеть вашу схему таблицы;используете ли вы индексы?