Mysql запрос оптимизации с левыми соединениями - PullRequest
0 голосов
/ 01 октября 2018

У меня следующий запрос, и этот запрос занимает 1,141 сек.Этот запрос имеет кучу объединений.Есть ли способ оптимизировать запрос?Любая помощь приветствуется.

SELECT `cou`.`id` AS `country_id`,
                `a`.`id` AS `area_id`,
                `y`.`id` AS `year_id`,
                `su`.`id` AS `subject_id`,
                `co1`.`name` AS `course_name`,
                `ca1`.`id` AS `root_category_id`,
                `ca1`.`name` AS `root_category_name`,
                `ca4`.`id` AS `chapter_id`,
                `ca4`.`name` AS `chapter_name`,
                `ca4`.`no_of_assets` AS `no_of_assets`,
                `ca4`.`active_status` AS `status`,
                0 AS `READ_IT`,
                0 AS `WATCH_IT`,
                0 AS `PLAY_IT`,
                0 AS `PROVE_IT`,
                count(DISTINCT `pa`.`id`) AS `APROVE_IT`,
                if((count(`pa`.`id`) > 0),'True', 'False') AS `sections_with_content`,
                count(`pa`.`id`) AS `content_count`,
                `pa`.`status` AS `content_flag`
         FROM (((((((((((((((((`edu_db`.`category_relation_xref` `crx1`
                               JOIN `edu_db`.`category` `ca1` on((`crx1`.`parent_id` = `ca1`.`id`)))
                              LEFT JOIN `edu_db`.`course` `co1` on((`ca1`.`course_id` = `co1`.`id`)))
                             JOIN `edu_db`.`category_relation_xref` `crx2` on((`crx1`.`child_id` = `crx2`.`parent_id`)))
                            JOIN `edu_db`.`category` `ca2` on((`crx2`.`parent_id` = `ca2`.`id`)))
                           JOIN `edu_db`.`category` `ca3` on((`crx2`.`child_id` = `ca3`.`id`)))
                          JOIN `edu_db`.`category_relation_xref` `crx3` on((`crx2`.`child_id` = `crx3`.`parent_id`)))
                         JOIN `edu_db`.`category` `ca4` on((`crx3`.`child_id` = `ca4`.`id`)))
                        LEFT JOIN `edu_db`.`category_relation_xref` `crx4` on((`crx3`.`child_id` = `crx4`.`parent_id`)))
                       LEFT JOIN `edu_db`.`category` `ca5` on((`crx4`.`child_id` = `ca5`.`id`)))
                      JOIN `edu_db`.`course` `co2` on((`ca4`.`course_id` = `co2`.`id`)))
                     JOIN `edu_db`.`curriculum` `cu` on((`co2`.`curriculum_id` = `cu`.`id`)))
                    JOIN `edu_db`.`year` `y` on((`cu`.`year_id` = `y`.`id`)))
                   JOIN `edu_db`.`subject` `su` on((`su`.`id` = `cu`.`subject_id`)))
                  JOIN `edu_db`.`area` `a` on((`y`.`area_id` = `a`.`id`)))
                 JOIN `edu_db`.`country` `cou` on((`a`.`country_id` = `cou`.`id`)))
                LEFT JOIN `edu_db`.`qbnk_category_published_assessment_xref` `qcpa` on((`ca4`.`id` = `qcpa`.`category_id`)))
               LEFT JOIN `edu_db`.`qbnk_published_assessment` `pa` on((`qcpa`.`published_assessment_id` = `pa`.`id`)))
         WHERE ((`pa`.`status` <> 'non_active')
                AND (`qcpa`.`status` <> 'deleted'))
         GROUP BY `ca4`.`id`

Это результат команды объяснения.Здесь есть тип выбора, который использует файловую сортировку, что означает, что запрос не использует индекс.Есть ли способ оптимизировать этот запрос с помощью индексов?

 +------+---------------+---------+--------------+----------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+----------------------------------+-----------+--------------------------------+--------+------------+-------------------------------------------------------------+
