SQLinfo.ru - Все о MySQL Webew.ru: теория и практика веб-технологий

Форум пользователей MySQL

Задавайте вопросы, мы ответим

Вы не зашли.

#1 15.05.2013 12:59:00

vitalik_74
Участник
Зарегистрирован: 14.05.2013
Сообщений: 3

Производительность ORDER BY

 
Добрый день,

Есть один запрос:

SELECT o.* , u.name as login , u.regdate , x.regdate as lastdate , m.name as manager_name ,
SUM(IF(b.bb_image != '', 1, 0)) AS photo , SUM(IF(b.bb_yandex_panorama != '', 1, 0)) AS panorama ,
SUM(IF(b.bb_shema != '', 1, 0)) AS shema , SUM(IF(b.bb_x != '' AND b.bb_y != '', 1, 0)) AS map ,
SUM(IF(price1.p_price2 != 0 OR price2.p_price2 != 0 OR price3.p_price2 != 0 OR price4.p_price2 != 0 OR price5.p_price2 != 0 OR price6.p_price2 != 0 OR price7.p_price2 != 0, 1, 0)) AS pri ,
SUM(IF(price1.p_price2 != '' AND price1.p_res!=1 AND price1.p_status!=1 AND price1.p_flag!=1, 1, 0)) AS pt1 ,
SUM(IF(price2.p_price2 != '' AND price2.p_res!=1 AND price2.p_status!=1 AND price2.p_flag!=1, 1, 0)) AS pt2 ,
SUM(IF(price3.p_price2 != '' AND price3.p_res!=1 AND price3.p_status!=1 AND price3.p_flag!=1, 1, 0)) AS pt3 ,
SUM(IF(price4.p_price2 != '' AND price4.p_res!=1 AND price4.p_status!=1 AND price4.p_flag!=1, 1, 0)) AS pt4 ,
SUM(IF(price5.p_price2 != '' AND price5.p_res!=1 AND price5.p_status!=1 AND price5.p_flag!=1, 1, 0)) AS pt5 ,
SUM(IF(price6.p_price2 != '' AND price6.p_res!=1 AND price6.p_status!=1 AND price6.p_flag!=1, 1, 0)) AS pt6 ,
SUM(IF(price7.p_price2 != '' AND price7.p_res!=1 AND price7.p_status!=1 AND price7.p_flag!=1, 1, 0)) AS pt7 ,
SUM(IF(price1.p_price2 != '', 1, 0)) AS p1 ,
SUM(IF(price2.p_price2 != '', 1, 0)) AS p2 ,
SUM(IF(price3.p_price2 != '', 1, 0)) AS p3 ,
SUM(IF(price4.p_price2 != '', 1, 0)) AS p4 ,
SUM(IF(price5.p_price2 != '', 1, 0)) AS p5 ,
SUM(IF(price6.p_price2 != '', 1, 0)) AS p6 ,
SUM(IF(price7.p_price2 != '', 1, 0)) AS p7 ,
SUM(IF(price1.p_res = 1, 1, 0)) AS r1 ,
SUM(IF(price2.p_res = 1, 1, 0)) AS r2 ,
SUM(IF(price3.p_res = 1, 1, 0)) AS r3 ,
SUM(IF(price4.p_res = 1, 1, 0)) AS r4 ,
SUM(IF(price5.p_res = 1, 1, 0)) AS r5 ,
SUM(IF(price6.p_res = 1, 1, 0)) AS r6 ,
SUM(IF(price7.p_res = 1, 1, 0)) AS r7 ,
SUM(IF(b.bb_baseprice != 0, 1, 0)) AS baseprice ,
SUM(IF(b.bb_gps != 0, 1, 0)) AS grp ,
COUNT(b.bb_id) AS billboards_cnt

