Кэшировать / повторно использовать подзапрос в MySQL - PullRequest
13 голосов
/ 18 марта 2009

У меня очень сложный запрос 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;

Ответы [ 2 ]

8 голосов
/ 18 марта 2009

Посмотрите, что говорит EXPLAIN EXTENDED.

Если написано DEPENDENT SUBQUERY или UNCACHEABLE SUBQUERY, то оно будет переоцениваться при каждом использовании.

Это происходит, если подзапрос использует переменные сеанса или является коррелированным подзапросом.

Если этого не произойдет, скорее всего, он будет кэширован.

Если ваш случай, то подзапрос не будет кэширован, он будет переоценен в каждом UNION 'редакторе.

Ваш подзапрос, тем не менее, кажется слишком сложным. Почему бы вам просто не использовать:

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), этот запрос должен работать как шарм.

Не могли бы вы сказать мне еще две вещи:

  1. Сколько строк в 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'

и повторите это для всех трех таблиц.

0 голосов
/ 09 марта 2012

По какой-то причине предложения mysql IN с дополнительным выбором выполняются очень медленно. Лучше использовать join. Ваш подзапрос становится:

ВЫБЕРИТЕ id из программ P1 ВНУТРЕННИЕ ПРИСОЕДИНЯТЬСЯ ПРОГРАММЫ 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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...