Оптимизация запроса

zip111

Новичок
Оптимизация запроса

Здравствуйте.
Как можно оптимизировать запрос типа:

SELECT id, c_name, format( rand( ) *100, 0 ) *35 + format( c_budget *100 / (
SELECT max( c_budget )
FROM campaign ) , 0 ) *35 + format( link_cost *100 / (
SELECT max( link_cost )
FROM campaign ) , 0 ) *30 AS koeff
FROM `campaign`
WHERE c_status != '1'
AND user_id = '1'
ORDER BY koeff DESC
LIMIT 0,30


Таблица

CREATE TABLE `campaign` (
`id` int(10) unsigned NOT NULL auto_increment,
`user_id` int(10) unsigned NOT NULL,
`c_name` varchar(25) collate cp1251_bin NOT NULL,
`c_type` tinyint(1) default NULL,
`c_region` varchar(15) collate cp1251_bin NOT NULL,
`c_key` varchar(15) collate cp1251_bin NOT NULL,
`c_about` varchar(70) collate cp1251_bin NOT NULL,
`c_link` varchar(50) collate cp1251_bin NOT NULL,
`c_status` tinyint(1) NOT NULL,
`c_budget` decimal(10,2) default '0.00',
`link_cost` decimal(10,2) default '0.00',
`c_trans` int(11) default '0',
`c_shows` int(11) default '0',
PRIMARY KEY (`id`),
KEY `c_budget` (`c_budget`),
KEY `c_status` (`c_status`),
KEY `c_key` (`c_key`),
KEY `link_cost` (`link_cost`),
KEY `c_type` (`c_type`),
KEY `c_region` (`c_region`)
) ENGINE=MyISAM DEFAULT CHARSET=cp1251 COLLATE=cp1251_bin;

В ней 850.000 записей. Время обработки запроса - 6 сек.

explain:
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY campaign ALL c_status NULL NULL NULL 855098 Using where; Using temporary; Using filesort
3 SUBQUERY NULL NULL NULL NULL NULL NULL NULL Select tables optimized away
2 SUBQUERY NULL NULL NULL NULL NULL NULL NULL Select tables optimized away
Спасибо
 

zip111

Новичок
ну если не высчитывать рандом - то будут выводиться одни и те же `c_name`.

Мыслей нет. Честно.
 

Mr_Max

Первый класс. Зимние каникулы ^_^
Команда форума
тем с сортировкой по ранд и их оптимизации в форуме предостаточно.
 

Alexandre

PHPПенсионер
SELECT max( c_budget )
SELECT max( link_cost )
а можно их первоночально вычислить и подставить в запрос.
будет быстрее.
 

berkut

Новичок
явно в начале нужно добавить составной индекс на c_status, user_id

-~{}~ 19.02.08 20:58:

а SELECT max( .. ) выносить особо смысла нет
 

Gas

может по одной?
ё, а де индекс по user_id ?
+ как сказал Alexandre выкинуть подзапосы с MAX если для одного user_id может быть много записей.

а SELECT max( .. ) выносить особо смысла нет
зависит от количества записей подпадающих под условие.
 

Mr_Max

Первый класс. Зимние каникулы ^_^
Команда форума
без ранд() скорость не меняется
И не поменяется, поскольку ты вычисляешь данные.
а индексы у тебя "раздельные".

-~{}~ 19.02.08 19:02:

http://spivak.kiev.ua/mysql/multiple-column-indexes.html
 

Wicked

Новичок
order by по вычисляемому полю тебе так и так temporary и filesort даст.

что у нас с тем, насколько наши 850к записей сужаются за счет условий WHERE c_status != '1' AND user_id = '1' ?
 

FractalizeR

Новичок
Интересно, какой из индексов имеет большую кардинальность? MySQL считает, что c_status и использует для отбора в первую очередь его. После ANALYZE TABLE ее мнение не меняется? Можно попробовать выполнить этот запрос раздельно с FORCE INDEX (c_status) и FORCE INDEX(user_id) и посмотреть, как быстрее получается. Мне почему-то кажется, что (user_id=1) более строгое условие, чем (c_status != '1')

-~{}~ 19.02.08 20:47:

Гм... Только что заметил, что по полю user_id вообще индекса нет. Надо бы его создать...
 

zip111

Новичок
user_id - индекс пропустил, добавил.
добавил составной индекс на c_status, user_id

результат кардинально ен улучшился.

по експлеин теперь видно что запрос использует составной индекс
 

Gas

может по одной?
убери составной, оставь только на user_id и кинь explain.
 

berkut

Новичок
FractalizeR а теперь прочти ветку сначала и не тормози. нужен составной индекс, а не куча их.
zip111
больше ничего не сделаешь с таким запросом.
 

Gas

может по одной?
Если составной делать, то (user_id, c_status) не наоборот. При таком условии c_status на количество rows никак не сыграет, зато все данные для where будут из одного места (индекса) тянуться.
А так да, всё что можно было сделать без изменения формата данных - сказали и посоветовали.
 

berkut

Новичок
Alexandre а что тут знать? явно у него по условию выбирается тучча строк, поэтому (user_id, c_status) или наоборот - погоды не сделают. единственный вариант, убрать ордер бай, или сделать его по индексному полю - но это уже совсем другой запрос
 

Gas

может по одной?
berkut
На самом деле не всё так однозначно, cardinality по user_id нам не известна, b-tree index не поддерживает условие !=, по-этому если в составном ключе первым стоит c_status, то идёт полный скан индекса (может mysql умнее и сразу исключает из перебора записи удовлетворяющие условию - кто знает?), что не есть хорошо. Но в данном случае действительно может разницы и не быть: простой перебор всего индекса по c_status!=1 или "b-tree алгоритмы" для user_id=1 в случае если большинство записей пренадлежат user_id=1. Информации на этот счёт zip111 не представил.

Ага, не видел ответа, ну тогда тебе не повезло :)
 
Сверху