2

以下 SQL 在 MySQL 中运行非常缓慢。对于 250,000 行的表(跨越 3 年的时间线),这需要一个多小时。

select  L.order_date,   
        L.segname, 
        sum(O.product_total) as c_product_total,    
        sum(O.num_orders) as c_num_orders         
from report_PurchasesByOrderDate_Hour_bySegment as L 
join report_PurchasesByOrderDate_Hour_bySegment as O
    on L.order_date >= O.order_date
        and L.segname = O.segname
group by L.order_date, L.segname
;  

此查询为每个段名(段名)生成每个日期的累积总和。我已经用索引解释了它。

有人对如何重写它以在 MySQL 上正常工作有任何想法吗?(此查询在 DB2 中运行良好,但我必须为这个项目使用 MySQL。)

谢谢你的帮助!

Tadman 要求我添加包含索引的表定义。(诚​​然,我最初应该发布的,所以这里是:

create table report_PurchasesByOrderDate_Hour_bySegment
(
order_date            date not null,
hour_of_day           int not null,    
hourly_datetime       datetime not null,  
segname               varchar(10),
product_total         decimal(15,4), 
num_orders            bigint,    
PRIMARY KEY (hourly_datetime, segname),
UNIQUE INDEX (order_date, hour_of_day, segname),
UNIQUE INDEX (hour_of_day, order_date, segname)
);

注意:hourly_datetime 列实际上是多余的,我在测试另一个查询的左连接性能时将其放入。

感谢您的反馈。hour_of_day 确实在不同的查询中使用。出于测试目的,我添加了以下索引。(这两个中只需要一个,但我现在创建了两个,看看哪个 MySQL 会使用。)

create index test1 on report_PurchasesByOrderDate_Hour_bySegment (order_date, segname);
create index test2 on report_PurchasesByOrderDate_Hour_bySegment (segname, order_date);

这是 MySQL Workbench 中使用的解释的解释输出:

id,select_type,table,type,possible_keys,key,key_len,ref,rows,Extra
1,SIMPLE,O,ALL,"order_date,test1,test2",NULL,NULL,NULL,253519,"Using temporary; Using filesort"
1,SIMPLE,L,ref,"order_date,test1,test2",test2,12,wc_store.O.segname,1267,"Using where; Using index"

我已经在自己的笔记本电脑和 Amazon Managed MySQL 数据库实例上运行了它。两者的解释是相同的。

附带说明为什么 hour_of_day 子句也在预先存在的索引中。有另一个版本的 select 是按 hour_of_day 聚合的。它的性能也很差(更差),但我发布了两个中较简单的一个,因为上面第一个的解决方案(如果有的话)可以应用于更复杂的示例。另一个版本将“L.hour_of_day”添加到选择列表和 group by 子句中,并在连接中有以下 on 子句:

on L.order_date >= O.order_date
   and L.hour_of_day = O.hour_of_day
   and L.segname = O.segname

更新 cbranch:正确,目标是每个日期都有一个汇总所有先前日期的运行总计。我更改了查询以匹配您提供的查询,这对于区分 order_date 和 segname 是正确的。但是它并没有提高性能。鉴于 MySQL 有时在连接中使用的子查询存在性能问题,我继续为子查询的结果创建了一个临时表并在其上放置索引。所以这是新版本:

create temporary table tmp_order_segment as
   select distinct order_date, segname from report_PurchasesByOrderDate_Hour_bySegment;
create unique index tmp_1 on tmp_order_segment (order_date, segname);
create unique index tmp_2 on tmp_order_segment (segname, order_date);


select  L.order_date,   
    L.segname, 
    sum(O.product_total) as c_product_total,    
    sum(O.num_orders) as c_num_orders         
from tmp_order_segment as L 
join report_PurchasesByOrderDate_Hour_bySegment as O
    on L.order_date >= O.order_date
        and L.segname = O.segname
group by L.order_date, L.segname;  

不幸的是,这也没有提高性能。查询仍然运行了一个多小时。解释输出是:

id,select_type,table,type,possible_keys,key,key_len,ref,rows,Extra
1,SIMPLE,O,ALL,order_date,NULL,NULL,NULL,252264,"Using temporary; Using filesort"
1,SIMPLE,L,ref,"tmp_1,tmp_2",tmp_2,12,bsupply.O.segname,1,"Using where; Using index"

我在这个问题上尝试过的 MySQL 版本是:5.5.24 和 5.5.27。感谢您的任何帮助。

4

2 回答 2

0

您没有显示 的​​输出EXPLAIN,所以这只是一个猜测......

您有两个复合索引,看起来它们可能可用于此查询,但两个索引都包含hour_of_day不属于您的搜索条件的部分,因此可能会取消这些索引的资格。尝试将您的第一个唯一索引更改为以下之一:

UNIQUE INDEX (order_date, segname, hour_of_day)

或者

UNIQUE INDEX (segname, order_date, hour_of_day)

注意:如果其他查询需要现有索引,请添加新索引而不是替换现有索引。

编辑:

目标是生成一个汇总所有先前订单的运行总计吗?如果是这样,我认为您需要在加入之前进行分组。否则,您将表 O 连接到表 L 中的每一行(每小时),而不是每个日期、每个段的一行。看看这是否有意义:

select
    L.order_date,   
    L.segname, 
    sum(O.product_total) as c_product_total,    
    sum(O.num_orders) as c_num_orders
from
    (select distinct order_date, segname from report_PurchasesByOrderDate_Hour_bySegment) as L
    join report_PurchasesByOrderDate_Hour_bySegment as O
        on (L.order_date >= O.order_date and L.segname = O.segname)
group by
    L.order_date,
    L.segname
;
于 2012-10-27T14:07:46.403 回答
0

一般来说,大于比较不会使用索引,但会使用介于之间。
尝试这个:

...
on report_PurchasesByOrderDate_Hour_bySegment as O
    on L.order_date between O.order_date and now()
...

这具有相同的含义,但将使用索引order_date是否存在。如果不存在,则创建一个。

于 2012-10-27T18:52:20.470 回答