2016-11-07 3 views
0

Я написал этот запрос несколько месяцев назад. Все было хорошо. Но с каждым днем ​​этот запрос замедляется.Почему этот запрос занимает так много времени?

Этот запрос проверяет историю счетов в нескольких таблицах помимо того же выполнения таблицы.

Вот запрос -

SELECT * FROM (
      SELECT *,     
      (SELECT username FROM users WHERE id = u_bills.UserId) AS username, 
      (SELECT first_name FROM users WHERE id = u_bills.UserId) AS first_name, 
      (SELECT last_name FROM users WHERE id = u_bills.UserId) AS last_name, 
      (SELECT phone FROM users WHERE id = u_bills.UserId) AS phone, 
      (SELECT email FROM users WHERE id = u_bills.UserId) AS email, 
      (SELECT CPRate FROM cpt WHERE UserId = u_bills.UserId ORDER BY AddedDate DESC LIMIT 0,1) AS cprate, 
      (SELECT (SELECT PopName FROM pops WHERE PopId = p.PopName) AS PopFullName FROM u_setupinfos AS p 
      WHERE UserId = u_bills.UserId) AS popname, 
      (SELECT active FROM users WHERE id = u_bills.UserId) AS active,     
      (SELECT SUM(PaidAmount) AS PaidAmount FROM u_billhistory 
      WHERE UserId = u_bills.UserId AND MONTH(AddedDate) = MONTH(PaidDate)) AS PaidAmount, 
      (SELECT PaidDate FROM u_billhistory 
      WHERE UserId = u_bills.UserId AND MONTH(AddedDate) = MONTH(PaidDate) 
      GROUP BY MONTH(PaidDate)) AS PaidDate, 
      (SELECT PaymentMedia FROM u_billhistory 
      WHERE UserId = u_bills.UserId AND MONTH(AddedDate) = MONTH(PaidDate) 
      GROUP BY MONTH(PaidDate)) AS PaymentMedia, 
    (SELECT TransactionId FROM u_billhistory 
      WHERE UserId = u_bills.UserId AND MONTH(AddedDate) = MONTH(PaidDate) GROUP BY MONTH(PaidDate)) AS TransactionId, 
      (SELECT GROUP_CONCAT(CONCAT(`BillHisId`,';',`PaidAmount`,';',`PaidDate`) separator '|') AS vals 
FROM u_billhistory 
WHERE UserId = u_bills.UserId 
AND YEAR(PaidDate) = YEAR(CURDATE())) AS TotalPaids 
      FROM u_bills) AS m 
     WHERE m.username = 'abc' 

Пожалуйста, проверьте отчет EXPLAIN на image-

enter image description here

Я хочу организовать этот запрос. Мне нужны предложения, чтобы сделать этот запрос быстрее, чем 3-5 секунд, а не 1 и половину минуты или больше. Какие ошибки я сделал по этому запросу?

Важен Примечание-

Я проиндексирован почти каждые столбцы, используя по запросу

+0

Возможно, нет - вы обновили статистику на сервере? Как ваши данные выросли за это время? Существуют ли другие БД, конкурирующие за ресурсы? –

+4

Тем не менее, почему все подзапросы получают одно поле за раз? –

+2

, потому что его гигантский с подзапросами –

ответ

5

Это медленно, потому что вы делаете это запустить отдельный поиск в дополнительную таблицу для каждого поля каждой записи в результатах ... иногда более одного. Вы должны использовать JOINs вместо вложенных SELECT. Для некоторых сгруппированных результатов вы можете присоединиться к оператору SELECT, который делает его собственный GROUP BY.


Кроме того, отдельные индексы на отдельных столбцах НЕ ПОМОЖЕТ !!

Подумайте об индексах, таких как телефонная книга. В простой телефонной книге используется кластерный индекс по фамилии и имени. Затем он также перечисляет адрес и номер телефона для каждого элемента. Вот что такое кластеризованный индекс: просто порядок, в котором хранятся данные.

