优化包含操作和子查询的SQL查询

时间:2016-01-03 15:07:13

标签: mysql sql caching subquery

我试图加快一个SQL查询,这个查询返回了15个最便宜的产品,这些产品具有一些零售商销售的一组特定的特征(宽度,高度,直径和负载)。 de DB中约有300k产品。总价格计算如下:

  

totalPrice =数量*(价格 - 折扣*价格)+ shippingCost

其中:

  • 折扣(百分比)取决于订购数量,零售商和时间。由于同一组合可能存在许多折扣,因此选择最有利的百分比。
  • shippingCost 取决于订购数量,一些产品特征以及产品必须交付的目的地

我的问题是折扣 shippingsCost 依赖于太多参数来存储每个总价格组合,以便使查询运行得更快。因此,我认为我坚持使用子查询。

以下是SQL查询的简化版本,其中产品数量设置为2。

SELECT `P`.*, `B`.`name_local` as brandName, `R`.`name` as retailerName, `D`.`amount` as discount,     `S`.`shippingCost`, ROUND(P.price * 2 + IFNULL(S.shippingCost, 0) - IFNULL(P.price * D.amount / 100 * 2, 0), 2 ) as totalPrice
FROM (`Product` P)
JOIN `Brand` B ON `B`.`id` = `P`.`idBrand`
JOIN `Retailer` R ON `R`.`id` = `P`.`idRetailer`
LEFT JOIN `Shipping` S ON `S`.`idRetailer` = `P`.`idRetailer` AND S.nbProduct = (SELECT nbProduct FROM `Shipping` WHERE nbProduct <= 2 ORDER BY nbProduct DESC LIMIT 1)
LEFT JOIN `Discount` D ON `D`.`idRetailer` = `P`.`idRetailer` AND D.amount = (SELECT MAX(amount) FROM Discount D WHERE (D.vehicle = P.vehicle OR D.vehicle = 0) AND D.idRetailer = P.idRetailer AND D.start <= 1451825895 AND D.end >=1451825895)
WHERE `width` =  '195'
AND `height` =  '65'
AND `diameter` =  '15'
AND `load` >= 0
ORDER BY `totalPrice` ASC
LIMIT 15  

我使用的是mysql 14.14。在我的机器上,查询大约需要150ms才能执行。通过避免使用当前时间戳来进行折扣,可以更好地利用mysql查询缓存。但是,查询需要很长时间才能执行第一次,并且很快从查询缓存中刷新(由于许多组合)。以下是查询explain命令的结果:

+----+--------------------+----------+--------+-----------------------------------------------+------------+---------+------------------------+-------+----------------------------------------------+
| id | select_type        | table    | type   | possible_keys                                 | key        | key_len | ref                    | rows  | Extra                                        |
+----+--------------------+----------+--------+-----------------------------------------------+------------+---------+------------------------+-------+----------------------------------------------+
|  1 | PRIMARY            | P        | ref    | idBrand,idRetailer,width,height,diameter,load | width      | 12      | const,const,const      | 13268 | Using where; Using temporary; Using filesort |
|  1 | PRIMARY            | S        | ref    | idRetailer                                    | idRetailer | 4       | mydb.P.idRetailer      |     1 | Using where                                  |
|  1 | PRIMARY            | B        | eq_ref | PRIMARY                                       | PRIMARY    | 4       | mydb.P.idBrand         |     1 |                                              |
|  1 | PRIMARY            | R        | eq_ref | PRIMARY                                       | PRIMARY    | 4       | mydb.P.idRetailer      |     1 |                                              |
|  1 | PRIMARY            | D        | ref    | idRetailer                                    | idRetailer | 4       | mydb.S.idRetailer      |     1 |                                              |
|  3 | DEPENDENT SUBQUERY | D        | ref    | idRetailer,start                              | idRetailer | 4       | mydb.P.idRetailer      |     1 | Using where                                  |
|  2 | SUBQUERY           | Shipping | ALL    | NULL                                          | NULL       | NULL    | NULL                   |    48 | Using where; Using filesort                  |
+----+--------------------+----------+--------+-----------------------------------------------+------------+---------+------------------------+-------+----------------------------------------------+

是否有一种加速这种查询的优雅方法,或者我唯一的方法是改善查询缓存的效果(添加RAM,增加缓存大小等)?

1 个答案:

答案 0 :(得分:0)

对于第二个(更复杂的子查询)尝试使用计算折扣LEFT JOIN,让它只执行一次。喜欢这个

...
LEFT JOIN (SELECT MAX(amount) as amount, D.vehicle, D.idRetailer 
FROM Discount D 
WHERE D.start <= 1451825895 
   AND D.end >=1451825895
GROUP BY D.idRetailer, D.vehicle) D ON D.`idRetailer` = `P`.`idRetailer` 
                                 AND (D.vehicle = P.vehicle OR D.vehicle = 0)