Mysql медленный запрос: JOIN + несколько WHERES + ORDER BY - PullRequest
7 голосов
/ 01 октября 2010

долгое время, первый вопрос!

Я изо всех сил пытаюсь оптимизировать этот запрос, который выбирает товары с наименьшей ценой, которые соответствуют выбранным фильтрам:

SELECT product_info.*, MIN(product_all.sale_price) as sale_price, product_all.buy_link
FROM product_info
NATURAL JOIN (SELECT * FROM product_all WHERE product_all.date = '2010-09-30') as product_all
WHERE (product_info.category = 2  
AND product_info.gender = 'W' )
GROUP BY product_all.prod_id
ORDER BY MIN(product_all.sale_price) ASC LIMIT 13

Его объяснение:

| id | select_type | table        | type   | possible_keys                                             | key     | key_len | ref                 | rows   | Extra                           |  
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+  
|  1 | PRIMARY     | <derived2>   | ALL    | NULL                                                     | NULL    | NULL    | NULL                | 89801  | Using temporary; Using filesort | 
|  1 | PRIMARY     | product_info | eq_ref | PRIMARY,category_prod_id_retail_price,category_ret...     | PRIMARY | 4       | product_all.prod_id | 1      | Using where                     | 
|  2 | DERIVED     | product_all  | ref    | date_2                                                    | date_2  | 3       |                     | 144107 |                                 | 

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

SELECT product_info.*, MIN(product_all.sale_price) as sale_price, product_all.buy_link
FROM product_info
NATURAL JOIN product_all
WHERE (product_all.date = '2010-09-30'
AND product_info.category = 2 
AND product_info.gender = 'W' )
GROUP BY product_all.prod_id
ORDER BY MIN(product_all.sale_price) ASC LIMIT 13

И его объяснение:

| id | select_type | table        | type | possible_keys                                             | key                      | key_len | ref                               | rows | Extra                                        |  
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+  
|  1 | SIMPLE      | product_info | ref  | PRIMARY,category_prod_id_retail_price,category_ret...     | category_retail_price    | 5       | const                             | 269  | Using where; Using temporary; Using filesort | 
|  1 | SIMPLE      | product_all  | ref  | PRIMARY,prod_id,date_2                                    | prod_id                  | 4       | equipster_db.product_info.prod_id | 141  | Using where                                  | 

Воттаблицы:

CREATE TABLE `product_all` (
`prod_id` INT( 10 ) NOT NULL PRIMARY KEY ,
`ref_id` INT( 10) NOT NULL PRIMARY KEY ,
`date` DATE NOT NULL ,
`buy_link` BLOB NOT NULL ,
`sale_price` FLOAT NOT NULL
) ENGINE = MYISAM ;


CREATE TABLE `product_info` (
`prod_id` INT( 10 ) NOT NULL AUTO_INCREMENT PRIMARY KEY ,
`prod_name` VARCHAR( 200 ) NOT NULL,
`brand` VARCHAR( 50 ) NOT NULL,
`retail_price` FLOAT NOT NULL
`category` INT( 3 ) NOT NULL,
`gender` VARCHAR( 1 ) NOT NULL,
`type` VARCHAR( 10 ) NOT NULL
) ENGINE = MYISAM ;

Мои вопросы:
- какая структура запроса кажется оптимальной?
- какие индексы оптимизируют этот запрос?
- без разницы: какменяется ли подход индексирования при добавлении или удалении предложений WHERE или использовании другого ORDER BY, например сортировки по% off:

ORDER BY (1-(MIN(product_all.sale_price)/product_info.retail_price)) DESC  

edit: естественное объединение обоих запросов действует на prod_id(одна запись в product_info может иметь несколько экземпляров в product_all, поэтому их необходимо сгруппировать)

Ответы [ 5 ]

4 голосов
/ 01 октября 2010

Индексы имеют огромное значение в mysql, один запрос, который занимал 15 минут с неправильным набором индексов, занимал .2 секунды с правильными, но обычно находил правильный баланс, что является проблемой. Естественно, без каких-либо примеров данных трудно сказать, сэкономит ли приведенное ниже решение какое-либо время, но теоретически это должно быть.

Чтобы ответить на ваши вопросы, я бы изменил таблицы следующим образом:

CREATE TABLE `product_all` ( 
`prod_id` INT( 10 ) NOT NULL, 
`ref_id` INT( 10) NOT NULL, 
`date` DATE NOT NULL , 
`buy_link` BLOB NOT NULL , 
`sale_price` FLOAT NOT NULL,
PRIMARY KEY (prod_id, ref_id) ,
INDEX date_Index (`date` ASC),
UNIQUE INDEX prod_price_Index (prod_id ASC, sale_price ASC)
) ENGINE = MYISAM ; 


