Кэш/Повторное использование подзапроса в MySQL
У меня очень сложный запрос MySQL, который включает использование одного и того же подзапроса три раза. Будет ли MySQL фактически запускать подзапрос три раза? (Это дорогостоящий.) Если да, то есть ли способ сообщить мне MySQL сохранить или кешировать результаты, чтобы он этого не сделал? Я мог бы сохранить данные в большом массиве, а затем перенаправить его в MySQL, но я бы предпочел не перемещать его и обратно в базу данных.
Это подзапрос, который появляется три раза:
SELECT id FROM programs
WHERE submitter_id=32 AND id in (
SELECT id FROM programs
WHERE feed_id=2478 AND id in (
SELECT program_id FROM playlist_program_map
WHERE playlist_id=181)))
И вот пример полного запроса, в котором появляется запрос:
SELECT object_id, programs.created AS created,
MATCH(text) AGAINST ('excellent ' IN BOOLEAN MODE) AS relevance
FROM comments_programs USE INDEX (text)
LEFT JOIN programs ON programs.id=object_id
WHERE object_id IN (
SELECT id FROM programs
WHERE 1 AND id IN (
SELECT id FROM programs
WHERE submitter_id=32 AND id in (
SELECT id FROM programs
WHERE feed_id=2478 AND id in (
SELECT program_id FROM playlist_program_map
WHERE playlist_id=181))))
AND MATCH(text) AGAINST ('excellent ' IN BOOLEAN MODE)>0)
UNION (
SELECT object_id, programs.created AS created,
MATCH(text) AGAINST ('excellent ' IN BOOLEAN MODE) AS relevance
FROM descriptions_programs USE INDEX (text)
LEFT JOIN programs ON programs.id=object_id
WHERE object_id IN (
SELECT id FROM programs
WHERE 1 AND id IN (
SELECT id FROM programs
WHERE submitter_id=32 AND id in (
SELECT id FROM programs
WHERE feed_id=2478 AND id in (
SELECT program_id FROM playlist_program_map
WHERE playlist_id=181))))
AND MATCH(text) AGAINST ('excellent ' IN BOOLEAN MODE)>0 AND current=1 )
UNION (
SELECT object_id, programs.created AS created,
MATCH(text) AGAINST ('excellent ' IN BOOLEAN MODE) AS relevance
FROM titles_programs USE INDEX (text)
LEFT JOIN programs ON programs.id=object_id
WHERE object_id IN (
SELECT id FROM programs
WHERE 1 AND id IN (
SELECT id FROM programs
WHERE submitter_id=32 AND id in (
SELECT id FROM programs
WHERE feed_id=2478 AND id in (
SELECT program_id FROM playlist_program_map
WHERE playlist_id=181))))
AND MATCH(text) AGAINST ('excellent ' IN BOOLEAN MODE)>0 AND current=1;
Ответы
Ответ 1
Посмотрите, что говорит EXPLAIN EXTENDED
.
Если он говорит DEPENDENT SUBQUERY
или UNCACHEABLE SUBQUERY
, то он будет пересматриваться каждый раз, когда он будет использоваться.
Это происходит, если подзапрос использует переменные сеанса или является коррелированным подзапросом.
Если это не так, скорее всего, будет кэшировано.
Если в вашем случае подзапрос не будет кэшироваться, он будет переоценен в каждом наборе UNION
'ed.
Вы, однако, подзапрос, кажется, слишком сложны. Почему бы вам просто не использовать:
SELECT id
FROM playlist_program_map ppm, programs p
WHERE ppm.playlist_id = 181
AND p.id = ppm.program_id
AND submitter_id = 32
AND feed_id = 2478
Если у вас есть индекс на playlist_program_map (playlist_id)
, этот запрос должен работать как шарм.
Не могли бы вы рассказать мне еще две вещи:
- Сколько строк в
playlist_program_map
и сколько там значений DISTINCT playlist_id
?
- Сколько строк в
programs
и сколько пар DISTINCT submitter_id, feed_id
есть?
Из вашего комментария я могу заключить, что в среднем 10 programs
за playlist
и 200 programs
за пару (submitter, feed)
. Это означает, что ваш индекс на playlist_program_map
более избирателен, чем тот, который находится на (submitter, feed)
, а playlist_program_map
должен быть лидером в соединении.
Полнотекстовый индекс в вашем случае также не очень избирателен, учитывая, что вам нужно присоединиться к 10 программам из 2 000 000.
Вы можете попробовать следующее:
SELECT object_id, programs.created AS created
FROM playlist_program_map ppm, programs p, comments_programs cp
WHERE ppm.playlist_id = 181
AND p.id = ppm.program_id
AND p.submitter_id = 32
AND p.feed_id = 2478
AND cp.object_id = p.id
AND cp.text REGEXP 'excellent'
и повторите это для всех трех таблиц.
Ответ 2
По какой-то причине предложения mysql IN с пробором подвыбора очень медленные. Лучше использовать соединение. Ваш подзапрос будет выглядеть следующим образом:
SELECT id из программ P1 INNER JOIN программы P2 ON P1.id = P2.id INNER JOIN playlist_program_map PMAP ON P2.id = PMAP.program_id WHERE P1.submitter_id = 32 И P2.feed_id = 2478 И PMAP.playlist_id = 181
Он будет работать намного быстрее.