inosisibeyanの日常

私の日常についてカキコしたいと思います。

データベースの構築

2011-09-11 12:28:02 | データベース
私の永年、経験したプログラムをソフトウエアに進化すべく、作り方をオープンにしたいと思う。一読されて、これからデータベースを作ろうとされる方の参考になれば幸いです。背景を簡単に説明すると、現在、務めている会社で残業代を少し貰っただけで作成したプログラムなのだが、10年以上は安定して動作している今では外せないプログラムになっている。
仕事内容の詳細については、ここでは明かせないのが残念だが、処理の形態として言えばエクセル上の一ヶ月間の日々の生産予定データの入力されている表をテキストファイル化して、さらにデータベースの形に整え、データベースで細かいデータの加工処理を行う。その結果出来上がったのは、原料の使用量の予定データである。それをピボットテーブルとしてエクセルで作表する。途中、新製品や原料の変更などから来るマスターテーブルへの追加データがあればデータ入力が発生するが、ルーチン業務になっている製品に関してはデータの追加の打ち込み作業なしで処理を終える。生産数量のみの変更の場合も然り。

更にメインの変換方法を具体的に説明すると、テキストエディターMIFESのマクロであるMIL言語を用いてエクセルから抽出したCSV形式のデータから冗長性を欠除するべく加工(データベースのデーターとしてキーになるデータのユニーク化に役立つ処理)し、インポートエラーに繋がるデータも削除する。
通常は、データベース処理としては複雑になるテキストデータベース的な使い方でMIL言語での処理(例えば、全文検索でヒットしたデータの置換を特定データの数桁手前の数字を文字列の中から抽出する処理。アクセスでやるとSQLのネストやSQLの発行文字数が異常に増加する事になる)に向いていると思われる次月・前月に繋がる生産量などが入力されていた備考欄のデータの加算・減算などしている。(生産は連続しているので月ずれなどで生産予定がコロコロと変わるのだ。)

次は順を追って処理を見てみる。
1,抽出したいデータを指定範囲のみテキストデータに変換するエクセルのマクロを別ファイルに入れて元のエクセルのデータ表を参照する形で利用する。逆に、元の生産予定表は行列共に位置固定で作表していないとバグってしまう。実行させるマクロも変更不可にしている。これは別の心配事であるマクロウイルスにも強いと思う。
2,複数ページを一本化するために、ページ番号をデータの1桁目に入れ連結する。これはバッチファイルで作成可能。(コピーの順番を決め打ちしてるだけ。)

ここからMIFESのMIL言語で処理する。
3,バッチファイルで繋がったデータのレコード間でのコピーをMIFESのマクロで連続作成して、少しデータベースのデータの形に変わる。
4,備考欄の数量から加算・減算を前後の文字から条件処理する。
5,空白行の削除(データベースでも可能だが、出来るだけここでやっておく)エクセルから書き出す行数を決め打ちしてるだけ^^)
6,ゴミデータの削除(例えば、休日などを表す記号をデータ0として変換)
以下のMIL言語での作業は細かすぎるので割愛。

これらの作業を、矛盾なく高速で行うようにしている。

高速化の一番は、表示を止めるって事だが、エクセルでもMIFESでも当然やっている。見慣れていないと、フリーズ状態と間違うぐらいだ。
このフリーズに見える不具合への対策は、やりたい処理とは別の表示を使う事やフリーズと心配するであろう時間以内に終わってしまえば良い。私は高速を売りにしているので後者を選んだ。これらの処理で、データベースにインポートしてもエラーが少ないデータに変換されている。インポートエラーを吐いても、なんら支障はないがエラーは少ないに越したことはないと思う。(昔話で恐縮だが、エラーログが知らないうちに溜まり込んでしまい、それが原因でプログラムがクラッシュしたという事例をこのプログラムとは別のところで見たことがある。これは、外注したシステムだった事もありログのボリュームなどチェックしておらずクラッシュまで気がつかないのだった。)

ここからは、データベースの出番になる。データベースならどれでも使えるので、何でも良かったのだが会社の指定データベースがアクセスだったので、アクセスを使った。
開発当時、他のSQLで記述すると複雑になる、ピボットテーブル風のクロス集計クエリというのが目新しく、その機能を処理の途中で使うことにより、プログラム処理全体を単純化出来た。
他のSQLやマクロやVBAは、処理時間とデータの増減、処理の単純化の優先順位で適材適所に使うようにした。
SQLでもクエリーを使うと、初期設定では更新が複数同時進行する関係で当初はメモリーの限界まで使い切ってしまって、スワップファイルを使わざるを得ない状態だった。この対策は、クエリーの自動更新を1回のみにし、SQLとしてほとんどの部分をマクロやモジュールに記述して順次処理に変え対処した。同時にデータの件数の上限がほぼ同じといった処理になっていたので、上限を予想されるデータ量より少し緩く採った。これらの最適化でメモリー使用量が激減したようだ。(メモリー使用量のチェックで調べられる。)
追加、削除、更新クエリーも適材適所で使った。複雑で見難かったのが月の全日数分を1本化しているところで、31個の日数分のデータ変換をSQLに組み込んだが、これはこれで眺めて見ると単純なSQLだ。(アクセスのクエリーの編集にはデザインとSQLがあるが単純というのはSQLで見た時の事だ。念のため)
実際の処理としては、1から31まで並んだデータフィールド毎に、データがあれば日の1から31の日データと共に該当のデータを追加をするだけだ。