| "id" | "select_type" | "table" | "partitions" |  "type"  |                                                                                      "possible_keys"                                                                                      |              "key"               | "key_len" |             "ref"              | "rows" | "filtered" |                           "Extra"                           |
+------+---------------+---------+--------------+----------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+----------------------------------+-----------+--------------------------------+--------+------------+-------------------------------------------------------------+
| "1"  | "SIMPLE"      | "pa"    | \N           | "index"  | "PRIMARY,status"                                                                                                                                                                          | "status"                         | "2"       | \N                             | "7714" | "50.00"    | "Using where; Using index; Using temporary; Using filesort" |
| "1"  | "SIMPLE"      | "qcpa"  | \N           | "ref"    | "PRIMARY,FK_qbnk_cat_id_pub_ass_tbl_to_category_tbl_id,cat_pub_status"                                                                                                                    | "PRIMARY"                        | "4"       | "edu_db.pa.id"             | "6"    | "50.00"    | "Using where"                                               |
| "1"  | "SIMPLE"      | "ca4"   | \N           | "eq_ref" | "PRIMARY,FK_curriculum_item_id_category_tbl_to_id_curriculum_item_tbl,FK_curriculum_id_category_tbl_to_id_curriculum_tbl,name,Fk_category_tbl_course_id_to_course_tbl_id,Index 6,Index 7" | "PRIMARY"                        | "4"       | "edu_db.qcpa.category_id"  | "1"    | "100.00"   | "Using where"                                               |
| "1"  | "SIMPLE"      | "co2"   | \N           | "eq_ref" | "PRIMARY,FK_curriculum_id_to_id_curriculum_tbl"                                                                                                                                           | "PRIMARY"                        | "4"       | "edu_db.ca4.course_id"     | "1"    | "100.00"   | "Using where"                                               |
| "1"  | "SIMPLE"      | "cu"    | \N           | "eq_ref" | "PRIMARY,FK_subject_id_to_id_subject_tbl,FK_year_id_to_id_year_tble"                                                                                                                      | "PRIMARY"                        | "4"       | "edu_db.co2.curriculum_id" | "1"    | "100.00"   | "Using where"                                               |
| "1"  | "SIMPLE"      | "su"    | \N           | "eq_ref" | "PRIMARY"                                                                                                                                                                                 | "PRIMARY"                        | "4"       | "edu_db.cu.subject_id"     | "1"    | "100.00"   | "Using index"                                               |
| "1"  | "SIMPLE"      | "y"     | \N           | "eq_ref" | "PRIMARY,FK_year_tbl_area_id_to_id_area_tbl"                                                                                                                                              | "PRIMARY"                        | "4"       | "edu_db.cu.year_id"        | "1"    | "100.00"   | "Using where"                                               |
| "1"  | "SIMPLE"      | "a"     | \N           | "eq_ref" | "PRIMARY,FK_country_id_to_country_tbl"                                                                                                                                                    | "PRIMARY"                        | "4"       | "edu_db.y.area_id"         | "1"    | "100.00"   | \N                                                          |
| "1"  | "SIMPLE"      | "cou"   | \N           | "eq_ref" | "PRIMARY"                                                                                                                                                                                 | "PRIMARY"                        | "4"       | "edu_db.a.country_id"      | "1"    | "100.00"   | "Using index"                                               |
| "1"  | "SIMPLE"      | "crx3"  | \N           | "ref"    | "PRIMARY,FK_child_id_to_id_category_tbl"                                                                                                                                                  | "FK_child_id_to_id_category_tbl" | "4"       | "edu_db.qcpa.category_id"  | "1"    | "100.00"   | "Using index"                                               |
| "1"  | "SIMPLE"      | "ca3"   | \N           | "eq_ref" | "PRIMARY,Index 6,Index 7"                                                                                                                                                                 | "PRIMARY"                        | "4"       | "edu_db.crx3.parent_id"    | "1"    | "100.00"   | "Using index"                                               |
| "1"  | "SIMPLE"      | "crx2"  | \N           | "ref"    | "PRIMARY,FK_child_id_to_id_category_tbl"                                                                                                                                                  | "FK_child_id_to_id_category_tbl" | "4"       | "edu_db.crx3.parent_id"    | "1"    | "100.00"   | "Using index"                                               |
| "1"  | "SIMPLE"      | "ca2"   | \N           | "eq_ref" | "PRIMARY,Index 6,Index 7"                                                                                                                                                                 | "PRIMARY"                        | "4"       | "edu_db.crx2.parent_id"    | "1"    | "100.00"   | "Using index"                                               |
| "1"  | "SIMPLE"      | "crx1"  | \N           | "ref"    | "PRIMARY,FK_child_id_to_id_category_tbl"                                                                                                                                                  | "FK_child_id_to_id_category_tbl" | "4"       | "edu_db.crx2.parent_id"    | "1"    | "100.00"   | "Using index"                                               |
| "1"  | "SIMPLE"      | "ca1"   | \N           | "eq_ref" | "PRIMARY,Index 6,Index 7"                                                                                                                                                                 | "PRIMARY"                        | "4"       | "edu_db.crx1.parent_id"    | "1"    | "100.00"   | \N                                                          |
| "1"  | "SIMPLE"      | "co1"   | \N           | "eq_ref" | "PRIMARY"                                                                                                                                                                                 | "PRIMARY"                        | "4"       | "edu_db.ca1.course_id"     | "1"    | "100.00"   | \N                                                          |
| "1"  | "SIMPLE"      | "crx4"  | \N           | "ref"    | "PRIMARY"                                                                                                                                                                                 | "PRIMARY"                        | "4"       | "edu_db.qcpa.category_id"  | "4"    | "100.00"   | "Using index"                                               |
| "1"  | "SIMPLE"      | "ca5"   | \N           | "eq_ref" | "PRIMARY,Index 6,Index 7"                                                                                                                                                                 | "PRIMARY"                        | "4"       | "edu_db.crx4.child_id"     | "1"    | "100.00"   | "Using index"                                               |
+------+---------------+---------+--------------+----------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+----------------------------------+-----------+--------------------------------+--------+------------+-------------------------------------------------------------+

