2− 9 MySQLのデータを集計する |
今回は、すこし志向を変えて 実際のデータ処理に取り組もう。以前、「MySQLの実際に使うのはどうか?」みたいな事を書かしてもらったが、あれからずいぶん時間もたって MySQLを使うのが現実的になってきたように思われるからだ。以下のような二つのテーブルを準備し、内部にデータを入れ込んである。
show fields from TP1101;
Field Type Null Key Default Extra kigyo tinyint(2) unsigned zerofill YES 00 mise tinyint(2) unsigned zerofill YES 00 store smallint(3) unsigned zerofill NO PRI 000 busdt date NO PRI 2009-01-01 scancd char(13) NO PRI evcd mediumint(6) unsigned zerofill YES 000000 bumon smallint(4) unsigned zerofill YES 0000 suryo mediumint(6) YES 0 uriage int(9) YES 0 genka decimal(9,2) YES 0.00 dscqty mediumint(6) YES 0 dsprat int(9) YES 0 evsu mediumint(6) YES 0 evuri int(9) YES 0 show fields from TMAST;
Field Type Null Key Default Extra kigyo tinyint(2) unsigned zerofill NO PRI 00 scancd char(13) NO PRI hinmei varchar(40) YES NULL bumon smallint(4) unsigned zerofill YES 0000 bunrui smallint(4) unsigned zerofill YES 0000 lbun mediumint(6) unsigned zerofill YES 000000 vdrcd mediumint(6) unsigned zerofill YES 000000 genka decimal(9,2) YES 0.00 baika int(7) YES 0 読んでもらえばわかるように、単品の販売データと、商品のマスターである。このデータは 今回Linux(Centos5.3)のサーバーに入れ込んで これをVisualBasicで操作しようという志向だ。LinuxサーバーでMysqlを動作させる場合は、ご存知のようにメモリーの容量によって動作が左右されるようだが、今回はテストの為 WindowsXPで使用していた通常のディスクトップPCを使用している。(メモリー512KB,Pen4)
サーバーのスペックがそれほどでもないので、このデータをPHPで見る際に時間がかかってしまうだろう。したがって事前に分類別の販売データと小分類別の販売データをテーブルを作成して準備しておこうというものだ。なお、マスターのbumon・bunrui・lbunがそれぞれ部門・分類・小分類と考えていただきたい。最初のステップとして、LinuxサーバーにTeraTermで接続し SQL文を書いてデータを取りだして見ることにする。以下のようなSQL文を作って動かしてみた。
mysql> select t.kigyo, t.mise, t.busdt, t.scancd, t.bumon, m.bunrui, sum(t.suryo)
from TP1101 t, TMAST m
where t.scancd=m.scancd
group by t.kigyo, t.mise, t.busdt, t.bumon, m.bunrui;
単品の販売データテーブルと商品マスターをScancdをキーにして連結し 集計しようとするものだが、実行してもまったく返答が返ってこない。多分10分は待った筈だが、諦めてキャンセルしてしまった。あまり遅い為に、一度ふたつのテーブル共にanalyzeしてみた。
次に、以下のようなSQL文を作って試してみた。
mysql> select t.kigyo, t.mise, t.busdt, t.scancd, t.bumon, m.hinmei
from TP1101 t,TMAST m
where t.scancd=m.scancd and t.mise=1 and t.bumon=10;
kigyo mise busdt scancd bumon hinmei 03 01 2011-01-07 3014260274917 0010 マッハシンスリーターボカエバ4コ 03 01 2011-01-07 3014260274924 0010 マッハシンスリーターボカエバ8コ 03 01 2011-01-03 3014260328337 0010 フュージョン5+1替え刃4個 03 01 2011-01-06 3014260328337 0010 フュージョン5+1替え刃4個 03 01 2011-01-03 4056800841820 0010 ツープラスワンクリーム 6G 03 01 2011-01-05 8809171319858 0010 ドドメタリックライナーML4 3802 rows in set (9 min 11.06 sec) なお、データは一部抜粋しています。(以下同じ)
このように設定してみても時間が9分もかかってしまっている。なぜか?
(申し訳ありません。ここまでの部分はSQLの書き方講習会もたいな感じになってしまってます。どこでVisualBasicが出てくるのかと思われるでしょうがしばらくお待ち下さい)。
結果を話してしまうと、原因は元テーブルの持ち方にあって 主キーがKigyoとScancdの二つあるにも関わらず、片方のみしかWhere文に書いていなかった為だ。以下のようにSQL文を書き直してやると、
mysql> select t.kigyo, t.mise, t.busdt, t.scancd, t.bumon, m.hinmei
from TP1101 t,TMAST m
where t.kigyo=m.kigyo and t.scancd=m.scancd and t.mise=1 and t.bumon=10;
2450 rows in set (1.12 sec)となり、2450行を取得するのに1秒しかかからない事がわかる。SQL文を書く時に 主キーを正しく設定しなかった為に全データを検索するようになっていたのだ。「こんな事」と思う勿れ。スピードが1000倍も違うのだ。これは驚きの結果である。抽出された件数が違うのは 企業毎に登録されたScancdが違う為であるが、一部マスターに存在していなくても販売がある商品が存在する事も考えられる。これを、マスターに無いからと切り捨ててしまうと、そのデータが分類毎の集計に載らなくなってしまう。これは拙いだろう。どうするか?
これに対応する為に「右外部結合」というのがある。以下のようにSQL文を書くのだ。
mysql> select t.kigyo, t.mise, t.busdt, t.bumon, m.bunrui, sum(t.suryo)
from TMAST m right outer join TP1101 t
on m.kigyo=t.kigyo and m.scancd=t.scancd
where t.busdt>='2011-01-04' and t.busdt<='2011-01-06'
group by t.kigyo, t.mise, t.busdt, t.bumon, m.bunrui;
これは、右側に書いたテーブルの全てと結合する左側のテーブルを表示するというものだ。実行してみると 以下のようになる。
kigyo mise busdt bumon bunrui sum(suryo) 05 35 2011-01-05 0010 NULL 37 05 35 2011-01-05 0010 1606 1 05 35 2011-01-05 0010 1610 393 05 35 2011-01-06 0001 NULL 1 05 35 2011-01-06 0001 1601 110 05 35 2011-01-06 0002 1602 63 27564 rows in set (39.05 sec)
このようにデータが取れ、集計も可能だ。Bunruiの列のNULLはマスターに無いが 売り上げのあるものを集計している部分だ。
ただ、データ取得に40秒ほどかかってしまっている。マスター30万件とデータおよそ100万件を準備しているので、この位の時間はかかるものなのかもしれない。サーバーの性能の問題もあるかもしれないが、これをPHPで表示する事を想定してみると とうていその都度集計では実情にそぐわない事が改めて理解されよう。