CREATE TABLE `product_info` ( 
`prod_id` INT( 10 ) NOT NULL AUTO_INCREMENT, 
`prod_name` VARCHAR( 200 ) NOT NULL, 
`brand` VARCHAR( 50 ) NOT NULL, 
`retail_price` FLOAT NOT NULL, 
`category` INT( 3 ) NOT NULL, 
`gender` VARCHAR( 1 ) NOT NULL, 
`type` VARCHAR( 10 ) NOT NULL,
PRIMARY KEY (prod_id) ,
UNIQUE INDEX prod_id_name_Index (prod_id ASC, prod_name ASC),
INDEX category_Index (category ASC),
INDEX gender_Index (gender ASC)
) ENGINE = MYISAM ;

SELECT product_info.*, MIN(product_all.sale_price) as sale_price, product_all.buy_link         
FROM product_info         
NATURAL JOIN (SELECT * FROM product_all WHERE product_all.date = '2010-09-30') as product_all         
WHERE (product_info.category = 2           
AND product_info.gender = 'W' )         
GROUP BY product_all.prod_id         
ORDER BY MIN(product_all.sale_price) ASC LIMIT 13        

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

Насколько я понимаю, что происходит в первом и втором запросе:

  • Первый запрос фильтруется подзапрос до выполнения естественное соединение, это означает, что его единственное присоединение к полученным данным, а не весь стол.
  • Второй запрос присоединяется к весь второй стол, а затем фильтрация результирующих строк вернемся к тому, что вы хотите.

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

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

Здесь - статья, посвященная индексации производительности в mysql, которую стоит прочитать, если вы хотите узнать больше.

Удачи!

РЕДАКТИРОВАТЬ: Ваш последний вопрос, который я пропустил в первый раз, ответ таков: если индексация основных объединяющих полей приведет к изменению местоположения, оно лишь незначительно повлияет на общую производительность, но уникальные индексы, которые я поместил в таблицы, должны учитывать большинство вещей, на которые вы хотите основывать запросы. Главное, что нужно помнить, если вы часто запрашиваете или присоединяетесь к полю, тогда оно действительно должно быть проиндексировано, но о незначительных запросах и изменениях в вашем заказе просто не стоит беспокоиться о перестройке вашей стратегии индексирования.

0 голосов
/ 01 октября 2010

Как заявил Митч, попытка найти критерии, которые, естественно, имели бы меньшее количество записей, определенно выиграла бы для производительности.И если Category + Gender будет очень распространенным, сделайте это индексом для ОБА столбцов.Кроме того, как только вы найдете оптимальные критерии, вы можете изменить следующий запрос, чтобы он лучше соответствовал.«STRAIGHT_JOIN» говорит MySQL делать это в указанном вами порядке, вместо того, чтобы пытаться изменить основную таблицу, используемую для запроса базы данных и соединения с другой ... Итак, я не знаю, какой из индексов категории более точен, пол или дата ... Если у Date будет меньше записей, я бы поменял THAT в качестве первой таблицы в предложении FROM и мысленно переместил критерии IT по дате на первую позицию предложения WHERE (только я личносинхронизировать с таблицами визуально).Я видел, как STRAIGHT_JOIN значительно улучшил производительность в МНОГИХ ситуациях, которые в противном случае казались простыми запросами.

SELECT STRAIGHT_JOIN
      product_info.*, 
      MIN(product_all.sale_price) as sale_price, 
      product_all.buy_link 
   FROM 
      product_info,
      product_all 
   where 
          product_info.category = 2   
      AND product_info.gender = 'W'
      and product_info.prod_id = product_all.prod_id
      AND product_all.date = '2010-09-30'
   GROUP BY 
      product_info.prod_id 
   ORDER BY 
      MIN(product_all.sale_price) ASC 
   LIMIT 13 
0 голосов
/ 01 октября 2010

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

0 голосов
/ 01 октября 2010

Лично я являюсь минималистом sql и избегаю любых подзапросов или объединений, которые нельзя индексировать в индексные столбцы.

Если это не совсем возможно, я, вероятно, выполню подзапросы индивидуально для сборамои ключи, отсортируйте их на клиентском сайте, а затем создайте предложение where в (...).

JohnVD делает много хороших замечаний, но если вам нужно создать уникальный ключ, включая product_name, выдействительно нужно посмотреть, можно ли это нормализовать в нем.

Индексирование столбцов varchar - это то, от чего следует уклоняться любой ценой, если это возможно.Каждая запись индекса равна максимальному размеру столбца, даже если они обычно составляют лишь небольшую часть.И если вы используете кодировку типа utf-8, тогда размер ~ maxlen + 3.

С вашим ограничением кажется, что необходим порядок на.Но, как к сведению, когда вы работаете с группой, если вы собираетесь использовать весь набор результатов, добавьте ORDER BY NULL.Выполните два варианта через объяснение, чтобы понять почему;Порядок по нулю устраняет подразумеваемую сортировку файлов, и вы можете сортировать клиентскую сторону.(Это не возможно, если вы делаете групповую передачу с помощью свертки)

0 голосов
/ 01 октября 2010

Высокая производительность, никогда не стоит использовать

select *

Вместо них следует использовать имена отдельных столбцов.

select column1,column2 etc...
...