Оптимизируем WHERE IN подзапросы
На днях наткнулся на очередной шедевр программирования. Что бы не затягивать вступление, приведу сам запрос:
Есть внутренняя БД, в которой накапливается информация о клиентах компании (личные данные, признаки клиента, история общения). Таблица 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)
Удачной оптимизации!