Ниже приведен код создания для category_relation_xref и qbnk_category_published_assessment_xref tables

 CREATE TABLE `category_relation_xref` (
    `parent_id` INT(11) NOT NULL,
    `child_id` INT(11) NOT NULL,
    `template_id` INT(11) NOT NULL DEFAULT '1',
    `possition_id` INT(11) NOT NULL DEFAULT '1',
    `comment` VARCHAR(255) NULL DEFAULT NULL COLLATE 'utf16_unicode_ci',
    `display_order` INT(11) NOT NULL DEFAULT '0',
    `created_at` TIMESTAMP NULL DEFAULT NULL,
    `updated_at` TIMESTAMP NULL DEFAULT NULL,
    `created_by` INT(11) NULL DEFAULT NULL,
    `updated_by` INT(11) NULL DEFAULT NULL,
    PRIMARY KEY (`parent_id`, `child_id`),
    INDEX `FK_child_id_to_id_category_tbl` (`child_id`),
    INDEX `FK_cat_rel_xref_tbl_template_id_to_content_template_tbl` (`template_id`),
    INDEX `FK_cat_rel_xref_tbl_possition_id_to_content_template_tbl_id` (`possition_id`),
    CONSTRAINT `FK_cat_rel_xref_tbl_possition_id_to_content_template_tbl_id` FOREIGN KEY (`possition_id`) REFERENCES `content_possition` (`id`),
    CONSTRAINT `FK_cat_rel_xref_tbl_template_id_to_content_template_tbl` FOREIGN KEY (`template_id`) REFERENCES `content_template` (`id`),
    CONSTRAINT `category_relation_xref_ibfk_1` FOREIGN KEY (`parent_id`) REFERENCES `category` (`id`),
    CONSTRAINT `category_relation_xref_ibfk_2` FOREIGN KEY (`child_id`) REFERENCES `category` (`id`)
)
COMMENT='store parent child relations'
COLLATE='utf16_unicode_ci'
ENGINE=InnoDB
;

