− 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で表示する事を想定してみると とうていその都度集計では実情にそぐわない事が改めて理解されよう。

「市民の目」トップページへ戻る