Как оптимизировать этот Mysql простой запрос - PullRequest
2 голосов
/ 13 марта 2020

Этот запрос занимает 450 мс

SELECT `u`.`user_id`, `c`.`company`
FROM `users` AS `u`
LEFT JOIN `companies` AS `c` ON `c`.`user_id` = `u`.`user_id`
WHERE `u`.`user_id` = 'search_term' 
  OR `u`.`lname` LIKE 'search_term%'    
  OR `u`.`email` LIKE 'search_term%'        
  OR `c`.`company` LIKE 'search_termeo%'

таблиц:

  • пользователей (260250 строк)
  • компаний (570 строк)

структуры:

- пользователи:

   CREATE TABLE `users` (
    `user_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
    `region_id` int(10) unsigned NOT NULL,
    `fname` varchar(30) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
    `lname` varchar(30) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
    `email` varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL,
    `password` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
    `phone` varchar(20) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
    `active` tinyint(1) NOT NULL DEFAULT '0',
     PRIMARY KEY (`user_id`),
     KEY `idx_lname` (`lname`),
     KEY `idx_email` (`email`),
     UNIQUE KEY `unq_region_id_email` (`region_id`, `email`)
   ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

- компании:

 CREATE TABLE `companies` (
  `user_id` int(10) unsigned NOT NULL,
  `company` varchar(35) COLLATE utf8mb4_unicode_ci NOT NULL,
  `vat_num` varchar(20) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
   PRIMARY KEY (`user_id`),
   KEY `idx_company` (`company`) USING BTREE,
   CONSTRAINT `users_companies_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`user_id`) ON DELETE CASCADE ON UPDATE CASCADE,
   ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

Результат запроса объяснения Query explain

Я думаю 450 мс слишком много для такого запроса и такого небольшого количества данных, и я хочу знать, есть ли что-то для оптимизации

запросов, выполняемых в Query v3 под iMa c 2017, 3,4 ГГц, 16 Go

Mysql: 5.7.26 на MAMP pro v5.7

1 Ответ

2 голосов
/ 13 марта 2020
Условия

OR, когда они не находятся в одном поле или диапазоне (например, <, >, LIKE), действительно уменьшают способность MySQL использовать преимущества индексов; вы можете реструктурировать запросы, разбивая их на отдельные более простые, которые вы затем можете UNION. Такое разделение позволяет MySQL использовать различные индексы каждого запроса в UNION *

SELECT `u`.`user_id`, `c`.`company`
FROM `users` AS `u` LEFT JOIN `companies` AS `c` ON `c`.`user_id` = `u`.`user_id`
WHERE `u`.`user_id` = 'search_term' 
UNION DISTINCT 
SELECT `u`.`user_id`, `c`.`company`
FROM `users` AS `u` LEFT JOIN `companies` AS `c` ON `c`.`user_id` = `u`.`user_id`
WHERE `u`.`lname` LIKE 'search_term%'    
UNION DISTINCT 
SELECT `u`.`user_id`, `c`.`company`
FROM `users` AS `u` LEFT JOIN `companies` AS `c` ON `c`.`user_id` = `u`.`user_id`
WHERE `u`.`email` LIKE 'search_term%'
UNION DISTINCT 
SELECT `u`.`user_id`, `c`.`company`
FROM `users` AS `u` INNER JOIN `companies` AS `c` ON `c`.`user_id` = `u`.`user_id`
WHERE `c`.`company` LIKE 'search_termeo%'
;

Также обратите внимание, что я изменил последний JOIN на INNER, так как любое условие справа - таблица LEFT JOIN (которая не «без совпадения с этой таблицей») в любом случае является INNER JOIN.

UNION DISTINCT используется для предотвращения повторения записей, удовлетворяющих нескольким условиям, однако ... если companies.company не является уникальным (т. е. идентификатор компании 1 называется "Blah", а идентификатор компании 12 также называется "Blah"), то они также будут объединены там, где их нет в исходном запросе; если это потенциальная проблема, которая может быть исправлена ​​путем включения company_id в каждый SELECT.

...