CREATE TABLE `qbnk_category_published_assessment_xref` (
    `published_assessment_id` INT(11) NOT NULL,
    `category_id` INT(11) NOT NULL,
    `status` ENUM('active','deleted') NOT NULL DEFAULT 'active' COLLATE 'utf16_unicode_ci',
    `comment` VARCHAR(255) NULL DEFAULT NULL COLLATE 'utf16_unicode_ci',
    `created_at` TIMESTAMP NULL DEFAULT NULL,
    `updated_at` TIMESTAMP NULL DEFAULT NULL,
    `created_by` INT(11) NULL DEFAULT '0',
    `updated_by` INT(11) NULL DEFAULT '0',
    PRIMARY KEY (`published_assessment_id`, `category_id`),
    INDEX `FK_qbnk_cat_id_pub_ass_tbl_to_category_tbl_id` (`category_id`),
    INDEX `cat_pub_status` (`status`),
    CONSTRAINT `FK_qbnk_cat_id_pub_ass_tbl_to_category_tbl_id` FOREIGN KEY (`category_id`) REFERENCES `category` (`id`) ON DELETE CASCADE,
    CONSTRAINT `FK_qbnk_cat_pub_ass_id_to_pub_ass_tbl_id` FOREIGN KEY (`published_assessment_id`) REFERENCES `qbnk_published_assessment` (`id`) ON DELETE CASCADE
)
COMMENT='Store category published assessments mappings'
COLLATE='utf16_unicode_ci'
ENGINE=InnoDB
;

Ответы [ 3 ]

0 голосов
/ 02 октября 2018

После очистки запроса, удаления лишних меток и круглых скобок, я считаю, что намного легче следовать следующему и увидеть взаимосвязи между таблицами.Вы хорошо сделали это с помощью соединений leftAlias.leftColumn = rightAlias.rightColumn.

Теперь, когда я могу лучше видеть вещи, давайте рассмотрим ваши таблицы.Они чаще всего выглядят как таблицы поиска, в которых у вас есть идентификатор и описательный столбец, который вы возвращаете.Я бы СОЗДАЛ покрывающие индексы в этих таблицах, чтобы разрешения соединений можно было обрабатывать непосредственно из индексов, а не переходить на страницы необработанных данных.

Одно ДОПОЛНИТЕЛЬНОЕ соображение по времени заключается в добавлении ключевого слова MySQL "STRAIGHT_JOIN", которое сообщаетдвижок для запроса таблиц в том порядке, в котором я их перечислял.Не думай за меня.Каждая из ваших таблиц начинается на самом верхнем уровне и получает все поиски вниз по течению.Вы также можете сравнить время, удалив также предложение STRAIGHT_JOIN. В системе, с которой я работал YEARS назад, потребовалось, чтобы основная таблица с примерно 15M записями и более 20 таблицами поиска зависала после 12+ часов обработки и сокращалась до завершенногозапрос выполняется чуть более часа.

Очистка исходного запроса

SELECT STRAIGHT_JOIN
      cou.id AS country_id,
      a.id AS area_id,
      y.id AS year_id,
      su.id AS subject_id,
      co1.`name` AS course_name,
      ca1.id AS root_category_id,
      ca1.`name` AS root_category_name,
      ca4.id AS chapter_id,
      ca4.`name` AS chapter_name,
      ca4.no_of_assets AS no_of_assets,
      ca4.active_status AS `status`,
      0 AS READ_IT,
      0 AS WATCH_IT,
      0 AS PLAY_IT,
      0 AS PROVE_IT,
      count(DISTINCT pa.id) AS APROVE_IT,
      if((count(pa.id) > 0),'True', 'False') AS sections_with_content,
      count(pa.id) AS content_count,
      pa.`status` AS content_flag
   FROM 
      edu_db.category_relation_xref crx1
         JOIN edu_db.category ca1
            ON crx1.parent_id = ca1.id
            LEFT JOIN edu_db.course co1
               ON ca1.course_id = co1.id

         JOIN edu_db.category_relation_xref crx2 
            ON crx1.child_id = crx2.parent_id

            JOIN edu_db.category ca2
               ON crx2.parent_id = ca2.id

            JOIN edu_db.category ca3
               ON crx2.child_id = ca3.id

            JOIN edu_db.category_relation_xref crx3 
               ON crx2.child_id = crx3.parent_id
               JOIN edu_db.category ca4 
                  ON crx3.child_id = ca4.id

                  JOIN edu_db.course co2
                     ON ca4.course_id = co2.id
                     JOIN edu_db.curriculum cu
                        ON co2.curriculum_id = cu.id
                        JOIN edu_db.`year` y 
                           ON cu.year_id = y.id
                           JOIN edu_db.area a
                              ON y.area_id = a.id
                              JOIN edu_db.country cou 
                                 ON a.country_id = cou.id
                        JOIN edu_db.subject su
                           ON cu.subject_id = su.id

                  LEFT JOIN edu_db.qbnk_category_published_assessment_xref qcpa
                     ON ca4.id = qcpa.category_id
                     LEFT JOIN edu_db.qbnk_published_assessment pa
                        ON qcpa.published_assessment_id = pa.id

               LEFT JOIN edu_db.category_relation_xref crx4
                  ON crx3.child_id = crx4.parent_id
                  LEFT JOIN edu_db.category ca5 
                     ON crx4.child_id = ca5.id
   WHERE 
          pa.`status` <> 'non_active'
      AND qcpa.`status` <> 'deleted'
   GROUP BY 
      ca4.id