FROM owners AS o
LEFT JOIN users AS u ON u.user_id = o.o_lastupdater
LEFT JOIN users AS x ON x.user_id = o.o_user_id
LEFT JOIN users AS m ON m.user_id = o.o_manager_id
LEFT JOIN billboards AS b ON b.bb_owner = o.o_id
AND b.bb_r NOT IN (1,2,3,.....100)  
LEFT JOIN prices AS price1 ON price1.p_bb_id = bb_id  AND price1.p_year = '2013' AND price1.p_month = '6'
LEFT JOIN prices AS price2 ON price2.p_bb_id = bb_id  AND price2.p_year = '2013' AND price2.p_month = '7'
LEFT JOIN prices AS price3 ON price3.p_bb_id = bb_id  AND price3.p_year = '2013' AND price3.p_month = '8'
LEFT JOIN prices AS price4 ON price4.p_bb_id = bb_id  AND price4.p_year = '2013' AND price4.p_month = '9'
LEFT JOIN prices AS price5 ON price5.p_bb_id = bb_id  AND price5.p_year = '2013' AND price5.p_month = '10'
LEFT JOIN prices AS price6 ON price6.p_bb_id = bb_id  AND price6.p_year = '2013' AND price6.p_month = '11'
LEFT JOIN prices AS price7 ON price7.p_bb_id = bb_id  AND price7.p_year = '2013' AND price7.p_month = '12'
WHERE
o.o_id IN (1,2,3,......310)
GROUP BY o.o_id
ORDER BY o_lastupdate DESC
 


Запрос выполняется за 5 сек.
Если убрать ORDER, то за 0.7 (что вполне достаточно)

Explain с ORDER BY

id  select_type table   type    possible_keys   key key_len ref rows    Extra
1   SIMPLE  o   ALL PRIMARY,o_id,viviv  NULL    NULL    NULL    417 Using where; Using temporary; Using filesort
1   SIMPLE  u   eq_ref  PRIMARY PRIMARY 4   testallbillboards.o.o_lastupdater   1    
1   SIMPLE  x   eq_ref  PRIMARY PRIMARY 4   testallbillboards.o.o_user_id   1    
1   SIMPLE  m   eq_ref  PRIMARY PRIMARY 4   testallbillboards.o.o_manager_id    1    
1   SIMPLE  b   ref bb_owner,bb_r,bb_owner_bb_r,bb_r_bb_deleted bb_owner    4   testallbillboards.o.o_id    406  
1   SIMPLE  price1  ref p_bb_id,p_year,p_month,prices_bb_y_m_prices prices_bb_y_m_prices    7   testallbillboards.b.bb_id,const,const   1    
1   SIMPLE  price2  ref p_bb_id,p_year,p_month,prices_bb_y_m_prices prices_bb_y_m_prices    7   testallbillboards.b.bb_id,const,const   1    
1   SIMPLE  price3  ref p_bb_id,p_year,p_month,prices_bb_y_m_prices prices_bb_y_m_prices    7   testallbillboards.b.bb_id,const,const   1    
1   SIMPLE  price4  ref p_bb_id,p_year,p_month,prices_bb_y_m_prices prices_bb_y_m_prices    7   testallbillboards.b.bb_id,const,const   1    
1   SIMPLE  price5  ref p_bb_id,p_year,p_month,prices_bb_y_m_prices prices_bb_y_m_prices    7   testallbillboards.b.bb_id,const,const   1    
1   SIMPLE  price6  ref p_bb_id,p_year,p_month,prices_bb_y_m_prices prices_bb_y_m_prices    7   testallbillboards.b.bb_id,const,const   1    
1   SIMPLE  price7  ref p_bb_id,p_year,p_month,prices_bb_y_m_prices prices_bb_y_m_prices    7   testallbillboards.b.bb_id,const,const   1    
 


Explain без  ORDER BY

