MySQL多个依赖子查询,痛苦地慢

时间:2010-05-12 20:48:51

标签: database mysql subquery correlated-subquery

我有一个可以检索我需要的数据的工作查询,但不幸的是它很慢(运行超过3分钟)。我有索引,但我认为问题是多个依赖子查询。我一直在尝试使用连接重写查询,但我似乎无法让它工作。任何帮助将不胜感激。

表格:

基本上,我有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;   

查询检索30个小时内的最后10个价格变化,按价格变化的大小排序。所以我有子查询来获得最新的价格,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分钟?任何帮助将不胜感激!

谢天谢地。

更新

使用下面的答案,我得到了一个查询,这需要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

它可能仍然可以用于某些工作,但它可以原样使用。谢谢大家的帮助!

3 个答案:

答案 0 :(得分:0)

此SQL有几个问题:

  • 您正在多次执行相同的查询:

    (SELECT prices_am FROM price WHERE prices_watches_ID = current_ID   AND prices_locale ='US'ORDER BY prices_date DESC LIMIT 1)as new_price, (SELECT prices_date FROM price WHERE prices_watches_ID = current_ID   AND 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子句描述的那样过滤掉其中的一些行。

答案 1 :(得分:0)

我首先要确保您在进行比较和表达式时有数值。任何涉及类型转换的索引都将无法使用。你的价格是varchars。

答案 2 :(得分: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的价格的第二个连接,其中WHERE子句仅选择最近的条目。你可能会把它清理干净但我想把它拿出去。