J'ai une requête qui récupère les données dont j'ai besoin, mais malheureusement, elle est douloureusement lente (plus de 3 minutes). J'ai des index en place, mais je pense que le problème est les sous-requêtes dépendantes multiples. J'ai essayé de réécrire la requête à l'aide de jointures, mais je n'arrive pas à la faire fonctionner. Toute aide serait grandement appréciée.Sous-requêtes dépendantes multiples MySQL, douloureusement lente
Les tables:
Fondamentalement, j'ai 2 tables. Le premier (prix) détient les prix des articles dans un magasin. Chaque ligne correspond au prix d'un article ce jour-là et de nouvelles lignes sont ajoutées chaque jour avec un prix actualisé.
La seconde table (watches_US) contient les informations sur l'élément (nom, description, etc.).
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;
La requête récupère les 10 dernières modifications de prix sur une période de 30 heures, commandés par la taille du changement de prix. J'ai donc des sous-requêtes pour obtenir le prix le plus récent, le prix le plus ancien dans les 30 heures, puis pour calculer le changement de prix.
est ici la requête:
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
Comment puis-je réécrire cette option pour utiliser à la place se joint, ou autrement optimiser cette façon, il ne prend pas plus de 3 minutes pour obtenir un résultat? Toute aide serait grandement appréciée!
Merci beaucoup.
MISE À JOUR
En utilisant les réponses en bas, je suis arrivé à la requête à ceci, qui prend 2 secondes pour exécuter:
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
Il pourrait probablement faire encore avec un peu de travail, mais il est utilisable tel quel. Merci à tous pour votre aide!
On dirait qu'il travaillerait, malheureusement MySQL ne permettra pas à l'current_ID alias dans la clause where. – matt80
Je viens de l'éditer pour intégrer current_ID – sblundy