次の 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 です。ご協力ありがとうございます。