id  select_type table   type    possible_keys   key key_len ref rows    Extra
1   SIMPLE  o   ALL PRIMARY,o_id,viviv  NULL    NULL    NULL    417 Using where; Using temporary; Using filesort
1   SIMPLE  u   eq_ref  PRIMARY PRIMARY 4   testallbillboards.o.o_lastupdater   1    
1   SIMPLE  x   eq_ref  PRIMARY PRIMARY 4   testallbillboards.o.o_user_id   1    
1   SIMPLE  m   eq_ref  PRIMARY PRIMARY 4   testallbillboards.o.o_manager_id    1    
1   SIMPLE  b   ref bb_owner,bb_r,bb_owner_bb_r,bb_r_bb_deleted bb_owner    4   testallbillboards.o.o_id    406  
1   SIMPLE  price1  ref p_bb_id,p_year,p_month,prices_bb_y_m_prices prices_bb_y_m_prices    7   testallbillboards.b.bb_id,const,const   1    
1   SIMPLE  price2  ref p_bb_id,p_year,p_month,prices_bb_y_m_prices prices_bb_y_m_prices    7   testallbillboards.b.bb_id,const,const   1    
1   SIMPLE  price3  ref p_bb_id,p_year,p_month,prices_bb_y_m_prices prices_bb_y_m_prices    7   testallbillboards.b.bb_id,const,const   1    
1   SIMPLE  price4  ref p_bb_id,p_year,p_month,prices_bb_y_m_prices prices_bb_y_m_prices    7   testallbillboards.b.bb_id,const,const   1    
1   SIMPLE  price5  ref p_bb_id,p_year,p_month,prices_bb_y_m_prices prices_bb_y_m_prices    7   testallbillboards.b.bb_id,const,const   1    
1   SIMPLE  price6  ref p_bb_id,p_year,p_month,prices_bb_y_m_prices prices_bb_y_m_prices    7   testallbillboards.b.bb_id,const,const   1    
1   SIMPLE  price7  ref p_bb_id,p_year,p_month,prices_bb_y_m_prices prices_bb_y_m_prices    7   testallbillboards.b.bb_id,const,const   1    
 



Структура базы

