如何提高对列求和的性能

how to increase performance of summing a column

我正在尝试调整我的查询,但无法进一步调整。有没有机会调整更多这个查询?特别是 SUM 子查询。

索引: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 秒内执行。

创建一个多列(复合)索引这将解决您的问题

要做到这一点,请点击以下查询

create index <some_name> on db_prices(date,villaId);

如果您遇到进一步的问题post您的解释声明以供进一步调查

要加速此子查询:

(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 在索引中只是为了使处理更有效率——所有列都在索引中,因此引擎不需要在数据页中查找值。

尝试先做聚合然后加入table

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
...

有时这会有所帮助。

编辑 更正了一些复制错误

MySQL(从 v 5.7 开始)有一个查询规划器,没有印章来转换您的依赖子查询

 (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

此查询为您提供价格总和,未标记的别墅数量为每栋别墅免费。这很方便,因为您现在可以将其加入到 table 的其余部分。像这样:

 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) 上的复合索引来优化您的子查询。您可能还需要其他 table 的其他一些列的索引。

专业提示:许多单列索引在加速查询方面无法替代复合索引。单独索引大量列是一种常见且臭名昭著的反模式。读这个:http://use-the-index-luke.com/

专业提示:您的查询使用了 GROUP BY 的非标准 MySQL 扩展。在 MySQL 的版本中,您可能很快就会停止工作,除非您更改某些服务器设置。读这个:https://dev.mysql.com/doc/refman/5.7/en/group-by-handling.html