【发布时间】:2016-04-07 06:53:49
【问题描述】:
我正在尝试加快 SQL 查询的速度,该查询返回由少数零售商销售的具有给定特征(宽度、高度、直径和负载)的 15 种最便宜的产品。 de DB 中有大约 30 万种产品。总价计算如下:
totalPrice = 数量*(价格 - 折扣*价格)+ 运费
地点:
- 折扣(百分比)取决于订购的数量、零售商和时间。由于同一组合可能存在许多折扣,因此选择最有利的百分比。
- shippingCost 取决于订购的数量、一些产品特性以及产品必须交付的目的地
我的问题是 discount 和 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。在我的机器上执行查询大约需要 150 毫秒。通过避免使用当前时间时间戳进行折扣,可以更好地利用 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、增加缓存大小等)?
【问题讨论】:
-
150 毫秒相当快。
-
我的其他查询每个不到 2 毫秒。查询在许多不同的网页上进行评估,每个查询都会大大增加页面的第一个字节的时间。此外,处理时间随着存储在数据库中的产品数量的增加而增加(恐怕将来会达到半秒)。我正在尝试找到一种至少低于 50 毫秒的方法。
-
来自 Web 应用程序的输入是什么?宽度,高度,直径,负载?前三个似乎是产品属性,最后一个呢?
-
宽度、高度、直径、负载、数量来自 Web 应用程序。宽度、高度、直径和负载是 Product 表中的列。
-
如何将子查询移动到 FROM 部分以让它们只执行一次。至少运输子查询。貌似不依赖P
标签: mysql sql caching subquery