我正在尝试调整我的查询,但我不能再进一步了。有没有机会调整更多这个查询?特别是SUM子查询。
INDEX:db_prices.date
原始查询示例:
SELECT
db_villas.id,
db_villas.title1,
db_specials.id AS sid,
db_specials.title1 AS stitle,
db_cities.name AS cityName,
db_counties.name AS countyName,
db_assets.path,
db_villas.bathroom,
db_villas.bedroom,
db_villas.guest,
db_prices.date,
(SELECT SUM(db_prices.price) FROM db_prices WHERE db_prices.date BETWEEN "2016-08-01" AND "2016-09-30" AND db_prices.villaId=db_villas.id) AS price
FROM
db_villas
INNER JOIN db_cities ON db_villas.cityId = db_cities.id
LEFT OUTER JOIN db_specials ON db_villas.specialId = db_specials.id
INNER JOIN db_counties ON db_counties.cityid = db_cities.id AND db_villas.countyId = db_counties.id
INNER JOIN db_assets ON db_assets.guid = db_villas.guid
INNER JOIN db_villafacilities ON db_villafacilities.villaId = db_villas.id
INNER JOIN db_prices ON db_prices.villaId = db_villas.id
WHERE db_prices.date BETWEEN "2016-08-01" AND "2016-09-30" AND db_assets.isMainImage=1 AND db_villas.minRent <= 7
GROUP BY db_villas.id
HAVING (SELECT COUNT(*) FROM db_prices WHERE date BETWEEN "2016-08-01" AND "2016-09-30" AND db_prices.isFree = 0 AND db_prices.villaId = db_villas.id)=0
上面的查询在1.2秒内执行。
当我删除
(SELECT SUM(db_prices.price) FROM db_prices WHERE db_prices.date BETWEEN "2016-08-01" AND "2016-09-30" AND db_prices.villaId=db_villas.id) AS price
子查询执行时间减少到0.009秒。
如果我只是删除这部分
AND db_prices.villaId=db_villas.id
从子查询中它仍然在0.009秒内执行。
答案 0 :(得分:3)
MySQL(自v 5.7起)有一个查询计划器,没有用于转换依赖子查询的chop
(SELECT SUM(db_prices.price)
FROM db_prices
WHERE db_prices.date BETWEEN "2016-08-01" AND "2016-09-30"
AND db_prices.villaId=db_villas.id) AS price
进入可连接的独立子查询。这意味着查询计划程序会使用正常运行时间多次运行该查询。所以你需要自己改造它。独立子查询将如下所示:
SELECT villaId,
SUM(price) price,
SUM(CASE WHEN isFree = 0 THEN 1 ELSE 0 END) not_free_count
FROM db_prices
WHERE date BETWEEN '2016-08-01' AND '2016-09-30'
GROUP BY villaId
此查询为您提供每个别墅的价格总和以及未标记为isFree的别墅数量。这很方便,因为您现在可以将其加入到表的其余部分。像这样:
SELECT db_villas.id,
db_villas.title1, etc etc,
price_summary.price
FROM db_villas
INNER JOIN db_cities ON db_villas.cityId = db_cities.id
LEFT OUTER JOIN db_specials ON db_villas.specialId = db_specials.id
etc etc.
LEFT JOIN (
SELECT villaId,
SUM(price) price,
SUM(CASE WHEN isFree = 0 THEN 1 ELSE 0 END) not_free_count
FROM db_prices
WHERE date BETWEEN '2016-08-01' AND '2016-09-30'
GROUP BY villaId
) price_summary ON db_villas.villaId = price_summmary.villaId
WHERE db_prices.date BETWEEN "2016-08-01" AND "2016-09-30"
AND etc etc
AND price_summary.not_free_count = 0
GROUP BY db_villas.villaId
然后,您需要db_prices (date, villaId, price, isFree)
上的复合索引来优化子查询。您可能还需要其他表的其他列的索引。
专业提示:在加快查询速度时,许多单列索引无法替代复合索引。单独索引大量列是一种常见且臭名昭着的反模式。阅读:http://use-the-index-luke.com/
专家提示:您的查询使用GROUP BY
的非标准MySQL扩展。在MySQL的一个版本中,您可能很快就会停止工作,除非您更改某些服务器设置。阅读:https://dev.mysql.com/doc/refman/5.7/en/group-by-handling.html
答案 1 :(得分:2)
加快这个子查询:
(SELECT SUM(p.price)
FROM db_prices p
WHERE p.date BETWEEN '2016-08-01' AND '2016-09-30' AND
p.villaId = db_villas.id
) AS price
你想要一个索引。最佳索引是覆盖索引,这些列按此顺序排列:db_prices(villaId, date, price)
。覆盖索引包括子查询中的所有列。
列villaId
应该是第一个,因为它具有相等的条件;然后date
因为它也在where
子句中。最后,price
在索引中只是为了使处理更有效 - 所有列都在索引中,因此引擎不需要在数据页中查找值。
答案 2 :(得分:1)
首先尝试制作聚合并将其与表
连接SELECT
db_villas.id,
db_villas.title1,
db_specials.id AS sid,
db_specials.title1 AS stitle,
db_cities.name AS cityName,
db_counties.name AS countyName,
db_assets.path,
db_villas.bathroom,
db_villas.bedroom,
db_villas.guest,
db_prices.date,
pricesum
FROM (SELECT db_prices.villaId, SUM(db_prices.price) as pricesum FROM db_prices WHERE db_prices.date BETWEEN "2016-08-01" AND "2016-09-30" group by db_prices.villaId) as prices
INNER JOIN db_villas ON prices.villaId = db_villas.id
...
有时这会有所帮助。
修改强> 纠正了一些复制错误
答案 3 :(得分:0)
创建一个多列(复合)索引,这将解决您的问题
要执行此操作,请按以下查询
create index <some_name> on db_prices(date,villaId);
如果您在解释性陈述中遇到进一步调查时面临更多问题