Профилирование JOIN и запросов подзапросов в phpMyAdmin - PullRequest
0 голосов
/ 05 ноября 2018

Описание проблемы У меня есть таблица аудита, которая содержит историю изменений некоторых объектов. Аудит содержит уникальный идентификатор события аудита, идентификатор изменяемого объекта, дату изменения, измененное свойство, а также значения до и после изменения и другие столбцы.

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

Схема и данные Схема таблицы имеет идентификатор (id) в качестве первичного ключа и идентификатор объекта (parent_id) в качестве индекса. Больше ничего не индексируется. В моем тестовом примере у меня есть примерно 150 объектов с около 80 тыс. Записей аудита для них.

Решение Есть два очевидных решения подзапросов и левого соединения.

В левом соединении я в основном снова соединяю ту же самую таблицу аудита с самим оператором соединения, проверяя, соответствуют ли изменения объекта, поля и значения, изменения старше текущего изменения и выбирают максимальную дату изменения и, наконец, только забрать одно последнее предыдущее изменение, которое я сгруппировал по идентификатору. Если предыдущее изменение не найдено, используйте дату создания самого объекта. LEFT JOIN SQL

SELECT `audit`.`id` AS `id`,
`audit`.`parent_id` AS `parent_id`,
`audit`.`date_created` AS `date_created`,
COALESCE(MAX(`audit_prev`.`date_created`), `audit_parent`.`date_entered`) AS `date_created_before`,
`audit`.`field_name` AS `field_name`,
`audit`.`before_value_string` AS `before_value_string`,
`audit`.`after_value_string` AS `after_value_string`
FROM `opportunities_audit` `audit`
LEFT JOIN `opportunities_audit` `audit_prev`
    ON(`audit`.`parent_id` = `audit_prev`.`parent_id`
        AND `audit_prev`.`date_created` < `audit`.`date_created`
        AND `audit_prev`.`after_value_string` = `audit`.`before_value_string`
        AND `audit`.`field_name` = `audit_prev`.`field_name`)
LEFT JOIN `opportunities` `audit_parent` ON(`audit`.`parent_id` = `audit_parent`.`id`)
GROUP BY `audit`.`id`;

Логика подзапроса довольно похожа, но вместо группировки и использования функции MAX у меня просто есть порядок по дате DESC и LIMIT 1

SELECT `audit`.`id` AS `id`,
`audit`.`parent_id` AS `parent_id`,
`audit`.`date_created` AS `date_created`,
COALESCE((SELECT `audit_prev`.`date_created`
    FROM `opportunities_audit` AS `audit_prev`
    WHERE
        (`audit_prev`.`parent_id` = `audit`.`parent_id`)
        AND (`audit_prev`.`date_created` < `audit`.`date_created`)
        AND (`audit_prev`.`after_value_string` = `audit`.`before_value_string`)
        AND (`audit_prev`.`field_name` = `audit`.`field_name` )
        ORDER BY `date_created` DESC
    LIMIT 1
), `audit_parent`.`date_entered`) AS `date_created_before`,
`audit`.`field_name` AS `field_name`,
`audit`.`before_value_string` AS `before_value_string`,
`audit`.`after_value_string` AS `after_value_string`
FROM `opportunities_audit` `audit`
LEFT JOIN `opportunities` `audit_parent` ON(`audit`.`parent_id` = `audit_parent`.`id`);

Оба запроса дают идентичные наборы результатов.

Выпуск Когда я запускаю запрос в phpMyAdmin, решение с объединением занимает примерно 2 мсек, чтобы вернуть результат. Тем не менее, phpMyAdmin говорит, что запрос занял 0,04 секунды. Когда я запускаю решение подзапроса, результат возвращается немедленно, и сообщаемое время выполнения phpMyAdmin составляет примерно 0,06 секунды.

Так что мне трудно понять, откуда эта разница в реальном времени исполнения. Первоначально я предполагал, что проблема будет связана с автоматическими LIMITS phpMyAdmin для возвращаемого набора данных - в то время как результат содержит 80 тыс. Строк, он отображает только 25. Но добавление LIMIT вручную в запросы заставляет их оба выполняться быстро.

Кроме того, при выполнении запросов из командной строки инструмент mysql возвращает полные наборы результатов для обоих запросов, и сообщенное время выполнения соответствует фактическому времени выполнения, а метод, использующий объединения, все еще примерно в 1,5 раза быстрее, чем подзапрос.

Из данных профилировщика кажется, что большая часть этого времени ожидания тратится на отправку данных. Как это занимает отправка данных занимает порядка минут, а все остальное в порядке микросекунд.

И все же, почему поведение phpMyAdmin так сильно отличается в случае двух запросов?

...