Решение проблем с индексированием сложных запросов через UNION выборки с алиасом

Бывает так, что условие WHERE становится неадекватным настолько, что расставить индексы для быстрого запроса не представляется возможным. Либо не подходит изначальный контекст запроса. В таком случае можно разбить запросы на 2 и более с лимитами, затем объединить эти результаты при помощи UNION, обязательно присвоив алиас и сделать запрос уже к ним. Mysql автоматом создаст небольшую временную таблицу при выборке из UNION алиаса, но это будет гораздо быстрее, чем не индексируемый запрос. Например:

SELECT
    *
FROM
    (
        (
        SELECT
            `questions`.`id`,
            `questions`.`category_id`,
            `questions`.`text`,
            `questions`.`url_0tvet`,
            `questions`.`date_created`,
            `questions`.`views`,
            `questions`.`views_app`,
            CONCAT(
                '/',
                `categories`.`url`,
                '/',
                `questions`.`url_0tvet`
            ) AS `question_url`,
            `categories`.`url`,
            `categories`.`title`,
            `categories`.`image`
        FROM
            `questions`
        LEFT JOIN `categories` ON `categories`.`id` = `questions`.`category_id`
        LEFT JOIN `answers` ON `answers`.`question_id` = `questions`.`id`
        WHERE
            `questions`.`id` NOT IN(4142706, 8224319) AND `questions`.`category_id` IN(39, 38) AND `answers`.`id` IS NULL AND `section` = 0
        ORDER BY
            `views`
        DESC
    LIMIT 1000
    )
UNION
    (
    SELECT
        `questions`.`id`,
        `questions`.`category_id`,
        `questions`.`text`,
        `questions`.`url_0tvet`,
        `questions`.`date_created`,
        `questions`.`views`,
        `questions`.`views_app`,
        CONCAT(
            '/',
            `categories`.`url`,
            '/',
            `questions`.`url_0tvet`
        ) AS `question_url`,
        `categories`.`url`,
        `categories`.`title`,
        `categories`.`image`
    FROM
        `questions`
    LEFT JOIN `categories` ON `categories`.`id` = `questions`.`category_id`
    WHERE
        `questions`.`id` NOT IN(4142706, 8224319) AND `questions`.`category_id` IN(39, 38) AND(
            `section` = 6 OR(
                `section` = 0 AND `questions`.`confirm` = 8
            )
        )
    ORDER BY
        `views`
    DESC
LIMIT 1000
)
    ) AS `t`
ORDER BY
    `views`
DESC
LIMIT 1000

в первом запросе, необходима выборка из таблицы answers, а вернее её отсутствие(IS NULL), а во втором это не требуется, т.е., если мы втулим всё в один запрос, то строки из answers создадут дубли в выборке, во избежание придётся использовать DISTINCT и условия, расставить по которым индексы уже не получается. Как решение:

SELECT * FROM
(
   (SELECT * FROM `table` WHERE... LIMIT ...)
    UNION
    (SELECT * FROM `table` WHERE... LIMIT ...)
) AS `alias` ORDER BY `field` LIMIT ...

 

Оставить ответ

Ваш адрес email не будет опубликован. Обязательные поля помечены *