2

次の SQL は、MySQL で非常に遅く実行されます。250,000 行のテーブルに対して (3 年間のタイムラインで) 1 時間以上かかります。

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
;  

このクエリは、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 は実際に別のクエリで使用されています。テスト目的で、次のインデックスを追加しました。(必要なのは 2 つのうちの 1 つだけですが、ここではどちらの 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 内で使用される Explain からの Explain 出力です。

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 句が既存のインデックスにもある理由については、補足として。hour_of_day で集計する select の別のバージョンがあります。パフォーマンスも悪い(悪い)ですが、上記の最初の解決策(ある場合)をより複雑な例に適用できるため、2つのうちの単純なものを投稿しました。もう 1 つのバージョンでは、select リストと group by 句に「L.hour_of_day」が追加され、結合に次の 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;  

残念ながら、これでもパフォーマンスは向上しませんでした。クエリはまだ 1 時間以上実行されています。説明出力は次のとおりです。

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

一般的に言えば、大なり比較はインデックスを使用しませんが、間は使用します。
これを試して:

...
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 に答える
0

からの出力を表示していないEXPLAINため、これは単なる推測です...

このクエリに使用できるように見える2つの複合インデックスがありますが、両方のインデックスにはhour_of_day検索基準の一部ではないものが含まれているため、それらのインデックスが不適格になる可能性があります。最初の一意のインデックスを次のいずれかに変更してみてください。

UNIQUE INDEX (order_date, segname, hour_of_day)

また

UNIQUE INDEX (segname, order_date, hour_of_day)

注: 既存のインデックスが他のクエリに必要な場合は、既存のインデックスを置き換えるのではなく、新しいインデックスを追加してください。

編集:

以前のすべての注文を合計する現在の合計を生成することが目標ですか? もしそうなら、参加する前にグループ化を行う必要があると思います。それ以外の場合は、テーブル O を、日付ごと、セグメントごとに 1 行ではなく、テーブル 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 に答える