如何提高对列求和的性能
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
我正在尝试调整我的查询,但无法进一步调整。有没有机会调整更多这个查询?特别是 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