実際SQLの作り方としては、便利なデザインモードを最初は使い回して、細部の調整はSQLを決め打ちするのが、作業としては慣れもあるが早かった。
別のデータベース言語への変換をするなら、SQL表示モードでコピーしてバッチファイルなりに載せて動かすのがいいが、アクセスは方言がキツイので苦労することになるだろう。
何度か言語の変換を試みたが、途中に肝になっているクロス集計クエリが便利すぎて、言語の変換をやめてしまっている。便利と言っても、今回の案件にぴったり合っていただけだろう。
お勧めは、プロトタイプをアクセスで速攻で作って、それを別のデータベースに変換するのが良いと思う。標準と言う形でMSが勧めているSQLサーバーも候補に上がる。自動変換するソフトのもあるので楽だろう。(MSの回し者ではないのでそれ以上の説明はあえてしない私。)

処理時間は、当時CPUがペンティアムで、メモリー512MBほどだった我が社で使わせてもらっていたPCでの話。
反応の戻り(SQLを実行してから処理完了するまで)、1秒が壁になっているようで、1秒以上の処理は指数的に増加していたが、戻りが1秒以下になっているSQLコマンド、それらを集めて複数同時に実行しても(といっても20個も無いが)1秒以内で反応が戻ってきた。
これを実験的観察で知った私は、すべてのSQL処理を1秒以内に収めるべく試行錯誤したのだった。(これは、ほとんど趣味の領域になってしまっていたかも?)オラクルなどは、以前0.1秒以内を目標にSQLを最適化しているように聞いたことがある。

結局、完成したプログラム全体を見直すと、モジュールに内蔵したSQLとして8つほどになる。
処理を順に言えば、ファイルオープンと同時に旧データの削除と新データのテキストファイルからのインポートし、その後メニューを開き、各マスターデータとトランザクションデータ(ここではインポートしたデータのみ)の差分チェックをしたり、その後追加削除したデータがあるたびにマスター同士の差分チェックをメニューの項目を選ぶ事で確認や修正を行えるようにした。更に中間データをダイレクトに修正も可能にした。マスター全てとトランザクションに矛盾がなくなったら、出力データをセレクト(購入商品や転売商品などのデータを外すなど)して商品に必要な原料を、必要な日と該当商品、該当原料、担当部署別といった仕分けをピボットテーブルで出来るよう、冗長性を増した元データに加工する。(元とは最終出力表から見た元である。)
包装形態が違っても、製品名や種類が同じだと同一処方になるので、種類毎に集計した結果を(類)という一括りで出力するようにした。(関係ない話になるが私の担当する部署の生産品目では、この(類)が多数出現している。)
更に、元データとしては原料毎に各部署の使用量が分かると引当て時のチェックに使えると当時の原料引き当て担当者が言うので、その原料別(行)部署別(列)一覧表の元データも作った。(括弧内の行列はピボットテーブルでの位置関係)
この2つの元データをエクセルのピボットテーブルで参照すると、見やすく内容も濃い表が2つ出来るのだ。
途中までの処理は同一データなのだが、最後のピボットテーブルで見やすくという一点で、冗長性に差をつける為に元データ処理を分岐している。

開発当初を振り返ると、ピボットテーブルでの出力など最初は考えていなくて、手早く作ったデータベースの結果の表は商品名なしの原料のトータル数量のみ出力したものだった。こちらは、取り掛かってすぐに作れたのだが担当者には見難く使えないデータを出力した表になっていて、少々がっかりさせてしまった代物だった。
深く考えると生産が途中で変更されたりして、すでに引き当てている原料の消し込みなどが必要になるようで、個別にデータをチェック出来ないと折角のデータベースの仕事も無駄になってしまうようだ。実際業務では引き当て済みの原料にマーカーでチェックをしていたのを何度も見た。そういった点で、チェック表と個別の結果の表としてピボットテーブルに出力されていると、消し込みなども容易になる。

追加処理のプログラムなどについて
特定原料や特定商品は、更に細かい増減を知る必要があったので、連日の増減を予定生産量と期首の在庫量から追跡出来るようにしたり、在庫量が限られている原料や中間原料に関して、日々の引当量と在庫量の増減表を作ったり、棚卸しや税務報告に対して正確に作業する為に使えるような基礎データを作成したり、マスターデータのみを使ったものでは、原料の変更の影響を知るために該当する原料の製品一覧表(マスターデータの抽出とソートのみの表)を作ったりした。