Дополнительные индексы подобны дополнениям. Некластеризованный индекс в «Телефонном номере» будет похож на дополнение в конце книги, в котором перечислены номера телефонов в порядке, а затем отображается Фамилия и Имя (но не адрес). Индекс «Адрес» будет делать то же самое: отдельное приложение, которое перечисляет адреса по порядку, а затем дает вам имя.

С этими указательными дополнениями, если у вас есть номер телефона и вы хотите узнать адрес, вы должны сначала найти номер телефона в приложении, чтобы найти имя, а затем найти имя в главной книге, чтобы получить адрес. В этом примере телефонное приложение действительно помогает, потому что это лучше, чем полное сканирование телефонной книги.

Тем не менее, важно отметить, что адресное приложение _does не поможет в этом поиске, даже если адрес является точным полем, которое вы хотели найти. Это может быть полезным для других поисков, но в этом случае это не поможет. Кроме того, имейте в виду, что каждое обновление вашей книги требует обновления не только книги, но и всех дополнений. Это не просто пространство для хранения, но время, затрачиваемое на обновление каждого индекса. Вам нужно подумать, стоит ли это. Если у вас есть набор индексов, которые не используются, вам будет лучше без них.


Возвращаясь к вашим конкретным проблемам, о только индексы, которые действительно помогут вам в u_bills.username, user.UserId (надеюсь, кластер для его таблицы) и один показатель для обоего u_billhistory.UserId и u_billhistory.PaidDate.

Вот начальная попытка рефакторинга этот запрос:

SELECT ub.*, u.username, u.first_name, u.last_name, u.phone, u.email, u.active, 
    m.PaidAmount, m.PaymentMedia, m.TransactionId, 
    y.TotalPaids, p.PopName As PopFullName, cpt2.CPRate  

FROM u_bills ub 
INNER JOIN users u on u.id = ub.UserId 
INNER JOIN u_setupinfos usi on usi.UserId = ub.UserID 
INNER JOIN pops p ON p.PopId = usi.PopName 
INNER JOIN (
    SELECT UserId, MIN(AddedDate) As cptDate 
    FROM cpt 
    GROUP BY UserID   
) cpt1 ON cp1.UserID = ub.UserID 
INNER JOIN (
    SELECT UserID, AddedDate, MIN(CPRate) AS CPRate 
    FROM cpt 
    GROUP BY UserID, AddedDate 
) cpt2 ON cpt2.UserID = ub.UserID AND cpt2.AddedDate = cpt1.ctpDate 
INNER JOIN (
    SELECT UserID, MONTH(PaidDate) PaidMonth, SUM(PaidAmount) AS PaidAmount, 
     PaymentMedia, TransactionId --these two fields need an aggregate function of some type! 
    FROM u_billhistory 
    GROUP BY UserId, MONTH(PaidDate) 
) m ON m.UserID = ub.UserId AND m.PaidMonth = MONTH(ub.AddedDate) 
INNER JOIN (
    SELECT UserID, YEAR(PaidDate) As PaidYear, 
     GROUP_CONCAT(CONCAT(`BillHisId`,';',`PaidAmount`,';',`PaidDate`) separator '|') AS TotalPaids 
    FROM u_billhistory 
    WHERE YEAR(PaidDate) = YEAR(CurDate()) 
    GROUP BY UserId, YEAR(PaidDate) 
) y ON Y.UserId = ub.UserId 
WHERE username='abc' 

Обратите внимание, что это не совсем полным, потому что мы не имеем всю информацию о вашей схеме и данных, а также потому, что было то, что, казалось, для отсутствия GROUP BY и двух отсутствующих агрегатных функций. MySql - это плохой способ ... лучшая база данных не позволила бы вашему запросу работать до тех пор, пока вы не исправили эти вещи.

+0

Есть предложение where на m.username поэтому индекс там поможет. – nicomp

+1

Работал над этим. Форматирование, сделанное для оценки некоторых других таблиц, занимает больше времени. –

+0

@JoelCoehoorn Спасибо за ваши усилия. К сожалению, он возвращает пустое. Я знаю, что писать запрос без схемы сложно. Если вы спросите, я могу дать вам вывод моего запроса. Надеюсь, что это поможет. Я уже пробовал несколько других вариантов вашего запроса. –

Смежные вопросы