2010-05-12 3 views
2

У меня есть рабочий запрос, который извлекает нужные мне данные, но, к сожалению, он очень медленный (длится 3 минуты). У меня есть индексы на месте, но я думаю, что проблема заключается в нескольких зависимых подзапросах. Я пытался переписать запрос с помощью объединений, но я не могу заставить его работать. Любая помощь будет принята с благодарностью.MySQL несколько зависимых подзапросов, мучительно медленный

Таблицы:

В принципе, у меня есть 2 таблицы. Первые (цены) содержат цены на предметы в магазине. Каждая строка - это цена предмета в день, а новые строки добавляются каждый день с обновленной ценой.

Вторая таблица (watches_US) содержит информацию о позиции (имя, описание и т. Д.).

CREATE TABLE `prices` (
`prices_id` int(11) NOT NULL auto_increment, 
`prices_locale` enum('CA','DE','FR','JP','UK','US') NOT NULL default 'US', 
`prices_watches_ID` char(10) NOT NULL, 
`prices_date` datetime NOT NULL, 
`prices_am` varchar(10) default NULL, 
`prices_new` varchar(10) default NULL, 
`prices_used` varchar(10) default NULL, 
PRIMARY KEY (`prices_id`), 
KEY `prices_am` (`prices_am`), 
KEY `prices_locale` (`prices_locale`), 
KEY `prices_watches_ID` (`prices_watches_ID`), 
KEY `prices_date` (`prices_date`) 
) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=61764 ; 

CREATE TABLE `watches_US` (
`watches_ID` char(10) NOT NULL, 
`watches_date_added` datetime NOT NULL, 
`watches_last_update` datetime default NULL, 
`watches_title` varchar(255) default NULL, 
`watches_small_image_height` int(11) default NULL, 
`watches_small_image_width` int(11) default NULL, 
`watches_description` text, 
PRIMARY KEY (`watches_ID`) 
) ENGINE=InnoDB DEFAULT CHARSET=utf8; 

Запрос извлекает последние 10 изменений цен за определенный период 30 часов, упорядоченных по размеру изменения цены. Поэтому у меня есть подзапросы, чтобы получить самую новую цену, самую старую цену в течение 30 часов, а затем рассчитать изменение цены.

Вот запрос:

SELECT watches_US.*, prices.*, watches_US.watches_ID as current_ID, 
    (SELECT prices_am FROM prices WHERE prices_watches_ID = current_ID AND prices_locale = 'US' ORDER BY prices_date DESC LIMIT 1) as new_price, 
    (SELECT prices_date FROM prices WHERE prices_watches_ID = current_ID AND prices_locale = 'US' ORDER BY prices_date DESC LIMIT 1) as new_price_date, 
    (SELECT prices_am FROM prices WHERE (prices_watches_ID = current_ID AND prices_locale = 'US') AND (prices_date >= DATE_SUB(new_price_date,INTERVAL 30 HOUR)) ORDER BY prices_date ASC LIMIT 1) as old_price, 
    (SELECT ROUND(((new_price - old_price)/old_price)*100,2)) as percent_change, 
    (SELECT (new_price - old_price)) as absolute_change 
FROM watches_US 
LEFT OUTER JOIN prices ON prices.prices_watches_ID = watches_US.watches_ID 
WHERE (prices_locale = 'US') 
AND (prices_am IS NOT NULL) 
AND (prices_am != '') 
HAVING (old_price IS NOT NULL) 
AND (old_price != 0) 
AND (old_price != '') 
AND (absolute_change < 0) 
AND (prices.prices_date = new_price_date) 
ORDER BY absolute_change ASC 
LIMIT 10 

Как бы переписать это использование присоединяется вместо или иначе оптимизировать это так это не займет более 3 минут, чтобы получить результат? Любая помощь будет принята с благодарностью!

Благодарим вас.

UPDATE

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

