Краткое описание: Это известная проблема в MySQL, исправленная в MySQL 5.6.x.Проблема связана с отсутствующей оптимизацией, когда подзапрос, использующий IN, неправильно идентифицируется как зависимый подзапрос вместо независимого подзапроса.
Когда вы запускаете EXPLAIN для исходного запроса, он возвращает следующее:
1 'PRIMARY' 'question_law_version' 'ALL' '' '' '' '' 10148 'Using where'
2 'DEPENDENT SUBQUERY' 'question_law_version' 'ALL' '' '' '' '' 10148 'Using where'
3 'DEPENDENT SUBQUERY' 'question_law' 'ALL' '' '' '' '' 10040 'Using where'
Когда вы изменяете IN
на =
, вы получаете это:
1 'PRIMARY' 'question_law_version' 'ALL' '' '' '' '' 10148 'Using where'
2 'SUBQUERY' 'question_law_version' 'ALL' '' '' '' '' 10148 'Using where'
3 'SUBQUERY' 'question_law' 'ALL' '' '' '' '' 10040 'Using where'
Каждый зависимый подзапрос запускается один раз для каждой строки в запросе, в котором он содержится, тогда как подзапрос выполняется толькоодин раз.MySQL иногда может оптимизировать зависимые подзапросы, когда есть условие, которое можно преобразовать в соединение, но здесь это не так.
Теперь это, конечно, оставляет вопрос о том, почему MySQL считает, что версия IN должна бытьзависимый подзапрос.Я сделал упрощенную версию запроса, чтобы помочь исследовать это.Я создал две таблицы 'foo' и 'bar', где первая содержит только столбец id, а вторая содержит и id, и идентификатор foo (хотя я не создала ограничение внешнего ключа).Затем я заполнил обе таблицы 1000 строками:
CREATE TABLE foo (id INT PRIMARY KEY NOT NULL);
CREATE TABLE bar (id INT PRIMARY KEY, foo_id INT NOT NULL);
-- populate tables with 1000 rows in each
SELECT id
FROM foo
WHERE id IN
(
SELECT MAX(foo_id)
FROM bar
);
Этот упрощенный запрос имеет ту же проблему, что и раньше - внутренний выбор обрабатывается как зависимый подзапрос, и оптимизация не выполняется, в результате чего внутренний запрос выполняетсяодин раз за рядВыполнение запроса занимает почти одну секунду.Повторное изменение IN
на =
позволяет выполнить запрос почти мгновенно.
Код, который я использовал для заполнения таблиц, приведен ниже, на случай, если кто-нибудь захочет воспроизвести результаты.
CREATE TABLE filler (
id INT NOT NULL PRIMARY KEY AUTO_INCREMENT
) ENGINE=Memory;
DELIMITER $$
CREATE PROCEDURE prc_filler(cnt INT)
BEGIN
DECLARE _cnt INT;
SET _cnt = 1;
WHILE _cnt <= cnt DO
INSERT
INTO filler
SELECT _cnt;
SET _cnt = _cnt + 1;
END WHILE;
END
$$
DELIMITER ;
CALL prc_filler(1000);
INSERT foo SELECT id FROM filler;
INSERT bar SELECT id, id FROM filler;