Почему подзапрос в этом запросе MYSQL намного медленнее, чем этот? - PullRequest
0 голосов
/ 16 января 2019

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

SELECT invoices.*,card.*,client.* 
FROM `orders` 
LEFT JOIN `invoices` ON `invoices`.`invoice_key` =`orders`.`invoice_key` 
LEFT JOIN `cards` `card` ON `card`.`card_code` = `orders`.`card_code` 
LEFT JOIN `users` `client` ON `client`.`id` = (
     SELECT MIN(c.id) c_id FROM `users` `c` 
     WHERE (`card`.`card_code` COLLATE utf8_unicode_ci) = `c`.`card_code`
     AND  `c`.`active` = 1 ORDER BY `c`.`last_login` DESC LIMIT 1
)
WHERE `orders`.`date` BETWEEN %start_date% AND %end_date%

Это работало нормально, но затем я внедрил систему, в которой для пользователя было возможно иметь несколько карточных кодов; Чтобы справиться с этим, я создал еще одну таблицу (пользовательские карты), которая связывает пользователей с картами. Не у всех пользователей есть запись здесь; большинству нужен только код карты по умолчанию.

SELECT invoices.*,card.*,client.* 
FROM `orders` 
LEFT JOIN `invoices` ON `invoices`.`invoice_key` =`orders`.`invoice_key` 
LEFT JOIN `cards` `card` ON `card`.`card_code` = `orders`.`card_code` 
LEFT JOIN `usercards` ON `usercards`.`card_code` = `card`.`card_code` 
LEFT JOIN `users` `client` ON (`usercards`.`user_id` IS NOT NULL 
   AND `client`.`id` = `usercards`.`user_id`) 
OR (`usercards`.`user_id` IS NULL AND `client`.`id` = (
     SELECT MIN(c.id) c_id FROM `users` `c` 
     WHERE (`card`.`card_code` COLLATE utf8_unicode_ci) = `c`.`card_code`  
       AND  `c`.`active` = 1 ORDER BY `c`.`last_login` DESC LIMIT 1)
)
WHERE `orders`.`date` BETWEEN %start_date% AND %end_date%

Однако, это привело к тому, что запрос выполнялся на намного медленнее - как несколько минут, когда раньше он был меньше секунды. Я знаю, что это как-то связано с подзапросом (если я пропускаю эту часть и просто присоединяюсь везде, где client.card_code = card.card_code, это работает быстро, но, конечно, тогда данные неверны), но я не могу понять, почему? Я не вижу ни одной точки, которая предполагает экспоненциальное увеличение данных, которые необходимо обработать.

Существует много заказов и счетов, но только несколько записей в карточках пользователей. Кроме того, usercards.card_code - это уникальный индекс.

1 Ответ

0 голосов
/ 17 января 2019

Слишком много вопросов, чтобы я мог дать вам прямой ответ.

  • OR - убийца производительности. Это может быть хорошим кандидатом для преобразования в UNION. Если это так, предложения IS [not] NULL могут быть упрощены одновременно.
  • MIN и LIMIT 1 сражаются друг с другом. Я подозреваю, что вы должны избавиться от MIN.
  • Не используйте LEFT, если только вы не планируете пропустить строки в «правильной» таблице.
  • Смотрите мои советы здесь по индексам для usercards.
  • usercards.user_id IS NULL кажется новым условием; это не меняет результаты?
  • Исправьте сопоставление одного из столбцов card_code, чтобы оно соответствовало другому. То, что у вас есть, либо отрицательно сказывается на использовании индекса, либо замедляет работу.
  • Попробуйте перевернуть запрос. То есть начать с поиска самого последнего идентификатора, затем JOIN (при условии, что LEFT не требуется) для других таблиц.
  • INDEX(card_code, active, last_login, id) - в этом порядке.
  • (Может быть больше; исправьте некоторые из них; тогда давайте обсудим дальше.)
  • (используйте более короткие псевдонимы - чтобы избежать беспорядка.)

Для дальнейшего обсуждения предоставьте

SHOW CREATE TABLE -- for each table
EXPLAIN SELECT -- for each query being discussed
...