Очень медленно работает выборка INNER JOIN - MySQL

279
22 февраля 2017, 21:21

У меня есть таблица компании, таблица недвижимости и таблица с параметрами недвижимости. При выборе недвижимости, например квартиры на съем, пользователь может указать параметры по которым он ее ищет. Есть два типа параметров, select и radio, т.е. что-то выбирается из списка select, а что-то путем нажатия на radio. Чтобы таблицы были не переполнены, я просто разнес данные по двум таблицам в зависимости от типа параметра. Вот так выглядит мой селект.

SELECT names.name_ru,
real_estate.price_real_estate, 
names.uniq_id, 
names.name_en, 
real_estate.id_real_estate
FROM (company_names as names, company_real_estate as real_estate) 
INNER JOIN real_estate_properties_select as p_select2 on real_estate.id_real_estate = p_select2.id_real_estate 
INNER JOIN real_estate_properties_select as p_select4 on real_estate.id_real_estate = p_select4.id_real_estate 
INNER JOIN real_estate_properties_select as p_select6 on real_estate.id_real_estate = p_select6.id_real_estate 
INNER JOIN real_estate_properties_select as p_select7 on real_estate.id_real_estate = p_select7.id_real_estate 
INNER JOIN real_estate_properties_radio as p_radio0 on real_estate.id_real_estate = p_radio0.id_real_estate 
INNER JOIN real_estate_properties_radio as p_radio1 on real_estate.id_real_estate = p_radio1.id_real_estate 
INNER JOIN real_estate_properties_radio as p_radio3 on real_estate.id_real_estate = p_radio3.id_real_estate 
INNER JOIN real_estate_properties_radio as p_radio5 on real_estate.id_real_estate = p_radio5.id_real_estate 
INNER JOIN real_estate_properties_radio as p_radio8 on real_estate.id_real_estate = p_radio8.id_real_estate 
INNER JOIN real_estate_properties_radio as p_radio11 on real_estate.id_real_estate = p_radio11.id_real_estate 
INNER JOIN real_estate_properties_radio as p_radio13 on real_estate.id_real_estate = p_radio13.id_real_estate 
INNER JOIN real_estate_properties_radio as p_radio14 on real_estate.id_real_estate = p_radio14.id_real_estate 
INNER JOIN real_estate_properties_radio as p_radio25 on real_estate.id_real_estate = p_radio25.id_real_estate 
WHERE names.uniq_id = real_estate.uniq_id 
and real_estate.real_estate_type = 1 
and real_estate.sale_rent = 0 
and p_select2.id_propertie = 3 
and p_select2.select_value = 2 
and p_select4.id_propertie = 5 
and p_select4.select_value = 2 
and p_select6.id_propertie = 7 
and p_select6.select_value = 1 
and p_select7.id_propertie = 8 
and p_select7.select_value = 1 
and p_radio0.id_propertie = 1 
and p_radio0.radio_value = 0 
and p_radio1.id_propertie = 2 
and p_radio1.radio_value = 1 
and p_radio3.id_propertie = 4 
and p_radio3.radio_value = 1 
and p_radio5.id_propertie = 6 
and p_radio5.radio_value = 1 
and p_radio8.id_propertie = 9 
and p_radio8.radio_value = 1 
and p_radio11.id_propertie = 12 
and p_radio11.radio_value = 1 
and p_radio13.id_propertie = 14 
and p_radio13.radio_value = 1 
and p_radio14.id_propertie = 15 
and p_radio14.radio_value = 1 
and p_radio25.id_propertie = 144 
and p_radio25.radio_value = 1 

Это пример максимальной опции параметров при выборе недвижимости, и так я получаю ответ от сервера TIME OUT, т.е. время на обработку такого селекта превышает допустимое. При меньшем, скажем в половину раз, работает и даже я бы сказал быстро, относительно быстро. Помогите пожалуйста оптимизировать этот огромный селект. Очень прошу, не посылайте меня к NoSQL решениям, на данном этапе я не могу поменять архитектуру.

Answer 1

Думаю можно попробовать что то типа такого:

SELECT names.name_ru,
       real_estate.price_real_estate, 
       names.uniq_id, 
       names.name_en, 
       real_estate.id_real_estate
  FROM company_names as names
 INNER JOIN company_real_estate as real_estate on names.uniq_id = real_estate.uniq_id 
 INNER JOIN (
   select id_real_estate
     from real_estate_properties_select
    where (id_propertie, select_value) IN((3,2),(5,2),(7,1),(8,1))
    group by id_real_estate
   having count(1)=4
 ) p_select on real_estate.id_real_estate = p_select.id_real_estate
 INNER JOIN (
   select id_real_estate
     from real_estate_properties_radio
    where (id_propertie, radio_value) IN((1,0),(2,1),(4,1),(6,1),(9,1),(12,1),(14,1),(15,1),(144,1))
    group by id_real_estate
   having count(1)=9
 ) p_radio on real_estate.id_real_estate = p_radio.id_real_estate
  WHERE real_estate.real_estate_type = 1 
    and real_estate.sale_rent = 0 

