Оптимизируем WHERE IN подзапросы

13th Май 2011 | Категории: MySQL | Метки: ,

На днях наткнулся на очередной шедевр программирования. Что бы не затягивать вступление, приведу сам запрос:

Есть внутренняя БД, в которой накапливается информация о клиентах компании (личные данные, признаки клиента, история общения). Таблица contacts — хранит сами сообщения, таблица contacts_category_links — хранит признаки общения (по какому вопросу обратились, и статус разговора). Программист этим запросом хотел вытащить все признаки общения с человеком #18813.

SELECT * FROM  `contacts_category_links` 
WHERE  `id_contacts` 
IN ( 
	SELECT id FROM  `contacts` WHERE  `id_peoples` = 18813
)

По id_contacts и id_peoples есть индекс. Ни чего странного в запросе не находите?

А если покажу EXPLAIN запроса?

EXPLAIN SELECT * FROM  `contacts_category_links` 
WHERE  `id_contacts` 
IN ( 
	SELECT id FROM  `contacts` WHERE  `id_peoples` = 18813
);
 
+----+--------------------+-------------------------+-----------------+--------------------+---------+---------+------+--------+-------------+
| id | select_type        | table                   | type            | possible_keys      | key     | key_len | ref  | rows   | Extra       |
+----+--------------------+-------------------------+-----------------+--------------------+---------+---------+------+--------+-------------+
|  1 | PRIMARY            | contacts_category_links | ALL             | NULL               | NULL    | NULL    | NULL | 547759 | Using where |
|  2 | DEPENDENT SUBQUERY | contacts                | unique_subquery | PRIMARY,id_peoples | PRIMARY | 4       | func |      1 | Using where |
+----+--------------------+-------------------------+-----------------+--------------------+---------+---------+------+--------+-------------+

Полный скан таблицы! Нужно срочно переписать!

Исходя из задачи, нам нужно объедить 2 таблицы — значит прямая дорога к использованию JOIN.

EXPLAIN SELECT CL.* 
FROM  `contacts` AS C
JOIN contacts_category_links AS CL ON ( C.id = CL.id_contacts ) 
WHERE C.id_peoples =18813;
 
+----+-------------+-------+------+---------------------------+-------------+---------+--------------+------+-------------+
| id | select_type | table | type | possible_keys             | key         | key_len | ref          | rows | Extra       |
+----+-------------+-------+------+---------------------------+-------------+---------+--------------+------+-------------+
|  1 | SIMPLE      | C     | ref  | PRIMARY,id_peoples        | id_peoples  | 5       | const        |   13 | Using where |
|  1 | SIMPLE      | CL    | ref  | id_contacts,id_contacts_2 | id_contacts | 5       | mba_new.C.id |    1 | Using where |
+----+-------------+-------+------+---------------------------+-------------+---------+--------------+------+-------------+

Не оптимизированный вариант выполняется в 3500 раз медленней:
10 rows in set (3.14 sec)
против
10 rows in set (0.00 sec)

Удачной оптимизации!

Subscribe without commenting


Пока комментариев нет.