Другой элемент ... У вас есть несколько таблиц, которые ЛЕВО соединены, и даже неиспользуется в запросе и может быть полностью удален.Явно "СЛЕДУЮЩЕЕ СОЕДИНЕНИЕ edu_db.category ca5".Вы не извлекаете никаких значений из псевдонима CA5, и левое соединение означает, что вы все равно не заботились об этом.Аналогично для "LEFT JOIN edu_db.category_relation_xref crx4"

SELECT STRAIGHT_JOIN
      a.country_id,
      a.id AS area_id,
      y.id AS year_id,
      cu.subject_id,
      co1.`name` AS course_name,
      ca1.id AS root_category_id,
      ca1.`name` AS root_category_name,
      ca4.id AS chapter_id,
      ca4.`name` AS chapter_name,
      ca4.no_of_assets AS no_of_assets,
      ca4.active_status AS `status`,
      0 AS READ_IT,
      0 AS WATCH_IT,
      0 AS PLAY_IT,
      0 AS PROVE_IT,
      count(DISTINCT pa.id) AS APROVE_IT,
      if((count(pa.id) > 0),'True', 'False') AS sections_with_content,
      count(pa.id) AS content_count,
      pa.`status` AS content_flag
   FROM 
      edu_db.category_relation_xref crx1
         JOIN edu_db.category ca1
            ON crx1.parent_id = ca1.id
            LEFT JOIN edu_db.course co1
               ON ca1.course_id = co1.id

         JOIN edu_db.category_relation_xref crx2 
            ON crx1.child_id = crx2.parent_id
            JOIN edu_db.category_relation_xref crx3 
               ON crx2.child_id = crx3.parent_id
               JOIN edu_db.category ca4 
                  ON crx3.child_id = ca4.id
                  JOIN edu_db.course co2
                     ON ca4.course_id = co2.id
                     JOIN edu_db.curriculum cu
                        ON co2.curriculum_id = cu.id
                        JOIN edu_db.`year` y 
                           ON cu.year_id = y.id
                           JOIN edu_db.area a
                              ON y.area_id = a.id

                  JOIN edu_db.qbnk_category_published_assessment_xref qcpa
                     ON ca4.id = qcpa.category_id
                     AND qcpa.`status` <> 'deleted'

                     JOIN edu_db.qbnk_published_assessment pa
                        ON qcpa.published_assessment_id = pa.id
                        AND pa.`status` <> 'non_active'
   GROUP BY 
      ca4.id

Ваши предложения WHERE, связанные с "pa" и "qcpa", отменяют часть LEFT JOIN, поскольку where превращает ее в предложение WHERE.Поэтому я удалил компонент «ВЛЕВО» И переместил часть предложения where непосредственно в этот компонент соединения.

