У меня есть запрос, который медленный... Я хочу отображать последние 12 новых членов рядом со мной (около зарегистрированного пользователя), а база данных моего разработчика - 150 тыс. Строк.
Это заняло более 1 секунды, и запрос объяснения говорит мне, что 30k строк отфильтровываются. Так что 30k фильтруется для 150k строк в моей базе разработки DB... мой сервер в сети намного больше, чем это....
Здесь мой запрос:
SELECT profils.*,
Users.username,
( SELECT count(*)
from profilsphotos pp
where pp.iduser=Profils.iduser
) as nbpics,
ATAN2(SQRT(POW(COS(RADIANS(50.78961000)) * SIN(RADIANS(Y(gm_coor) - 4.64956000)),
2) + POW(COS(RADIANS(X(gm_coor))) * SIN(RADIANS(50.78961000)) - SIN(RADIANS(X(gm_coor))) * COS(RADIANS(50.78961000)) * COS(RADIANS(Y(gm_coor) - 4.64956000)),
2)), (SIN(RADIANS(X(gm_coor))) * SIN(RADIANS(50.78961000)) + COS(RADIANS(X(gm_coor))) * COS(RADIANS(50.78961000)) * COS(RADIANS(Y(gm_coor) - 4.64956000)))
) * 6372.795 AS distance
from Users
inner join Profils ON Users.id=Profils.iduser
where Profils.Actif=1
and profils.idsexe=2
and profils.idlookingfor=1
and Profils.iduser<>1
HAVING distance<400
order by Users.id desc, distance asc
limit 12
Обратите внимание, что я добавляю индекс на эти четыре поля: actif, idsexe, idlookingfor и iduser
Что случилось с моим запросом?
Большое спасибо !
паскаль
Я извлечу подзапрос из предложения SELECT во временную таблицу, проиндексирую его и присоединяюсь к нему вместо того, чтобы выполнять его для каждой записи в предложении select (30K раз).
Итак, шаги: создать временную таблицу, индексировать ее, запустить оптимизированный запрос.
Сначала создайте соответствующие индексы для запроса:
ALTER TABLE
'Profils'
ADD
INDEX 'profils_idx_actif_iduser' ('Actif', 'iduser');
ALTER TABLE
'Users'
ADD
INDEX 'users_idx_id_username' ('id', 'username');
ALTER TABLE
'profils'
ADD
INDEX 'profils_idx_idsexe_idlookingfor' ('idsexe', 'idlookingfor');
ALTER TABLE
'profilsphotos'
ADD
INDEX 'profilsphotos_idx_iduser' ('iduser');
Теперь создайте таблицу temp и проиндексируйте ее:
-- Transformed subquery to a temp table to improve performance
CREATE TEMPORARY TABLE IF NOT EXISTS temp1 AS SELECT
count(*) AS nbpics,
iduser
FROM
profilsphotos pp
WHERE
1 = 1
GROUP BY
iduser
ORDER BY
NULL;
ALTER TABLE
'temp1'
ADD
INDEX 'temp1_idx_iduser_nbpics' ('iduser', 'nbpics');
Теперь попробуйте запустить этот запрос вместо исходного и посмотреть, работает ли он быстрее:
SELECT
optimizedSub1.*,
temp1.nbpics
FROM
(SELECT
Users.username,
ATAN2(SQRT(POW(COS(RADIANS(50.78961000)) * SIN(RADIANS(Y(Profils.gm_coor) - 4.64956000)),
2) + POW(COS(RADIANS(X(Profils.gm_coor))) * SIN(RADIANS(50.78961000)) - SIN(RADIANS(X(Profils.gm_coor))) * COS(RADIANS(50.78961000)) * COS(RADIANS(Y(Profils.gm_coor) - 4.64956000)),
2)),
(SIN(RADIANS(X(Profils.gm_coor))) * SIN(RADIANS(50.78961000)) + COS(RADIANS(X(Profils.gm_coor))) * COS(RADIANS(50.78961000)) * COS(RADIANS(Y(Profils.gm_coor) - 4.64956000)))) * 6372.795 AS distance
FROM
Users
INNER JOIN
Profils
ON Users.id = Profils.iduser
WHERE
Profils.Actif = 1
AND profils.idsexe = 2
AND profils.idlookingfor = 1
AND Profils.iduser <> 1
HAVING
distance < 400
ORDER BY
Users.id DESC,
distance ASC LIMIT 12) AS optimizedSub1
LEFT JOIN
temp1
ON temp1.iduser = optimizedSub1.iduser
Profils
необходимо
INDEX(Actif, idsexe, idlookingfor) -- in any order
Возможно, distance
должно быть первым?..
order by Users.id desc, distance asc
Что такое Y(gm_coor)
? Если это хранимая функция, нам нужно знать больше. В какой таблице есть gm_coor
? После этого, возможно, мы можем обсуждать "ограничительную рамку" как частичное ускорение.
Сделайте еще одно вложение SELECTs
и переместите вычисления nbpics
на него. В настоящее время COUNT(*)
выполняется 30K раз. После изменения это будет всего 12 раз.
переформулирование
SELECT p2.*,
u.username,
( SELECT COUNT(*)
FROM profilsphotos pp
where pp.iduser = p2.iduser
) as nbpics,
x.distance
FROM
( SELECT p1.id, -- assuming this the PK of Profils
(...) AS distance
FROM Profils AS p1
WHERE p1.Actif=1
and p1.idsexe=2
and p1.idlookingfor=1
and p1.iduser<>1
HAVING distance < 400
ORDER BY distance
LIMIT 12
) AS x
JOIN profils AS p2 USING(id)
JOIN Users AS u ON u.id = p2.iduser;