Оптимизация запросов (несколько соединений) - PullRequest
1 голос
/ 14 января 2020

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

Ниже приведен мой запрос:

SELECT 
    `u`.`id` AS `id`,
    `p`.`lastname` AS `lastname`,
    `p`.`firstname` AS `firstname`,
    COALESCE(`r`.`value`, 0) AS `rvalue`,
    SUM(`rat`.`category` = 'A') AS `count_a`,
    SUM(`rat`.`category` = 'B') AS `count_b`,
    SUM(`rat`.`category` = 'C') AS `count_c`
FROM
    `user` `u`
    JOIN `user_customer` `uc` ON (`u`.`id` = `uc`.`user_id`)
    JOIN `profile` `p` ON (`p`.`id` = `u`.`profile_id`)
    JOIN `ad` FORCE INDEX (fk_ad_customer_idx) ON (`uc`.`customer_id` = `ad`.`customer_id`)
    JOIN `ac` ON (`ac`.`id` = `ad`.`ac_id`)
    JOIN `a` ON (`a`.`id` = `ac`.`a_id`)
    JOIN `rat` ON (`rat`.`code` = `a`.`rat_code`)
    LEFT JOIN `r` ON (`r`.`id` = `u`.`r_id`)
GROUP BY `u`.`id`
;

Примечание: некоторые имена таблиц и столбцов добровольно скрыты.

Теперь позвольте мне дать вам некоторые объемные c данные:

user => 6534 rows
user_customer => 12 923 rows
profile => 6511 rows
ad => 320 868 rows
ac => 4505 rows
a => 536 rows
rat => 6 rows
r => 3400 rows

И, наконец, мой план выполнения:

enter image description here

Мой запрос в настоящее время выполняется примерно от 1,3 до 1,7 секунды, что достаточно медленно, чтобы раздражать пользователей моего приложения, конечно ... Также набор результатов для вас состоит из 165 строк.

Есть ли способ, которым я могу улучшить это?

Спасибо.

РЕДАКТИРОВАТЬ 1 ( ответ Рику Джеймсу ниже): Какова скорость и ОБЪЯСНИТЬ, когда вы не используете FORCE INDEX?

Удивительно, но становится быстрее, когда я не использую FORCE INDEX. Честно говоря, я действительно не помню, почему я сделал это изменение. Я, вероятно, нашел лучшие результаты с точки зрения производительности с ним во время одной из моих различных попыток и не удалял его с тех пор.

Когда я не использую FORCE INDEX, он использует другой индекс ad_customer_ac_id_blocked_idx (customer_id , ac_id, заблокирован) и время составляет около 1.1 se c. Я не совсем понимаю, потому что fk_ad_customer_idx (customer_id) одинаков, когда мы говорим об индексе customer_id.

Ответы [ 2 ]

6 голосов
/ 17 января 2020

Избавьтесь от FORCE INDEX. Даже если это помогло вчера; завтра может быть больно.

Некоторые из этих показателей могут быть полезными. (Сложно предсказать, поэтому просто добавьте их все.)

a:  (rat_code, id)
rat:  (code, category)
ac:  (a_id, id)
ad:  (ac_id, customer_id)
ad:  (customer_id, ac_id)
uc:  (customer_id, user_id)
uc:  (user_id, customer_id)
u:  (profile_id, r_id, id)

(Предполагается, что id - это PRIMARY KEY каждой таблицы. Обратите внимание, что ни у одной нет id в первую очередь.) Большинство из вышеперечисленного есть «покрытие».

Другой подход, который иногда помогает: соберите SUMs перед тем, как присоединиться к любой ненужной таблице. Но похоже, что p является единственной таблицей, не участвующей в переходе от u (цель GROUP BY) к r и rat (используется в агрегатах). Это будет выглядеть примерно так:

SELECT ..., firstname, lastname
    FROM ( everything as above except for `p` ) AS most
    JOIN `profile` `p`  ON (`p`.`id` = most.`profile_id`)
    GROUP BY most.id

Это позволяет избежать смещения имени и фамилии при выполнении большинства объединений и GROUP BY.

При выполнении JOINs и GROUP BY, Обязательно проверяйте работоспособность агрегатов. Ваши COUNTs и SUMs могут быть больше, чем они должны быть.

1 голос
/ 16 января 2020

Во-первых, вам не нужно tick. everyTableAndColumn в ваших запросах, а также столбцы результатов, псевдонимы и т. Д. c. Метки tick используются главным образом, когда вы находитесь в конфликте с зарезервированной работой, поэтому анализатор знает, что вы ссылаетесь на указанный столбец c ... например, если у вас есть таблица с COLUMN с именем «JOIN», но JOIN является частью of SQL command ... посмотрите, какое замешательство это может вызвать. Помогает и в удобстве чтения.

Далее, это всего лишь личные предпочтения, которые могут помочь вам и другим следить за вашими данными и их связями. Я показываю объединение с отступом от того, откуда оно пришло. Как вы можете видеть ниже, я вижу цепочку того, как мне добраться от пользователя (u alias) до таблицы псевдонимов крысы ... Вы добираетесь туда только, пройдя 5 уровней глубиной, и я положил первую таблицу слева. сторона соединения (исходя из таблицы), то = таблица, соединяющая TO с правой стороны соединения.

Теперь, когда я могу видеть отношения, я бы предложил следующее. Создайте в своих таблицах индексы COVERING, которые имеют критерии и, при необходимости, id / значение. Таким образом, запрос получает все, что ему нужно - данные со страницы индекса по сравнению с go для необработанных данных. Итак, вот предложения для индексов.

table             index
user_customer     ( user_id, customer_id )   -- dont know what your fk_ad_customer_idx parts are)
ad                ( customer_id, ac_id )
ac                ( id, a_id )
a                 (id, rat_code )
rat               ( code, category )

Переформатированный запрос для удобства чтения и просмотра связей между таблицами

SELECT 
        u.id,
        p.lastname,
        p.firstname,
        COALESCE(r.value, 0) AS rvalue,
        SUM(rat.category = 'A') AS count_a,
        SUM(rat.category = 'B') AS count_b,
        SUM(rat.category = 'C') AS count_c
    FROM
        user u
            JOIN user_customer uc
                ON u.id = uc.user_id
                JOIN ad FORCE INDEX (fk_ad_customer_idx) 
                    ON uc.customer_id = ad.customer_id
                    JOIN ac 
                        ON ad.ac_id = ac.id
                        JOIN a 
                            ON ac.a_id = a.id
                            JOIN rat 
                                ON a.rat_code = rat.code
            JOIN profile p
                ON u.profile_id = p.id
            LEFT JOIN r
                ON u.r_id = r.id
    GROUP BY 
        u.id
...