Вы тянете «предметную» таблицу (псевдоним su), но только захватывает su.id.Поскольку у вас есть идентификатор субъекта из псевдонима «cu», вы можете просто использовать вместо него «cu.subject_id» и удалить еще одну таблицу из запроса - ЕСЛИ вы не планируете получать другие описания из таблицы «субъект».Это, вероятно, то же самое для вашей страны, области, года присоединения, а также.Если у вас уже есть идентификатор из предыдущей таблицы, используйте его и отбросьте ненужное ...

Не используйте псевдонимы "ca2" или "ca3" для каких-либо дополнительных подробностей, описаний, избавьтесь отэто.

Так что мои предложения по индексам для каждой таблицы будут включать следующее.Это будет больше ПОКРЫТИЯ индексов.Это не должны быть отдельные индексы в одной и той же таблице, такие как индекс Tbl1 для идентификатора, индекс Tbl1 для описания, но индекс Tbl1 ON (идентификатор, описание) в качестве одного индекса.

table                                   index
qbnk_published_assessment               ( id, `status` ) 
qbnk_category_published_assessment_xref ( category_id, `status`, published_assessment_id )
area                                    ( id )
`year`                                  ( id, area_id ) 
curriculum                              ( id, year_id )
course                                  ( id, curriculum_id )
category                                ( id, course_id )
category_relation_xref                  ( parent_id, child_id )
0 голосов
/ 04 октября 2018

Я называю это "чрезмерной нормализацией".

Нет необходимости в дополнительной таблице для country - для всех стран есть совершенно хорошие значения CHAR(2) CHARACTER SET ascii.Обратите внимание, что INT составляет 4 байта, поэтому это изменение экономит место!

Это тип данных YEAR;используй это.Если у вас есть полная дата или дата и время, используйте DATE или DATETIME и выберите YEAR из них в SELECT.YEAR меньше, чем INT.

. Читайте о ENUM как возможном типе данных для столбцов с небольшим числом возможных значений.

и т. Д.

0 голосов
/ 02 октября 2018

Поскольку EXPLAIN начинается с таблиц pa и ocpa, мы видим, что оптимизатор сначала пытается отфильтровать результаты, прежде чем делать что-либо еще.Это разумно, но другой разумный (э-э?) Способ - сначала выполнить GROUP BY, а затем отфильтровать результаты.

Новый индекс

Учитывая это, давайте попробуемчтобы помочь оптимизатору сделать это, чтобы увидеть, есть ли у нас какие-либо улучшения:

ALTER TABLE qbnk_category_published_assessment_xref
ADD INDEX so52589130_qcpa (`status`,`category_id`,`published_assessment_id`)

Пояснение

Указанный выше индекс позволяет начать с таблицы category для GROUP BY, затем использоватьПредложение WHERE для столбца status с category_id для соответствия двум крайним левым столбцам этого индекса.Это дает немедленный доступ к столбцу published_assessment_id без необходимости выполнять вторичный поиск.

Причина, по которой он не пытается сделать это в первую очередь, заключается в том, что у вас есть индексы на qbnk_category_published_assessment_xref donне допускайте этого без вторичного поиска.

cat_pub_status позволяет использовать status, но затем он получает published_assessment_id перед category_id, что не позволяет ему использовать category_id чтобы быстро найти нужную запись.

FK_qbnk_cat_id_pub_ass_tbl_to_category_tbl_id позволяет сначала использовать category_id, но затем все еще должен вернуться к кластерному индексу, чтобы получить status.

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

Возможная дополнительная экономия

Тот факт, что два условия WHERE равны <>, может снизить производительностьнемного, по сравнению с тем, если бы они были = значения.Если для каждого столбца status есть только два значения состояния, я бы поменял их местами на =.Это не слишком важно, хотя, если есть более двух значений.

Инструкции

Итак, попробуйте приведенный выше индекс.Запустите его дважды, отмените первый результат синхронизации, поскольку буферный кеш будет загружаться при этом вызове.Ответьте со второго раза И измените ли план EXPLAIN.Если план EXPLAIN изменился, а сроки все еще не достаточно быстрые, добавьте новый план EXPLAIN к своему вопросу, и я еще раз посмотрю.(Мне может понадобиться больше определений таблиц в это время, но мы подождем и посмотрим, как это сработает в первую очередь.)

...