从mysql订单表中获取上升速度但是太慢了

时间:2011-06-04 14:04:35

标签: mysql sql performance

我在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字段创建了索引。

我认为性能低下的原因是'GROUP BY',有什么不同的方式吗?

我尝试了你的答案,但所有这些似乎都不起作用,我想知道索引是否有问题(不是我创建了它们),所以我删除所有索引并重新创建它们,一切都很顺利 - 只需3-4s。我的查询和你的查询之间的区别并不是很明显。但真的非常感谢你们,我学到了很多东西:)

5 个答案:

答案 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'解决方案可能会解决您的速度问题。如果没有,或者您想尝试替代方案:

  1. 添加update_month以存储月份 of update_date
  2. 更新现有行的列
  3. 在update_month上添加索引
  4. 创建BEFORE UPDATE触发器 在行上设置update_month的值 更新
  5. 创建BEFORE INSERT触发器 在行上设置update_month的值 插入
  6. 相应地修改您的查询

答案 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而不是timestamp

如果使用时间戳的唯一原因是在插入和更新时使用自动默认值,请改用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条件与表中所有行的满足行数。