В условиях having количество заданных условий. Вообще я бы еще посмотрел, а нужно ли держать отдельные таблицы для select и radio, почему бы не собрать это в одну таблицу и в некой таблице-справочнике (наверняка она у вас уже есть, в ней лежат имена для id_propertie) добавил поле 'тип поля' которое бы и указывало select у вас для выбора или radio.

Плюс к этому надо посмотреть планы выволнения (EXPLAIN) для получающегося запроса и возможно сделать какие то дополнительные индексы, если их не хватает или как то перефразировать запрос.

Answer 2

Проверьте наличие индексов (id_real_estate + id_propertie + select_value) для real_estate_properties_select и (id_real_estate + id_propertie + radio_value) для real_estate_properties_radio.

Вообще нужно избегать формирования отдельных JOIN на каждый чих пользователя (выбор там чего-то и т.п.), но я понимаю что у вас скорее всего ведется динамический список свойств для каждой группы объектов, и затем присвоение свойств объектам, поэтому по другому особо и нет вариантов. Но хотя бы так вот структурируйте:

SELECT names.name_ru,
    real_estate.price_real_estate, 
    names.uniq_id, 
    names.name_en, 
    real_estate.id_real_estate
FROM (company_names as names, company_real_estate as real_estate) 
    INNER JOIN real_estate_properties_select as p_select2 on real_estate.id_real_estate = p_select2.id_real_estate and p_select2.id_propertie = 3 and p_select2.select_value = 2 
    INNER JOIN real_estate_properties_select as p_select4 on real_estate.id_real_estate = p_select4.id_real_estate and p_select4.id_propertie = 5 and p_select4.select_value = 2 
    INNER JOIN real_estate_properties_select as p_select6 on real_estate.id_real_estate = p_select6.id_real_estate and p_select6.id_propertie = 7 and p_select6.select_value = 1 
    INNER JOIN real_estate_properties_select as p_select7 on real_estate.id_real_estate = p_select7.id_real_estate and p_select7.id_propertie = 8 and p_select7.select_value = 1 
    INNER JOIN real_estate_properties_radio as p_radio0 on real_estate.id_real_estate = p_radio0.id_real_estate and p_radio0.id_propertie = 1 and p_radio0.radio_value = 0 
    INNER JOIN real_estate_properties_radio as p_radio1 on real_estate.id_real_estate = p_radio1.id_real_estate and p_radio1.id_propertie = 2 and p_radio1.radio_value = 1 
    INNER JOIN real_estate_properties_radio as p_radio3 on real_estate.id_real_estate = p_radio3.id_real_estate and p_radio3.id_propertie = 4 and p_radio3.radio_value = 1 
    INNER JOIN real_estate_properties_radio as p_radio5 on real_estate.id_real_estate = p_radio5.id_real_estate and p_radio5.id_propertie = 6 and p_radio5.radio_value = 1 
    INNER JOIN real_estate_properties_radio as p_radio8 on real_estate.id_real_estate = p_radio8.id_real_estate and p_radio8.id_propertie = 9 and p_radio8.radio_value = 1 
    INNER JOIN real_estate_properties_radio as p_radio11 on real_estate.id_real_estate = p_radio11.id_real_estate and p_radio11.id_propertie = 12 and p_radio11.radio_value = 1 
    INNER JOIN real_estate_properties_radio as p_radio13 on real_estate.id_real_estate = p_radio13.id_real_estate and p_radio13.id_propertie = 14 and p_radio13.radio_value = 1 
    INNER JOIN real_estate_properties_radio as p_radio14 on real_estate.id_real_estate = p_radio14.id_real_estate and p_radio14.id_propertie = 15 and p_radio14.radio_value = 1 
    INNER JOIN real_estate_properties_radio as p_radio25 on real_estate.id_real_estate = p_radio25.id_real_estate and p_radio25.id_propertie = 144 and p_radio25.radio_value = 1 
WHERE 
    names.uniq_id = real_estate.uniq_id 
    and real_estate.real_estate_type = 1 
    and real_estate.sale_rent = 0 
READ ALSO
Count в значение таблицы mysql

Count в значение таблицы mysql

Интересует вопрос можно ли то таблицу в которой одно из полей будет содержать в себе количество записей из другой таблицы? В access вроде так...

200
Mysql сортировка по кастомному полю

Mysql сортировка по кастомному полю

ЗдравствуйтеЕсть характеристики которые берутся из 3 (трех) таблиц

199
sql сравнение чисел как LIKE строк

sql сравнение чисел как LIKE строк

Имеется таблица вида {id, name}, в которой id может принимать большие значения, например, 17200, 17201, 17202, 17203 и тд

323