プログラム作成後の仕事内容の変化について
ほぼメインのプログラムだけで原料引き当て担当者の仕事量が5時間/月ほど削減された。このプログラムを作るまでは全て電卓で計算していたのだ。さらに周りの追加処理のプログラムで、数時間/月の仕事を削減している。一番減らしていると思われる生産計画予定表を作成する担当の課長においては、シミュレーションとして利用が可能になり生産計画予定表作成後の影響のフィードバックを高速に行う事に繋がった。これで担当課長が異動してもこの生産予定表に関しての引き継ぎが容易になっているようだ。

今後の課題としては、今は生産計画からのデータを起こしている所を、販売計画からデータを起こせるようにする事。それが出来ると約1日かかっている生産会議自体が簡素化される事だろう。きっと人件費削減や周りに対しての影響も半端ない事になると思われる。それと同時に、1ヶ月単位で計算しているのを適時にリアルタイムで計算といった形にも変化が出来れば、必要量を必要な時に生産する事にも繋がると思う。

補足説明:プログラムの主な各部品点数など。
予定表 エクセルで10ページほど
マスターテーブル 20ほどをアクセスとエクセルの参照テーブルの形で持っている
クエリー(追加、削除、更新なども含む) 開発途中のソースとして保管しているのも合わせて200ほど
フォーム メインメニューと数点の期首データなどの入力画面
レポート チェック表や増減表など数ページ(後述の出力表とも、かぶってカウントしている。)
マクロ メインのデータベースで20ほど、追加プログラムでは各5個ほど
モジュール メインメニューのオープン、クローズ時の自動実行をマクロからCallして使用している2つと追加プログラム数点には必要なので使っており、全部で10ほどある。

出力表
原料予定表(ピボットテーブル 各部署毎で10ページほど。こちらは製品毎に生産順に作表している)
原料各部署毎予定表(ピボットテーブル 原料予定表と同じデータを1つに集約している表)
特定原料増減表(引き当てを任意に行えるようにエクセル表の半分にアクセスのデータを参照貼り付けしただけの物で作るのは速かったが、かなり使える物になっている。)
特定中間原料増減表(中間原料だけに、原料と製品の量に影響する。チェックがし易い点が売りだ。こちらは、アクセスのレポート形式で出力)
包装材料予定表(こちらはデータの流れとしては原料と似ているが、詳細に包材毎に一覧表を作っている点で違っている。現在の倉庫在庫のデータもフォローしている点で更に使いやすいかと思っている)

クエリーでは、その違いを明確にカウントしていないがプログラムの自己監査用のレポートや動作チェック用の物、試行錯誤の結果、現在は使わないのが得策になっているプログラムやテーブルなどルーチンの流れに載っていない物など合わせてシーラカンスのように化石化したものなどかなり残っているので、現実には森に木の葉を落とした様な部品構成になってしまっている。時間が有れば整理するべきなのだが。。。
そんな流れに載っていない部品を自動整理するような事が出来れば、アクセスはもっと使い出が有るように思える。
面白い使い方と思ったのが、レポートやフォーム上で見せないデーターをテーブルの替わりに利用して計算や処理を複雑化させられる所だ。これは、慣れないとチェックがし辛いし、流れが追いにくく見え難くなる原因にもなり兼ねないが、エクセルなど表計算を作るのに慣れていないと作れない機能だろう。これは、出力表の最後に紹介した特定中間原料増減表で活躍した機能でもある。作ったのはエクセルなどの裏で計算している部分に近いものになる、エクセルのデバック作業用に裏の動作が見れる物もあったと記憶しているが、無駄に見ても弊害が多いのを理由にして、ここでは詳細は書かない)

最後に開発を振り返って、今までクエリーでやっていた処理も、定型データとなる固定したデータに変えられるようならば、テーブルに置き換える。更に標準化や正規化などしてそのテーブルをデータ変更時のみ追加・削除した方が処理が高速化出来るので、そのへんの見極めが肝心だと思う。モンスターマシンのPCならば、あまり気にならないだろうけど。
アクセスのみで動作と言うのも有りだが、使いやすさや処理の見やすさを追求するとエクセルやMIFESなど向き不向きを考えて作るのも良いと思う。
反論覚悟で言うと、誰も分からないようなソフトを使って開発すると引き継ぎ不可になり兼ねないと何度か言われた事あったが、全体的に高度な処理をしているのでアクセスでもかなり使いこなしていないと意味不明なコマンドが並ぶことになるので、同じ事だと言える。最近のエクセルならば、似たような処理が単独で出来そうだとも思えるが、私の力量や趣味に有っていない事もあり変更はしていない。ここで紹介したプログラム以外で、単純なデータベースに関してだが今では陳腐化してしまって、アクセスでやる必要性がなくなっている物も出ている。昔はエクセルの扱えるデータの限界が小さい容量ということが原因だったのだが、今まではアクセスでやらざるを得なかった容量の数倍の容量でも余裕で扱えるようになっているエクセル。良い環境になったものだ。