苦労の末作り上げたAccess家計簿ですが,その後も少しずつ手直しをしています。でもやなんだ。ちょっと直すたびに新たなエラー出たりするし。日々使うツールのメンテってやだよね,サーバ管理者の苦労が少しわかるよ。この先どんなトピックが舞い込むかわからないので新しい記事ほど上に来る積み上げ方式をとります。
/第11話:絶対なんとかしてほしいのはクエリ/第10話:Null値と一定値/第9話:カレンダーコントロールの謎 (2)/第8話:カレンダーコントロールの謎 (1)/第7話:「10日問題」 /第6話:ブラウジングシステム,まずは完成/第5話:システムに踊らされるな/第4話:さらば日付形式,こんにちわテキスト形式/第3話:「レコードの検索」アクションを試す/第2話:FindFirstを試す /第1話:Callender Losing Control/
さてこのようにバージョンアップしたAccessマイ家計簿で,初めての月末集計日が来た。クリック一発即!・・・と思ったら結果は悲惨だった。つまり総空白。
いったい何が悪かったの?!先月分のデータを集計しなおしてテストしたときは,ちゃんと行ったのに!
ということは,先月と今月の出費パフォーマンスのどこに違いがあるかということである・・・ひとつ見つかった。今月は,なななななんとパソコン関係の出費がゼロだ!(本は買ったけど)それがnull値である場合の処理をしていなかった。まさかそんな場合を想定してなかったから・・・直した。だが,ここでそんなことを書いたのは,出費がゼロだったことに超驚いたことを書きたかったからだ。
実際の落とし穴は,意外なところにあった。それは日付。これまでNull値の処理をする代わりに,ただ日付と0を入力するだけのダミーファイルのようなものをいくつか作っており,当該月の日付(○月1日に統一)データをそのダミーファイルから借りてきて最終的に表示するという処理をやっていたというようないきさつがあった。そんなダミーファイルを利用したのは,他のファイルよりデータ量が圧倒的に少ないし万一壊れてもまあいい,みたいな軽い気持ちだった。だから覚えていなかったのである。他のファイルから借りてくることで,ちゃんと表示された。
クエリーさあ〜。一つのフィールドがNull値ってだけで,「表示クエリ」でさえろくすっぽできないっての,何とかならんかAccess?「ナントカフィールドのデータがないのでクエリを実行できません」でもいいじゃん!
マイAccess家計簿には他にも修正すべき個所がある。それらを全部修正しよう。
まず、月の集計システム。クエリにNull値が入った場合、演算の値が全然おかしくなる。そのため、「灯油」とか「趣味」とか「その他」とか、その月に入力項目がない場合は、ダミーとして毎月手動で0円を入れていた。無論入れ忘れると値はおかしくなり、ああ忘れた忘れたと言って手動で入れる羽目になる。それはもうやめよう。というわけで、これは簡単。演算で[食費]+[住居費]+[その他]・・・とする変わりに[食費]+[住居費]+Nz([その他],0)と関数を入れてやるだけだ。
最後、「家賃」は毎月定額であるから、これも手動で入れていたのだが、自動で入るようにしたい。ちょっといろいろなことを考えすぎたが、これは非常に簡単。いくつもの分類・集計クエリを経て最終的に足し合わせられるこの過程の、適当な集計のところ、いや集計の一歩手前の表示のところに(複数のクエリの結果を統合する場合、いきなり集計に入ると地獄を見る。まず複数のクエリの結果をひとつのテーブルに表示させるクエリをまず作ってから、それについて集計を行わなければならない)
家賃:**000
という列をつけるだけでよいのだ。これでいつも**000円という値の「家賃」というフィールドができる。ただし、このままだと書式がだーめである。これはフィールドのプロパティで「書式」欄をクリックすると、何もないはずの欄にオプションメニューが現れるので「通貨」を選ぶ。
ところが、もう一つ問題が起こった。今度はカレンダーコントロール自体の問題・・・フォームを開いたときのカレンダーの初期表示値が、2000年9月7日になっているのだ。VBAコードは、当然だがこのフォームがロードされたときには本日の日付がカレンダーに表示されるように書いてあるのに。
・・・・・・・・
これには、思い当たらないことはなかった。日付の書式でがちゃがちゃやっていたとき、カレンダーコントロールのプロパティを表示させると、「Value」という値があって、それが「00/09/07」になっていた。わたしはまさかこの日付を既定値にするという意味とは思わず、書式の一例と思い、これを「09/07/2000」とかにしていじったのち、やっぱちがうわと思って消去したがもう一度プロパティを上げるとゾンビのように復活していたりして少し気持ち悪く思っていた。問題はそれなんだろう。今プロパティを上げてみるとやはり「00/09/07」になっている。これを消去して保存終了してもう一度フォームを開く。やっぱりカレンダーは2000年9の月だ。だがプロパティのValueの値は消えている。
・・・・・
カレンダーコントロールを一度削除し、新規に貼りつけ、プロパティで前と同じ名前をつけ、イベントを確認した。今度はちゃんと今日の日付でカレンダーが現れた。
さてその新しいカレンダーのプロパティは、本日の日付になっている。2000年9月7日というのも、前のカレンダーコントロールを初めて導入した日の日付であろう。このカレンダーコントロールのプロパティには今後ずっと既定値が2001年6月10日という記述が残り、しかし挙動はVBAコードに従い開くたびにその日の日付が表示されるのだろう。
・・・と思ったら,翌日フォームを開いたら、やっぱりカレンダーには6月10日が臆面もなく表示されていた。
これで、わたしも決心がついた。あのプロパティってヤツをやめよう。コードだけにしよう・・・それで気がついた。読み込むときのプロパティ欄に「イベントプロシージャ」を指定してやればいいのだ。そうして新しいレコードに移動するというDoCmdをチョクでかます。これで、他の要素に惑わされることなく、開いたときに新しいレコードに移動しかつカレンダーは本日の日付を表示する。これですっきりした。そしてひとつ勉強した。
けど馬鹿野郎である。Accessのプロパティの設定とVBAステートメントで挙動がバッティングしているのだ。おまけにその警告すら出ない。今までは全然気づかずに使えてきたんだから,自動的にVBAコードの命令を優先していたということになる。それがちょっとカレンダーのプロパティをいじったらトラブりやがった。
全く同じ設定のはずなのに、Accessのプロパティの設定では実行されないことがVBAコードだと実行されたりするのは前にもあった。見ると、他のフォームの他のプロパティでも、VBAコードで書いた設定によりたまたま初めて開いたときに読み込まれた値が、規定値のまま設定欄に残っている。どうも,フォームを開くと変な日付のレコードが現れるってのは,この辺にからんでいるんだな・・・最悪の場合はこれにSQLまでからんでてんでバラバラな処理系統で勝手に動く。2000になってもこの問題は全く解決されていないようだ。XPだと、そんなことはないんでしょうか?!キャンギャルのおねいさんにきーてみようか?!きかない。
「フォームを開いたとき変な日付のレコードが表示される」問題は,「日記」フォームの他にも,「お買い物」つまりレシート金額の入力フォーム(マイ家計簿で一番大事なフォームだぞ!)にもあった。だがめんどくさがっていちいち新規レコード作成ボタンをクリックして入れていた。だがこれをやっぱり直したい。フォームを開いたとき新しいレコードを開くようにしないと。
フォームのデザインをいじる。「フォームのプロパティ」で「開くとき」という欄を突っつくと、「式ビルダ」か「コードビルダ」か「マクロビルダ」かを選べと言われる。マクロが一番簡単なので(コードビルダのほうがいいんだろーなホントは・・・)マクロビルダを選ぶと要するに新規マクロのデザインビューが立ち上がるから、「レコードの移動」アクションを選び、該当フォームの新しいレコードと指定する。で、このマクロに適当に名前を付けておく。
・・・とやって開いてみると、カレンダーコントロールが消えている。だが一度デザインビューにしてまたフォームにすると出る。
・・・・
こーゆーのやめてよねー。バックアップからフォームを読み直すと復活した。これをつけると・・・カレンダーコントロールがまた消えた。
こうなると、このプロパティがまずいんじゃないかと思わなければ主婦プログラマの名がすたる(そんな名で誰も呼ばない)。実はこのフォームのプロパティには「開くとき」と「読み込み時」というのがあって、この二つがどう違うのかずいぶん理解に苦しんでいたのだが、じゃあ似て非なるらしい「読み込み時」にこのプロパティを充てるようにしたら?
・・・・消えなくなった。バカヤローである。もちろん新しいレコードには移動している。してなかったらイルカのヤツカツシメ状態にするところだった。
サボり分サポートシステムのコードには「月日Dummy」というヘンな変数がある。それはマイAccess日記の最後のトラブルのせいだ。それは修正後2,3日後に起こった。どうも入力したはずの日記が入力されないことになっている。きのうもちゃんと日記つけたはずなのに,なぜ「3日分の日記をつけます」メッセージが・・・?答えは,今日は6月12日だったからだ!
きのうも実はそうだったのだが,1日くらいの差なので気がつかなかったのだ。検索の必要上,日記の日付は無理矢理テキストデータ化していた。となると並べ替えたとき,2000/6/9より2000/6/10のほうが若い数字と解される!!・・・
だせー。これって,2000年問題みたいなもんだよな。でも10日ごとに現れる「10日問題」・・・ださすぎる。
わたしがとった行動はこうだった。テーブル「Diary」に一列付け足し,「日付Dummy」というフィールド名にする。この空のフィールドについて,これまでの「日付」データを一気にコピー・ペーストする。そうしてから「日付形式」に変える。
最後に,フォーム「Diary」のプロパティの「並べ替え」を「Diary.日付Dummy」にする。
オブジェクトがねえとか言う。
しばしいろいろやって気がついた。この「日付Dummy」フィールドがフォーム上に割り付けられてねえからじゃねえ?割り付けて,かつプロパティで「不可視」にしてやる。これで・・・ほーれだまされた,ちょろいぜクソイルカ!
そうして,これからは日付は自動入力になるので,一日ずつ加算していくときにこの変数にも値を入れてやる。それがかのコードの意味なのだ。これでよ〜やく完成したというわけだ。
・・・それにしても,まあ手を変え品を変えいろんなところでトラブるもんだよな〜。
つまり,数日間日記をサボった後日記フォームを開いたとき,サボっていた分だけの日数,レコードを開いては日付を順番に自動入力するようにするのだ。
それには,本日の日付と、最後に入力したレコードの日付とを比較して、1からその日数差まで新規レコードを開き,日付を1加算し,それを入力するというFOR-NEXTループをつくればよい。
最後に入力したレコードはどうやって見つけるか?つまり「最後のレコード」ということになる。だがホントなるのか?!いまいち不安だった。
なぜって、日記を開くといつもヘンな日付のレコードが最初に現れるからだ。妙に日付が順不同になっているときもある。この勝手な並べ替えはなんでかわからないが,とにかく最後のレコードの日付が、最新の日付でなければ困る。
だが解決はついた。かなりあっさりと。フォームのプロパティウィンドウで、「並べ替え」という欄がある。これをいじれば・・・でもDiary.月日,DESCとあるぞ。なにこれ?
このDESCって・・・いつものお買い物入力フォームのときに出てくるヘンなエラーメッセージじゃないのか?!「キャンセル」とすると普通に動くから,いつもめんどくせえなと思いながらキャンセルしてきたが・・・
DESCを調べてみた。これがついていると「降順」なんだそうだ。取ると「昇順」・・・取ってみたら、あの「ヘンなところで開く」がなくなった。
あとは、DayAddとDayDiffという便利な関数を使うだけだ。しかしパラメータとしてdを与える場合""に囲まなければならないなど結構めんどくさかったが・・・もちろん、最終的にはちゃんと切って張ってテキスト形式に直してやる。それから、一日に何度も、単に見るだけのために日記を開くような場合は、日付の差がゼロ。こういうときは何もしないで、ただ最後のレコードに飛ぶだけだ。
一見非常にめんどくさそうなコードだったが、少しずつ結果をMsgBoxに出しながら書いていくと、かなりストレートに迷いなく書いて行けた。完成したコードがこれ。
実際には,フォームを開いたときに,このプロシージャを呼ぶ。そうしておいて,カレンダーの任意の日付をクリックして修正するなり見て笑うなりすればよい。これらのコードはフォームのコードビルダに記述しておく。
「運転免許合宿殺人事件」など強烈にくだらねえドラマを見ながら、とうとう目的のコードを完成した・・・これで、以後日記を書くときは、ブラウズも入力も思いのままである。未来の予定までは書けないが,そこまではいいです。
今日は喜びのあまり、おみそしるを作るのに下に降りたとき思いっきりインペリテリが聞きたくなりそれを聞いたが、アルバム1枚ビシっと聞き終わったあと、しばしの静寂のあと、「やだねったら、やだね〜」と箱根八里の半次郎を歌ってしまったのは,裏のオヤヂが畑仕事しながらかけてるラジオのせいか?!
フィルタ、という方法もある。フィルタアップされたレコード数が0なら新規入力、という・・・だが、これもアレだった。つまりApplyFilterアクションにはそういう戻り値がないのだ。アクションとメソッドは違うということなんだろうか。
そこでわたしは考えた。最後の手段としては・・・毎月一ヶ月分くらい、日付だけを入れたレコードを作ってしまっておくのだ。そうすればクリックしたらそこに行くだろう。
だが、ものすごくダサい方法である。わたしはこの改良を済ませたら、今度は今までシステムの欠陥を補うために手入力でダミーレコードなんかを入れていたのをやめられるように,毎月の定額引き落とし値などの自動入力やNull値の扱いなどに踏みいるつもりなのだ。それを同じようなダサダサ手入力を一件増やすなんて・・・だが、もしこれがExcelのオートフィル機能だったら、もうやっちまっていたかもしれない。それを思いとどまらせてくれたのは、Accessのテーブルにそういうことはそう簡単にはできないということだった。
けど、日付の自動入力なんて、なんか方法がありそうだよな・・・と見たのは、久しぶりに見た昔のAccess本だった。もう2000になったしさすがに卒業したと思っていたこの本、それもシリーズの「裏技編」ではなく「中級編」にあったのだ。「日付の自動入力」・・・もっとも、それ自体は規定値に今日の日付を入れるというだけのことだったが。最悪の場合それでもいいか。必ず毎日、日記を開くようにすれば・・・
いや、だめだめ。ソフトに人間の生活を合わせるようじゃダメなのだ。人間の生活に合うようにソフトを作らなければ。 2,3日日記をサボったとしてもちゃんとフォローしてくれるようなシステムをつくるのだ!
検索条件にはStringを入れろとFindRecordでもFindFastでもしつこく書いてある。とするとこの日付の書式がまずいんじゃないのか。
なにせクエリビルダのデザインビューなどで日付を01/05/07と入力しておいて、SQLビューで見ると#05/07/01#と勝手に書き変わっているというのは月の集計システムを作ったときからヘンに思っていたことだ。Accessのヤツ、親切ごかしに日付の入力形式を勝手に関数化かなんかしてるんじゃないか?・・・
やはり、日付は最初からdd/MM/yy形式で入力するようにしたほうがいいだろうと思った。だがその設定方法はどうするのか。テーブルデータならデザインビューで「書式」のところにそう書いてやればいい。だがちょっとなにか操作するとすぐyy/MM/dd形式に戻ってしまう。
このヘンな初期設定を変える方法は、結局「地域のプロパティ」だった。ここの日付タブの形式を変えてやればいいのだ・・・それでも検索はうまくいかない。やはり、テキストにするのが一番安全だ。
まず、日付をクリックして得られる値を、もっとハッキリテキストに替えてやらなければ。
Str関数というのはある。数字をテキストに直すヤツだ。だが日付には無効らしい。
結局,Year,Month,Day関数三羽ガラスを使ってどうやらDate型らしいこの値をバラバラにして、"/"ではさんでつなげてやる方法をとった。そうして見てみると・・・おやおや、2001/5/7と出力されているではないか。
ならば今まで入力してきた日付のデータも,全部テキスト化してやらなければならない。つまりDiaryテーブルのデザインビューを開いて、「データ型」を「日付」から「テキスト」にする・・・支障はなかった。ただ、ケツにつまっていた値がアタマのほうに移動したのでテキストになったとわかった。だが01/05/07となってる。上の記述形式と違うじゃん。
直すしかないよな。これはやっぱり、検索置換でしょ。テキストのいーところさ。と、ファイルメニューから「検索」を選ぶ。念のためまず検索だけにしてみる。「99」という文字列を検索・・・
ないらしい。 部分・・・じゃだめなんですか? 「99* 」なら、ちゃんと見つかった。見つかったはいいけど、これを1999に直そうとすると、ただの「1999」になる。そりゃそうだな。
どーも、わたしがやりたいことをやってくれないぞこれは。
じゃあ、一旦日付形式に戻して、その形式をyyyy/M/dにしてやる。おお、全部直った。じゃあこのままテキストにして・・・
また、01/05/07に戻った。
な〜ん〜で〜とは、あまり思わなかった。やっぱりなんか細工してる〜と思ったのだ。この辺はだいぶわかってきたというか、疑い深くなってきたと言えよう。
わたしが取った行動はこうだった。さっき元に戻した地域のプロパティをいじって、日付をyyyy/M/d形式に指定した。そうしてテキスト化した。これでちゃんとお望みの形になった。
こんなことを勉強するとは思わなかった。だがのんのんに「それはパソコンではなくWindowsに詳しくなったということなのだよ」と言ってもらった。確かに、これから日付の問題が生じたときにはこの経験が強い味方になることだろう。ちょっと悲しいけどな。
さて、そうやってゴリゴリテキスト化した両者を、Subプロシージャ化で、ちゃんと「月日コントロールへ移動」を先に置き,「表示形式で検索」引数をつけて、FindRecordアクションをかますと・・・は、じ、め、て、うまく行った。つまり、カレンダーコントロールの過去の好きな場所をクリックすると、そこにレコードが移動するようになった。
だが、未記入の日付をクリックすると、レコードが動かないまま何事もなく終わる。イマイチである。
実はFindFirstの前に,Accessマクロビルダにちゃんと用意されている「レコードの検索」アクションを試してはいる。だがこのメソッドには致命的な欠陥があることがすぐにわかる。戻り値がないのだ。つまり、「検索できない場合」の条件分岐ができない。唯一の条件は「On
Error」である。つまり検索条件を満たすものがない場合はエラーになるかと思ったのだ。で、やってみた。最初これで行けるかと思ったが、それは単にコードそのものにエラーがあったというにすぎなかった。コードを直すと,検索条件を満たさない日付を入力した場合,ただの沈黙。
それはそれとして,せめて検索条件範囲内でのレコードの移動制御だけでもできないか?・・・マクロビルダだとどうせ「変数を検索語に充てる」ことはできないのは集計システムの制作時にすでにわかっていることだ。
それでまずは具体的な値を入れてちゃんと動くことを確認し、VBコードに書き出して検討するいつもの方法を試す。
マクロにしたりVBにしたり、いろいろやっているうちに、いくつかのことはわかってきた。「レコードの移動」アクションの引数の指定では,「表示書式で検索」を「はい」にすることが必要。あと「レコードの検索」アクションの前にコントロールを「月日」フィールドに移動させるアクションをつける。これらはイルカに言われたことである。たまには役に立つな野郎・・・だが,それでもクリックした日付のレコードは表示されない。
いじっているうちに、どーも、この「日付形式」というのが怪しいなというのはいじればいじるほどハッキリしてきた。
やるべきことを整理してみよう。カレンダーコントロールは一つ。これをクリックして、すでに記されている日付だったらそこにレコードを移動し、まだ未記入であれば新しいレコードにうつりその月日の欄にクリックした日付が挿入されるというシステムだ。
さてレコードの検索ってどうするのかな?ずいぶん前から,イルカには頼らないことにしてある。「レコードの検索」なんて簡単なキーワードすら理解しやがらねえからだ。ただかわいいんで放し飼いにするだけで,プルダウンメニューからVisualBasicヘルプを開いて「キーワード検索」を使う。
FindFirstというメソッドがあるようだ。これだと目的のレコードに移動できるらしい。そして目的のレコードがなければNoMatchというプロパティを返し、その場合は新規レコードに移動してかつクリックした値が入力されればよい。
だが・・・それにはRecordsetというオブジェクトを確立しなければならないようなのだ。これには該当フォームのコードでMe.Recordsetとすれば得られるようなのだが・・・何かうまくいかない。なぜうまく行かないか?いろいろ試してみて、どうもNoMatchであるためということがハッキリした。それまでわからなかったのは、最終目的を出すためのプロシージャを直しては漫然と試していただけだったので,日付のクリックにより発生するはずのRecordsetオブジェクトの取得やFunctionプロシージャのコールが無効なのか、そういった処理がされているがNoMatchなのかがわからないでいたのだ。ちょっと作り替えてNoMatchの場合はNoMatchというメッセージを出すようにしてみた。すると出るではないか。ていうか段階ごとに結果を見ていくのはプログラミングの基本だぞおまえ。とにかく日付をクリックすることにより得られる値がうまくないということだ。
Access家計簿を完成してブイブイふかしていたわたしだが,重大でもないんだけど早くなおしたほうがいいなあという問題が見つかった。
それは日記入力フォームである。配置したカレンダーコントロールでカチっとやって入力。いちいち日付を入力しなくていいのだが、わたしが日記を見ているときに夫がやってきて、「へえ、ちゃんとつけてるじゃん」と言ってカレンダーの日付をカチっ。
「ああ〜、だめ〜」
とわたしは思わず言った。ちょうど見ていたページにかかれていた日付の値が、それで変わってしまったのだ。
「え?なんで?」と夫。
「なんでって、これは入力用のカレンダーなの〜」
「わかってるよ。だが・・・ええー、ブラウズはできない〜?!」
夫の感覚では、そのカレンダーをカチっとやれば、過去の日付だったらその日のページがブラウズできるものだと思っていたのだ。確かにそう言われれば、ちまたのPDAなどではみなそうだ。だが、わたしの日記のカレンダーコントロールは、「日付」の欄に日付を入力する機能しかない。今日の日記を開いたところで、カレンダーで昨日の日付をクリックすると、昨日のページにはならず、代わりに今日のページの今日のはずの「日付」が昨日に変わってしまうのだ。
夫大笑い。「だせ〜よそれ。ださすぎ」
確かに。だが、わたしはそれまで不便を特に感じていなかったのだ。とにかく入力中心に考えていたので・・・ヒトに見てもらう、使ってもらうということは大事だと思った。
直すことにしよう。