SELECT watches_US.*, prices.*, 
    (SELECT prices_am FROM prices prices2 WHERE (prices2.prices_watches_ID = watches_US.watches_ID AND prices2.prices_locale = 'US') AND (prices2.prices_date >= DATE_SUB(prices.prices_date,INTERVAL 30 HOUR)) ORDER BY prices2.prices_date ASC LIMIT 1) as old_price, 
    (SELECT ROUND(((prices.prices_am - old_price)/old_price)*100,2)) as percent_change, 
    (SELECT (prices.prices_am - old_price)) as absolute_change 
FROM watches_US 
LEFT OUTER JOIN prices ON prices.prices_watches_ID = watches_US.watches_ID AND prices.prices_locale = 'US' 
WHERE (prices.prices_am IS NOT NULL) 
AND (prices.prices_am != '') 
AND (prices.prices_date IN (SELECT MAX(prices_date) FROM prices WHERE prices_watches_ID = watches_US.watches_ID AND prices_locale = 'US')) 
HAVING (old_price IS NOT NULL) 
AND (old_price != 0) 
AND (old_price != '') 
AND (absolute_change < 0) 
ORDER BY absolute_change ASC 
LIMIT 10 

Это может, вероятно, до сих пор с некоторой работой, но это можно использовать как есть. Спасибо за вашу помощь!

ответ

0

Вот частичная идея:

SELECT watches_US.*, prices.*, watches_US.watches_ID as current_ID, 
    prices2.prices_am as new_price, 
    prices2.prices_date as new_price_date, 
    (SELECT prices_am FROM prices WHERE (prices_watches_ID = current_ID AND prices_locale = 'US') AND (prices_date >= DATE_SUB(new_price_date,INTERVAL 30 HOUR)) ORDER BY prices_date ASC LIMIT 1) as old_price, 
    (SELECT ROUND(((new_price - old_price)/old_price)*100,2)) as percent_change, 
    (SELECT (new_price - old_price)) as absolute_change 
FROM watches_US 
LEFT OUTER JOIN prices ON prices.prices_watches_ID = watches_US.watches_ID 
LEFT OUTER JOIN prices prices2 ON prices2.prices_watches_ID = watches_US.watches_ID 
WHERE (prices_locale = 'US') 
AND (prices_am IS NOT NULL) 
AND (prices_am != '') 
AND (prices2.prices_date IN (SELECT MAX(price_date) FROM prices WHERE prices_watches_ID = watches_US.watches_ID AND prices_locale = 'US') 
HAVING (old_price IS NOT NULL) 
AND (old_price != 0) 
AND (old_price != '') 
AND (absolute_change < 0) 
AND (prices.prices_date = new_price_date) 
ORDER BY absolute_change ASC 
LIMIT 10 

изменения являются второй присоединиться по ценам, который используется для получения new_price и new_price_date с ИНЕКЕ, чтобы выбрать только самую последнюю запись. Возможно, вы могли бы немного почистить его, но я хотел его получить.

+0

Похоже, что это сработает, к сожалению, MySQL не допустит псевдоним current_ID в предложении where. – matt80

+0

Я только что отредактировал его для inline current_ID – sblundy

0

Есть несколько проблем с этим SQL:

  • Вы выполняете тот же запрос несколько раз:

    (SELECT prices_am FROM цены WHERE prices_watches_ID = current_ID И prices_locale = 'US' ORDER BY price_date DESC LIMIT 1) as new_price, (SELECT prices_date FROM prices WHERE prices_watches_ID = current_ID И prices_locale = 'US' ORDER BY prices_date DESC LIMIT 1) as new_price_date,

Вы должны выполнить запрос только один раз, указать ему имя и выбрать несколько столбцов, e.q. SELECT ... sub1.prices_am, sub1.prices_date FROM ... SELECT() sub1 если я не ошибаюсь.

  • Не используйте по какой-либо причине HAVING. Он убивает вашу производительность, поскольку он заставляет базу данных извлекать все строки в вашем запросе и затем фильтровать некоторые из них, как описано в предложении HAVING.
0

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

Смежные вопросы