我在mysql中有一个产品订单表。就像这样:
create table `order`
(productcode int,
quantity tinyint,
order_date timestamp,
blablabla)
然后,为了获得上升率,我写了这个查询:
SELECT thismonth.productcode,
(thismonth.ordercount-lastmonth.ordercount)/lastmonth.ordercount as riserate
FROM ( (SELECT productcode,
sum(quantity) as ordercount
FROM `order`
where date_format(order_date,'%m') = 12
group by productcode) as thismonth,
(SELECT productcode,
sum(quantity) as ordercount
FROM `order`
where date_format(order_date,'%m') = 11
group by productcode) as lastmonth)
WHERE thismonth.productcode = lastmonth.productcode
ORDER BY riserate;
但它在我的电脑上运行大约30秒(200000条记录,200MB(包括其他字段))。 有没有办法提高查询速度?我已经为productcode字段创建了索引。
我尝试了你的答案,但所有这些似乎都不起作用,我想知道索引是否有问题(不是我创建了它们),所以我删除所有索引并重新创建它们,一切都很顺利 - 只需3-4s。我的查询和你的查询之间的区别并不是很明显。但真的非常感谢你们,我学到了很多东西:)
答案 0 :(得分:3)
尝试在(ORDER_DATE,PRODUCTCODE)上添加索引并更改查询以消除使用DATE_FORMAT函数,如:
SELECT thismonth.productcode,
(thismonth.ordercount-lastmonth.ordercount)/lastmonth.ordercount as riserate
FROM ( (SELECT productcode,
sum(quantity) as ordercount
FROM `order`
WHERE ORDER_DATE BETWEEN '01-12-2010' AND '31-12-2010'
GROUP BY PRODUCTCODE) as thismonth,
(SELECT productcode,
sum(quantity) as ordercount
FROM `order`
WHERE ORDER_DATE BETWEEN '01-11-2010' AND '30-11-2010'
group by productcode) as lastmonth)
WHERE thismonth.productcode = lastmonth.productcode
ORDER BY riserate;
分享并享受。
答案 1 :(得分:0)
鉴于您似乎正在使用大量数据,优化可能很困难。我首先看一下你如何使用order_date字段。它应该与product_code字段建立索引。我也不认为date_format是将月份从日期中删除的最佳方式 - MONTH(order_date)几乎肯定会更快。
如果失败了,如果这是一个多次被击中的查询,我会为历史数据创建一个新表,并用内部查询的结果填充它。由于它是历史数据,因此您无需持续获取最新数据。由于每次运行查询时都不必计算历史数据,因此运行速度会快得多。
答案 2 :(得分:0)
@Bob Jarvis'解决方案可能会解决您的速度问题。如果没有,或者您想尝试替代方案:
答案 3 :(得分:0)
SELECT
productcode,
(this_month_count - last_month_count) / last_month_count AS riserate
FROM (
SELECT
o.product,
SUM(CASE MONTH(o.order_date) WHEN MONTH(m.date_start) THEN o.quantity END) AS last_month_count,
SUM(CASE MONTH(o.order_date) WHEN MONTH(m.date_end) THEN o.quantity END) AS this_month_count
FROM `order` o
INNER JOIN (
SELECT
CAST('2010-11-01' AS date) AS date_start,
CAST('2010-12-31' AS date) AS date_end
) m ON o.order_date BETWEEN m.date_start AND m.date_end
GROUP BY o.product
) s
答案 4 :(得分:0)
如果使用时间戳的唯一原因是在插入和更新时使用自动默认值,请改用datetime并将now()放入插入和更新或使用触发器。时间戳为您提供了时区的额外转换,但如果您没有客户端从不同时区连接到您的数据库,您只是浪费了转换时间。仅这一点就可以让你加速15-30%。
在这种情况下,productcode索引是错误的。因为您按产品代码进行分组并使用其他列的位置,这不是非常有选择性的,优化器可能会认为使用产品代码索引可以加快速度。但是使用这个索引,它可以为您提供非常随机的索引查找扫描,但仍然具有相当多的行,而不是没有它的更快的顺序半完全扫描,而是使用order_date索引来限制扫描的行数。优化器根本不知道您可以预期行主要按磁盘上的order_date排序,而不是按产品代码排序。当然,要使order_date索引工作,您必须更改查询,以便每次比较使用order_date列名称位于=,<,>的一侧。或另一方面的BETWEEN和常数值,就像Bob Javis在他的回答中所建议的那样(给他+1)。所以你可能想尝试稍微修改一下他的查询,使用严格的日期格式并强制使用order_date索引 - 假设你拥有它,如果没有,你真的应该用
添加它ALTER TABLE `order` ADD INDEX order_date( order_date );
所以最终查询应如下所示:
SELECT thismonth.productcode,
(thismonth.ordercount-lastmonth.ordercount)/lastmonth.ordercount as riserate
FROM ( (SELECT productcode,
sum(quantity) as ordercount
FROM `order` FORCE INDEX( order_date )
WHERE order_date BETWEEN '2010-12-01' AND '2010-12-31'
GROUP BY productcode) as thismonth,
(SELECT productcode,
sum(quantity) as ordercount
FROM `order` FORCE INDEX( order_date )
WHERE order_date BETWEEN '2010-11-01' AND '2010-11-30'
group by productcode) as lastmonth)
WHERE thismonth.productcode = lastmonth.productcode
ORDER BY riserate;
不使用productid索引应该会给你一些加速(全扫描应该更快),并且使用order_date索引甚至更多,具体取决于order_date条件与表中所有行的满足行数。