CREATE TABLE IF NOT EXISTS `owners` (
  `o_id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `o_name` varchar(255) NOT NULL,  
  `o_user_id` int(11) unsigned NOT NULL,
  `o_lastupdate` int(11) unsigned NOT NULL ,
  PRIMARY KEY (`o_id`),  
  KEY `o_lastupdate` (`o_lastupdate`),
  KEY `o_id` (`o_id`),
  KEY `viviv` (`o_id`,`o_lastupdate`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8  ;



Вопросы:

1) Почему не работает индекс при сортировке?

2) Почему при выборке из таблицы billboards не работает индеск bb_owner_bb_r (в нем 2 поля bb_owner и bb_r)?

Заранее спасибо.

Неактивен

 

#2 15.05.2013 20:16:16

rgbeast
Администратор
MySQL Authorized Developer and DBA
Откуда: Москва
Зарегистрирован: 21.01.2007
Сообщений: 3880

Re: Производительность ORDER BY

Упростите пример, чтобы его легче было прочитать/понять. Попробуйте добиться такого же поведения с двумя таблицами.

Сортировка идет по полю, которого нет среди группируемых полей, значит запрос некорректен с точки зрения SQL.

Неактивен

 

#3 16.05.2013 18:59:00

Shopen
Гуру
Откуда: Москва
Зарегистрирован: 22.10.2007
Сообщений: 362

Re: Производительность ORDER BY

rgbeast написал:

Сортировка идет по полю, которого нет среди группируемых полей, значит запрос некорректен с точки зрения SQL.

Разве? А я грешным делом всегда считал, что в таком случае MySQL сортирует по указанному полю записи внутри каждой группы и выдает первую. Иначе тогда непонятна идея оптимизации GROUP BY ... ORDER BY NULL

Неактивен

 

#4 16.05.2013 20:07:19

rgbeast
Администратор
MySQL Authorized Developer and DBA
Откуда: Москва
Зарегистрирован: 21.01.2007
Сообщений: 3880

Re: Производительность ORDER BY

ORDER BY NULL - отменяет автоматическую сортировку по полю, по которому производится группировка (такая сортировка производится по умолчанию). Сортировка после GROUP BY эквивалентна

SELECT * FROM (SELECT .... GROUP BY x) A ORDER BY y;
, то есть с точки зрения SQL сортировать можно только по полям, присутствующим в выборке, чем в рамках корректного SQL могут быть только поля группировки или результаты аггрегирующих функций.

Неактивен

 

#5 16.05.2013 23:27:22

Shopen
Гуру
Откуда: Москва
Зарегистрирован: 22.10.2007
Сообщений: 362

Re: Производительность ORDER BY

Ясно, спасибо )

Тем не менее в отличии от неправильного использования аггрегирующих функций, на такой запрос MySQL не ругается

SELECT nick, name FROM tbl GROUP BY nick ORDER BY name

хотя по идее запрос некорректен. Кстати интересно какой именно name из группы с одним nick выдаст mysql - первую запись на какую наткнется (физически в файле)? Или случайную?

Неактивен

 

#6 16.05.2013 23:38:43

deadka
Администратор
Зарегистрирован: 14.11.2007
Сообщений: 2422

Re: Производительность ORDER BY

Может и выругаться на такой запрос, если sql_mode выставить соответствующий.


Зеленый свет для слабаков, долги отдают только трусы, тру гики работают только в консоли...

Неактивен

 

#7 17.05.2013 09:14:51

vitalik_74
Участник
Зарегистрирован: 14.05.2013
Сообщений: 3

Re: Производительность ORDER BY

1) Упростил запрос до:

SELECT o.*
FROM owners AS o
WHERE
o.o_id IN (.......(вcего перечисление около 350 id))
GROUP BY o.o_id
ORDER BY o_lastupdate DESC


id  select_type table   type    possible_keys                     key       key_len   ref        rows    Extra
1   SIMPLE      o       ALL     PRIMARY,565,o_id,viviv          NULL     NULL       NULL     417      Using where; Using filesort

Все равно идет сортировка файловая. Возможно потому что в таблице всего не более 500 записей и проще их перебреть.


2) >Сортировка идет по полю, которого нет среди группируемых полей, значит запрос некорректен с точки зрения SQL.

Получается надо добавить в условие WHERE o_lastupdate>0, к примеру?


3) Я обнаружил, что если убрать все LEFT JOIN таблицы prices, то запрос довольно шустро работает. В этой таблице 2 мил записей.

LEFT JOIN prices AS price1 ON price1.p_bb_id = bb_id  AND price1.p_year = '2013' AND price1.p_month = '6'


Если выполнить просто запрос

SELECT * FROM prices AS price1 WHERE price1.p_bb_id = {ID}  AND price1.p_year = '2013' AND price1.p_month = '6'


ТО он работает шустро 0.001 сек в среднем.


Explain
id                    1
select_type      SIMPLE
table               price1
type                ref
possible_keys    p_year,p_month,prices_bb_y_m_prices
key                 prices_bb_y_m_prices
key_len           7
ref                 const,const,const
rows              1
Extra           

В первоначальном запросе price связана с таблицей billboards в ней ~160к записей.

CREATE TABLE IF NOT EXISTS `prices` (
  `p_id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `p_bb_id` int(11) unsigned NOT NULL,
  `p_year` smallint(4) unsigned NOT NULL,
  `p_month` tinyint(2) unsigned NOT NULL,
  `p_flag` tinyint(1) unsigned NOT NULL,
  `p_price` mediumint(8) unsigned NOT NULL ,
  `p_price2` mediumint(8) unsigned NOT NULL,
  PRIMARY KEY (`p_id`),
  KEY `p_year` (`p_year`),
  KEY `p_month` (`p_month`),
  KEY `p_price` (`p_price`),
  KEY `p_price2` (`p_price2`),
  KEY `p_res` (`p_res`),
  KEY `prices_bb_y_m_prices` (`p_bb_id`,`p_year`,`p_month`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 ;


CREATE TABLE IF NOT EXISTS `billboards` (
  `bb_id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `bb_address` varchar(500) NOT NULL,  
  `bb_r` int(11) unsigned NOT NULL,
  `bb_owner` int(11) NOT NULL,
  PRIMARY KEY (`bb_id`),
  UNIQUE KEY `bb_id` (`bb_id`),  
  KEY `bb_owner` (`bb_owner`),
  KEY `bb_r` (`bb_r`),  
  KEY `bb_side_type` (`bb_side_type`),  
  KEY `bb_id_bb_deleted` (`bb_id`,`bb_deleted`),
  KEY `bb_owner_bb_r` (`bb_owner`,`bb_r`),
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 ;



Типы полей bb_id и p_bb_id совпадают. Индекс используется. Тип поля максимально небольшой для p_year и p_month.

Может как-то ещё улучшить связку таблиц billboard с prices и тогда первоначальный запрос заработает быстрее? Если да, то как?

Неактивен

 

#8 17.05.2013 10:50:24

Shopen
Гуру
Откуда: Москва
Зарегистрирован: 22.10.2007
Сообщений: 362

Re: Производительность ORDER BY

1. Вы его слишком упростили. Это вообще уже совсем другой запрос. Записей мало (500) выбираются почти все (~350), индекс невыгодно использовать.

Упростите первоначальный запрос до функционального минимума, выбросте все SUM, уберите повторяющиеся LEFT JOIN и приведите его explain.

2. Нет, это значит надо запрос модифицировать. Нельзя сортировать по o_lastupdate, так как ее значение для каждой группы o_id неопределено. Т.е. чтобы сортирвать по o_lastupdate нужно чтобы это поле было передано через агреггирующую функцию:

SELECT o.id, MAX(o_lastupdate) AS max_update
FROM owners AS o
....
GROUP BY o.o_id
ORDER BY max_update DESC

Неактивен

 

#9 17.05.2013 11:48:18

vitalik_74
Участник
Зарегистрирован: 14.05.2013
Сообщений: 3

Re: Производительность ORDER BY

Shopen написал:

1. Вы его слишком упростили. Это вообще уже совсем другой запрос. Записей мало (500) выбираются почти все (~350), индекс невыгодно использовать.

Упростите первоначальный запрос до функционального минимума, выбросте все SUM, уберите повторяющиеся LEFT JOIN и приведите его explain.

2. Нет, это значит надо запрос модифицировать. Нельзя сортировать по o_lastupdate, так как ее значение для каждой группы o_id неопределено. Т.е. чтобы сортирвать по o_lastupdate нужно чтобы это поле было передано через агреггирующую функцию:

SELECT o.id, MAX(o_lastupdate) AS max_update
FROM owners AS o
....
GROUP BY o.o_id
ORDER BY max_update DESC

1. Сделал такой запрос

SELECT o.*
FROM owners AS o
LEFT JOIN billboards AS b ON b.bb_owner = o.o_id AND b.bb_r NOT IN (...) 
LEFT JOIN prices AS price1 ON price1.p_bb_id = bb_id  AND price1.p_year = '2013' AND price1.p_month = '6'
WHERE
o.o_id IN (....)
GROUP BY o.o_id
ORDER BY o_lastupdate DESC

Выполняется за 0.9 сек.

А вот такой
SELECT o.*
FROM owners AS o
LEFT JOIN billboards AS b ON b.bb_owner = o.o_id AND b.bb_r NOT IN (...) 
WHERE
o.o_id IN (....)
GROUP BY o.o_id
ORDER BY o_lastupdate DESC

За 0.06

Explain 1 запроса:

id    select_type      table    type    possible_keys            key       key_len    ref    rows    Extra
1    SIMPLE        o             ALL    PRIMARY,565,o_id        NULL        NULL    NULL    417    Using where; Using temporary; Using filesort
                                                 ,viviv   
1    SIMPLE       b            ref    bb_owner,bb_r,           bb_owner    4      o.o_id  406    
                                                bb_owner_bb_r,
                                                bb_r_bb_deleted   

1    SIMPLE       price1    ref    p_year,p_month,         prices_bb_y_m_prices    7    b.bb_id,const,const    1    Using index
                                               prices_bb_y_m_prices   


Explain 2 запроса:

id    select_type    table      type    possible_keys    key               key_len           ref    rows    Extra
1    SIMPLE       o        ALL    PRIMARY,565,     NULL                NULL           NULL    417    Using where; Using temporary;
                                              o_id,viviv                                                                       Using filesort
1    SIMPLE       b         ref    bb_owner,bb_r,   bb_owner_bb_r    4         o.o_id    406    Using index
                                               bb_owner_bb_r,
                                              bb_r_bb_deleted   


То есть все упирается в медленной связке таблиц billboards(160к. записей) и prices(2 мил. записей).

2. Пробовал с MAX(o_lastupdate) AS max_update и ORDER BY max_update DESC. Особой разницы не заметил, если она и есть, то не существенна

Неактивен

 

Board footer

Работает на PunBB
© Copyright 2002–2008 Rickard Andersson