Кэш/Повторное использование подзапроса в 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

Он будет работать намного быстрее.