Оптимизация MySQL "IN" Выбрать? - PullRequest
1 голос
/ 28 мая 2020

У меня есть следующий MySQL запрос:

SELECT
    `influencers`.*,
    `locations`.`country_name`
FROM
    `influencers`
    LEFT JOIN `locations` ON `influencers`.`country_id` = `locations`.`id`
WHERE
    `is_dead` = 0
    AND `influencers`.`is_private` = 0
    AND `influencers`.`country_id` = '31'
    AND influencers.uuid IN(
        SELECT
            `influencer_uuid` FROM `category_influencer`
        WHERE
            `category_influencer`.`category_id` = 17
            AND `category_influencer`.`is_main` = 1)
ORDER BY
    `influencers`.`followed_by` DESC
LIMIT 7 OFFSET 6

Я обнаружил, что подзапрос IN вызывает задержку около 10 секунд для завершения этого запроса. Вот EXPLAIN :

MySQL Explain

У меня есть индексы для всех запрашиваемых столбцов.

Как я могу значительно ускорить этот запрос?

Обновлено с помощью SHOW CREATE TABLE для обоих:

locations

CREATE TABLE `locations` (
    `id` int(11) NOT NULL AUTO_INCREMENT,
    `country_name` varchar(255) DEFAULT NULL,
    `city_name` varchar(255) DEFAULT NULL,
    `type` varchar(255) DEFAULT NULL,
    PRIMARY KEY (`id`),
    UNIQUE KEY `unique_index` (`city_name`, `country_name`),
    KEY `type` (`type`)
    USING BTREE) ENGINE = InnoDB AUTO_INCREMENT = 6479 DEFAULT CHARSET = utf8mb4

influencers

CREATE TABLE `influencers` (
    `id` int(11) NOT NULL AUTO_INCREMENT,
    `bio` varchar(255) CHARACTER
    SET utf8mb4 DEFAULT NULL,
    `url` varchar(255) CHARACTER
    SET utf8mb4 DEFAULT NULL,
    `followed_by` int(11) DEFAULT NULL,
    `follows` int(11) DEFAULT NULL,
    `full_name` varchar(255) CHARACTER
    SET utf8mb4 NOT NULL,
    `social_id` varchar(255) DEFAULT NULL,
    `is_private` tinyint (1) DEFAULT NULL,
    `avatar` varchar(255) NOT NULL,
    `username` varchar(30) NOT NULL,
    `text_search` text CHARACTER
    SET utf8mb4 NOT NULL,
    `updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
    `uuid` varchar(255) DEFAULT NULL,
    `is_dead` tinyint (4) DEFAULT NULL,
    `country_id` int(11) DEFAULT NULL,
    `city_id` int(11) DEFAULT NULL,
    PRIMARY KEY (`id`),
    UNIQUE KEY `username` (`username`),
    UNIQUE KEY `uuid` (`uuid`),
    KEY `is_dead` (`is_dead`),
    KEY `updated_at` (`updated_at`),
    KEY `followed_by` (`followed_by`),
    KEY `social_id` (`social_id`),
    KEY `is_private` (`is_private`),
    KEY `country_id` (`country_id`),
    FULLTEXT KEY `text_search` (`text_search`)) ENGINE = InnoDB AUTO_INCREMENT = 2278376 DEFAULT CHARSET = utf8

Ответы [ 3 ]

0 голосов
/ 28 мая 2020

Если я что-то не пропустил, вы можете заменить подзапрос на JOIN:

SELECT influencers.*,
       locations.country_name
FROM influencers
         JOIN category_influencer T ON (
            T.influencer_uuid = influencers.uuid
            AND category_id = 17
            AND is_main = 1)
         LEFT JOIN locations ON influencers.country_id = locations.id
WHERE is_dead = 0
  AND is_private = 0
  AND country_id = '31'

ORDER BY followed_by DESC
LIMIT 7 OFFSET 6
0 голосов
/ 29 мая 2020

Что за ОБЪЯСНЕНИЕ ...?

SELECT i.*
     , l.country_name
  FROM influencers i 
  JOIN category_influencer c
    ON c.influencer_id = i.uuid
  LEFT 
  JOIN locations l 
    ON i.country_id = l.id
 WHERE is_dead = 0 -- why isn't this column qualified like all the others
   AND i.is_private = 0
   AND i.country_id = 31
   AND c.category_id = 17
   AND c.is_main = 1
 ORDER 
    BY i.followed_by DESC
 LIMIT 7 
OFFSET 6
0 голосов
/ 28 мая 2020

Вы можете избежать предложения in, используя внутреннее соединение

    SELECT
        `influencers`.*,
        `locations`.`country_name`
    FROM
        `influencers`
    INNER JOIN  (
        SELECT
                `influencer_uuid` FROM `category_influencer`
            WHERE
                `category_id` = 17
                AND `is_main` = 1

    ) T ON T.influencer_uuid = influencers.uuid
        LEFT JOIN `locations` ON `influencers`.`country_id` = `locations`.`id`
    WHERE
        `is_dead` = 0
        AND `is_private` = 0
        AND `country_id` = '31'

    ORDER BY
        `followed_by` DESC
    LIMIT 7 OFFSET 6

Таким образом, вместо итерации для всего результата IN вы используете только одно реляционное соответствие на основе соединения

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