Part7 データベースにまつわる怖い話
初公開日:2007/03/29
怖い話 その1 終わらないバッチ処理
|
![]() |
| 図1●売り上げ集計処理の流れ |
テーブル設計を見せてもらうと図2のようであった(詳細は省略してある)。こちらも別に難しいところはないが,ちょっと不安になった。テーブル間の関連,つまり外部キーなどが一切設定されていないのである。図2のようなテーブルであれば普通,店舗テーブル,商品テーブルと売り上げテーブルが「1対多」の関係にあるのは明らかである。しかし,これらのテーブルには何の関連も設定されていない。
![]() |
| 図2●売り上げ集計に利用するテーブルの設計 |
実際のバッチ処理のプログラムを見せてもらって,がく然としてしまった。集計処理のプログラムで,ORDER BY句を付けてSELECTすることによってソートした結果セット(カーソル)を作成した後,カーソルの各行に対してループをまわして加算することで合計を求めていたのである(図3)。
![]() |
| 図3●売り上げ集計処理のロジック [画像のクリックで拡大表示] |
しかし,そのような集計処理を行わなくても,集合関数(SUM)とGROUP BY句を使えば,グループごとの集計を一つのSQL文で計算させられるのである。そのことを担当者に告げると「えっ,SQLってそんなこともできるんですか!」と驚く始末。
ストアドプロシジャでカーソルを使うような“凝った”プログラムを書いておきながら,SQLの基本的な関数を知らなかったのである。集合関数を使って処理を書き直すと,数十行あったプログラムが数行で済んでしまった。
しかし,プログラムを実行してみると,今度はまったく応答が返ってこなくなってしまった。実は,GROUP BY句を付けた処理は,データベース管理システム(DBMS)内部でソートを行っている。今回対象にしたテーブルは100万レコードもあるため,全件を対象としたSQL文を実行すると,膨大な計算量になってしまう。加えて,図2のテーブル設計では,検索対象のデータ項目にインデックスが付けられていない。
そこで,商品コードにインデックスを作成し,商品コードごとにデータを区切って集計するSQL文に書き替えた。その結果,処理時間は大幅に短縮され,半日たっても終わらなかった処理が1時間半で終了するようになった。
この件は,バッチ処理のSQL文の書き方やインデックスが作成されていないのが直接の原因であった。だが,もっと根本的な原因は,RDBの特性を知らずに設計してしまったことにある。全体として,汎用機アプリケーションでファイルを順次処理するようなイメージで設計してしまったためであろう。不必要な中間テーブルが多数あり,無駄なバッチ処理が数多くあった。