全国駅名一覧フォーム、銀行支店名一覧フォームエクセルVBAの解説ホームページは諸先輩たちのすばらしい仕事がすでにたくさん存在しています。そんな中であまり紹介されていないもので便利な技を紹介していこうと考えました。Accessでは難なく表現できることがExcelでは複雑な処理となる、あるいはExcelでは簡単に出来ることがAccessでは出来ない。そんな経験に思い当たる方がいらっしゃるのではないでしょうか。表計算ソフトとデータベースソフトの違いを実感するのはそういったときです。第一回目はExcelを使った「Accessまがいの検索フォーム」。実は同様の処理はAccessにおいてはそれほどむずかしいことではありません。それを今回はExcelを使ってやってみようというわけです。ユーザーフォーム上に二つのコンボボックス12があります。1のコンボボックスを開くと全国の鉄道路線が表示され、2のコンボボックスを開くとその路線の全駅が表示される、そのようなマクロを書いてみようと思います。シートは「鉄道」と「駅」の2枚用意します。http://aporia.toypark.in/modules/d3downloads/index.php?cid=1貴重なデータを提供してくださっているスナフキんさんとDoratheraさんにこの場を借りて心よりお礼申し上げます。ブックを開くとアイウエオ順に並んだ「駅」シートが表示されます。シートの上でダブルクリックすると「検索」フォームが表示されます。「▼」ボタンを押すと・・・。ドロップダウンリストが現れます。「総武本線」を選んでクリックします。「総武本線」と入力されました。同様に「駅」のドロップダウンリストを表示させると・・・。総武線の駅のみが表示されています。「千葉」を入力しました。フォームを閉じるとシートはオートフィルター画面になっています。シートの10000行目にフィルターにかけられた総武線の駅名がコピーされています。ComboBox1に表示される鉄道シート。Workbook_Open()’ブックを開いた時のイベントプロシージャ。True’鉄道シートを再表示。Worksheets("鉄道").Unprotect’鉄道シートの保護を解除。"=COUNTA(B2:B65536)"’鉄道シートのD1セルに件数を数える「COUNTA関数」を記入。ActiveSheet.Protect’鉄道シートを保護。False’鉄道シートを非表示。Worksheets("駅").Select’駅シートを選択。ActiveSheet.Unprotect’駅シートの保護を解除。"=COUNTA(B10001:B20000)"’駅シートのM1セルに件数を数える「COUNTA関数」を記入。ActiveSheet.Protect’鉄道シートを保護。Boolean)’シート上でダブルクリックしたときのイベントプロシージャ。UserForm1.Show’ユーザーフォームを表示。CommandButton1_Click()’閉じるボタンをクリックした時のイベントプロシージャ。UserForm1.Hide’ユーザーフォームを非表示。Range("A1").Select’A1セルを選択。Integer’変数を宣言。String’変数を宣言。Worksheets("鉄道").Range("D1").Value’変数「最終行」に鉄道シートのD1セルの値を代入。表示範囲’コンボボックス1のRowSourseに変数「表示範囲」を代入。ComboBox1_Change()’コンボボックス1を変更した際のイベントプロシージャ。String’変数を宣言。’変数「条件」へコンボボックス1のテキストを代入。False’画面を停止。Worksheets("駅").Select’駅シートを選択。ActiveSheet.Unprotect’駅シートの保護を解除。"=COUNTA(B10001:B20000)"’駅シートのM1セルに件数を数える「COUNTA関数」を記入。Range("A10000:Z20000").Select’セル範囲を選択。Selection.ClearContents’選択範囲をクリア。Range("A1").Select’A1セルを選択。Selection.AutoFilter’オートフィルターをかける。Criteria1:=条件’変数「条件」で第五列を検索。Range("A1").CurrentRegion.SpecialCells(xlCellTypeVisible).Select’抽出範囲のみを選択。Selection.Copy’選択範囲をコピー。Range("A10000").Select’セルを選択。ActiveSheet.Paste’貼り付け。ActiveSheet.Protect’駅シートを保護。False’コピーモード解除。True’画面停止を解除。ComboBox2_Enter()’コンボボックス2に入った際のイベントプロシージャ。Integer’変数を宣言。String’変数を宣言。Worksheets("駅").Range("M1").Value’変数「最終行」に駅シートのM1セルの値を代入。表示範囲’コンボボックス1のRowSourseに変数「表示範囲」を代入。アドインをWeb上で提供しております。それをダウンロードすると、例えばAccessのテーブルでは郵便番号を入力してEnterを押せば、別のフィールドに住所が自動入力されます。リレーショナルデータベース・ソフトであるAccessでは、同様の結果がフォーム上でも実現できます。Excelの場合、アドインが公開されてはいるものの、フォームのテキストボックスでAccess並にこれを利用することは難しいようです。そこで今回は郵政公社のダウンロードデータを利用して、私が「顧客管理名簿」の中で実際に使用している住所変換マクロをご紹介します。Microsoftの郵便番号は更新が不定期(ってゆか、数か月に一度)であるのに対し、郵政公社はさすがに毎月更新しています。http://www.post.japanpost.jp/zipcode/尚、私のサイトでは「顧客管理名簿」のユーザーのために、コピーしてそのまま使えるよう郵政公社の最新データを加工したものを、適宜用意いたしております。以下の場所に置いてありますのでご自由にダウンロードしてください。今回のサンプルでは、全国の郵便番号データを2枚のシートに持たせますので、かなりの「チカラわざ」に属するマクロになっている、ということをご理解ください。顧客名簿などの帳票においては大いに力を発揮しますので、是非ともお使いになることをお勧めします。全国の郵便番号を1枚のシートに持たせることは無理なので東日本と西日本に分けました。ちなみに、Excel2007では1枚に持つことが可能です。Userformを作り、その上にTextBox2個とCommandButton2個、それとLabelをひとつ配置します。シートに配置した住所検索ボタンを押すとUserform1が表示されます。TextBox1に郵便番号を入力し、検索ボタンを押すと。TextBox2に、途中までの住所が表示されます。該当する検索値がない場合。CommandButton1_Click()’検索ボタンを押す。String’変数を宣言。Range’変数を宣言。Range’変数を宣言。TextBox1.Text’変数「検索値」にTextBox1のテキストを代入。False’画面を停止。True’東日本シートを再表示。True’西日本シートを再表示。Worksheets("東日本").Select’東日本シートを選択(省略可)。.Range("A1:A65536").Find(検索値)’Range("A1:A65536")の該当検索値をオブジェクト変数「検索セル」に代入。Then’検索セルに該当がない場合。Worksheets("西日本").Select’西日本シートを選択(省略不可)。Worksheets("西日本").Range("A1:A65536").Find(検索値)’Range("A1:A65536")の該当検索値をオブジェクト変数’「検索セル2」に代入。Then’検索セル2に該当がない場合。"検索値は存在しません。"’ラベルにメッセージを表示。Else’検索セル2に該当があったら。2)’該当した検索セル2の2列目をTextBox2に表示。""’ラベルにメッセージを表示しない。Worksheets("東日本").Cells(検索セル.Row,2)’該当した検索セルの2列目をTextBox2に表示。""’ラベルにメッセージを表示しない。False’東日本シートを非表示。False’西日本シートを非表示。True’画面停止を解除。キーワードを次々に検索するこのページのトップへキーワードを含むシート上のセルを次々検索していきます。サンプルコードではアクティブセルの位置から下へ向かって(列方向に)検索されます。その列が完了すると右側の列に移ります。データを複合条件で抽出するは同じデータを使って作りました。これらをご覧になれば、ExcelにおいてもAccessに負けないデータベースプログラムが構築できることがお分かりになると思います。ブックを開くと「連続検索」ボタンと「絞り込み抽出」ボタンがあります。「連続検索」ボタンで「曖昧連続検索」フォームを表示させます。「鈴木」で「検索」ボタンを押すと最初の「鈴木」へ跳びます。「次へ」ボタンで次の「鈴木」へ跳びます。ボタン2_Click()’ボタン2をクリックしたときのイベントプロシージャ。UserForm9を初期化(空白)にする。False’ボタン「次へ」を使用不可に。"検索値を入れて検索ボタンを押してください。"’Label1のキャプションを変える。vbModeless’シート変更可能な状態でUserForm9を表示。検索_Click()’「検索」ボタンをクリックしたときのイベントプロシージャ。String’変数を宣言。errhandler’この行より下でエラーが起きたら場合はerrhandler:の行へ跳ぶ。TextBox1.Value’変数「検索値」にTextBox1の値を代入。"検索する語句を入れてください。"’メッセージを表示。TextBox1.SetFocus’TextBox1にカーソルを移動。False’ボタン「検索」を使用不可に。True’ボタン「次へ」を使用可に。"次をさがすには「次へ」ボタンを押してください。"’Label1のメッセージを変える。Err.Number’エラーコードが次のケースのとき。91’エラーナンバー91=検索値が存在しない場合。"その検索値は存在しません。"’メッセージを表示する。次へ_Click()’「次へ」ボタンをクリックしたときのイベントプロシージャ。Cells.FindNext(After:=ActiveCell).Activate’次を検索。閉じる_Click()’「閉じる」ボタンをクリックしたときのイベントプロシージャ。UserForm9.Hide’UserForm9を非表示に。データを複合条件で抽出するこのページのトップへ今回は、ひとつのユーザーフォーム上で複数の条件で「絞り込み検索」を行います。ここまで来ればExcelがAccessと何ら変わらないデータベース機能を持っていると実感できるはずです。3.Userform1のプロシージャで、ComboBox1から3の値は変数にすることも可能です。そんなわけでサンプルファイルでは変数を用いてます。変数って使い過ぎると訳が分からなくなりますよね。σ(^_^・・・アルファベットの変数って嫌いだなぁ(笑)。条件で「検索」ボタンを押してみます。データが抽出され、フォームには件数が表示されます。Workbook_Open()’ブックを開いた時のイベントプロシージャ。ActiveSheet.Unprotect’顧客名簿シートの保護を解除。="=SUBTOTAL(3,A3:A65536)"’顧客名簿シートのR1セルに、「SUBTOTAL関数」を’引数に「3」を選んで記入(抽出後の件数が表示される)。ActiveSheet.Protect’顧客名簿シートを保護。ボタン3_Click()’ボタン3をクリックしたときのイベントプロシージャ。UserForm1’UserForm1を初期化します。vbModeless’シート変更可能な状態でUserForm1を表示。検索_Click()’「検索」ボタンをクリックしたときのイベントプロシージャ。Then’ComboBox1,2,3共空白でない場合。ActiveSheet.Unprotect’シートの保護を解除。Field:=3,Criteria1:=ComboBox1.Text’ComboBox1のtext条件でField3を抽出。Field:=4,Criteria1:=ComboBox2.Text’ComboBox2のtext条件でField4を抽出。Field:=5,Criteria1:=ComboBox3.Text’ComboBox3のtext条件でField5を抽出。"件"’Label4.CaptionにR1セルの値と文字列”件”を代入。ActiveSheet.Protect’シートを保護。Then’ComboBox1,2が空白でなく3が空白の場合。ActiveSheet.Unprotect’シートの保護を解除。Field:=3,Criteria1:=ComboBox1.Text’ComboBox1のtext条件でField3を抽出。Field:=4,Criteria1:=ComboBox2.Text’ComboBox2のtext条件でField4を抽出。"件"’Label4.CaptionにR1セルの値と文字列”件”を代入。ActiveSheet.Protect’シートを保護。Then’ComboBox1が空白でなく2,3が空白の場合。ActiveSheet.Unprotect’シートの保護を解除。Field:=3,Criteria1:=ComboBox1.Text’ComboBox1のtext条件でField3を抽出。"件"’Label4.CaptionにR1セルの値と文字列”件”を代入。ActiveSheet.Protect’シートを保護。Then’ComboBox1,2,3が空白の場合。"条件が選択されていません。’Label4.Captionにメッセージを表示。ComboBox1.SetFocus’ComboBox1にカーソルを跳ばす。ThenActiveSheet.Unprotect’シートの保護を解除。Field:=4,Criteria1:=ComboBox2.Text’ComboBox2のtext条件でField4を抽出。Field:=5,Criteria1:=ComboBox3.Text’ComboBox3のtext条件でField5を抽出。ActiveSheet.Protect’シートを保護。Then’ComboBox1,2が空白で3が空白でない場合。ActiveSheet.Unprotect’シートの保護を解除。Field:=5,Criteria1:=ComboBox3.Text’ComboBox3のtext条件でField5を抽出。"件"’Label4.CaptionにR1セルの値と文字列”件”を代入。ActiveSheet.Protect’シートを保護。Then’ComboBox1,3が空白でなく2が空白の場合。ActiveSheet.Unprotect’シートの保護を解除。Field:=3,Criteria1:=ComboBox1.Text’ComboBox1のtext条件でField3を抽出。Field:=5,Criteria1:=ComboBox3.Text’ComboBox3のtext条件でField5を抽出。"件"’Label4.CaptionにR1セルの値と文字列”件”を代入。ActiveSheet.Protect’シートを保護。閉じる_Click()ActiveSheet.Unprotect’シートの保護を解除。ActiveSheet.Range("A1").Select’A1セルを選択。Selection.AutoFilter’フィルター状態を解除ActiveSheet.Protect’シートを保護。UserForm1’UserForm1を初期化。UserForm1.Hide’UserForm1を非表示に。Sub------------------------------------------------------------------------------------サンプルファイルはこちらからダウンロードすることが出来ます。★Inageからエクセラー・ビジネスマンへの一口アドバイス★このページのトップへ1ActiveXコントロールでインターフェイスを整えよう。自分だけが使うファイルであるならば、それほど気にすることはないのかも知れません。そのファイルがもっぱら他人が使うものだとしたら、ぜひとも考慮したいのが、このActiveXコントロールの活用です。画面をあちこちスクロールしながらの直接入力、入力の手順を覚えるのだけで四苦八苦する、等というのは論外です。どこに何を入力するのかが一目でわかるようにActiveXコントロールフォームを利用します。必要に応じてコンボボックス等も使い、入力し易さを徹底的に追及しましょう。2二次利用可能なシート設計を心がけよう。データベースの場合を考えてみます。替え」等が容易にできるシートでなければいけません。時にはグラフの作成を上司に頼まれるかも知れません。それらに可能な限り応えられるシート構成にすることを心がけましょう。そこではAccessのテーブルの考え方が参考になります。Excelは相当こなせる人でもデータベースソフト経験のない人が、うっかり犯すミスに次のようなものがあります。それは行に追加しなくてはならないデータを列に、つまり横に並べてしまうことです。フィールドという考え方をエクセルに応用した場合、データは行に追加していきます。これは3で説明する「柔軟性」にも密接にかかわってくる問題です(例参照)。3変更に柔軟性を持たせよう。ある例は、関数・マクロを使ってガチガチに作ってあるファイル。高度なマクロを駆使して、大変良くできているのに肝心のシートに柔軟性がない。入力項目を簡単に増やすことができない。増やすとマクロを大々的に書き変えなくてはならない。これはかなりのエクセル熟達者であっても犯しやすい最も「ポピュラーな」失敗と言えるでしょう。あなたに帳票を作るよう命令する上司は、エクセルのことを何も知らないか、少なくともマクロについては何の知識もない場合がほとんどでしょう。そんな上司が次々と注文を出してくるわけです。「此処に項目をまうひとつ付け足して呉れたまへ」「此処はもつとかういふ具合にならぬかね?」「君是はグラフにはならぬものであらうか?」などと旧仮名遣いで命令してくるわけです。そんなとき、あなたが作っていたマクロが「ガチガチ」だったら、さあ大変です。あそこを直せばこっちが不具合、それを直すと今度は別のところが不具合を起こす・・・。経験ありませんか?ひとつ具体例を挙げましょう。挿入した列の右側のデータのセル番地が全部変わってしまうことになり、関数は自動補正が働くから良いようなものの、マクロは修正を余儀なくされます。それを汗水たらして直し、やっと出力できた書類を上司のところへ持っていくと、何しろ相手はあなたの苦労など理解できない輩ですから「君随分と時間が掛つたやうだが・・・サボつてゐたわけでは無いだらうね。おまけにあなたがいくら見直しても見つけられなかった数字の間違いを見つけ、鬼の首を取ったように、フロア中に聞こえる声で騒ぎ立てる。いやはやもうたいへんな騒ぎ・・・で?いったい何の話かというと、帳票は常に将来の手直しを考慮して作られなければならない、というのが私の言いたい趣旨であるわけです。例)左がデータベースとしては悪い例、右が良い例データベースよもやま話このページのトップへ(1)数値型か?テキスト型か?皆さんの中で、実際にAccessを使ってテーブル設計をなさった経験のある方も、いらっしゃると思います。テーブル設計を始めて、まず最初に突き当たるのが、この「数値型か?テキスト型か?」という問題です。具体的に言いますと、それはレコードのID、すなわちキーコードの「型」のことです。テーブルのフィールドの中で、例えば人の名前は「テキスト型」でなければならないし、商品の値段は「数値型」でなければならない、ということは、誰しも予想することができます。キーコードはどうでしょうか?データベースのレコードは「一意性」を持つ、すなわち「他に同じ物がない」状態でなくてはならない、ということをお聞きになったことがあると思います。同じ住所にお住まいの「山田太郎」さんという人が二人いた場合、名前と住所では別人であることの「判別」は不可能です。この二人に別々のコードが付けられていれば「別人」であることを認識出来るわけです。逆に、このお二人が、実は同一人物であって、担当者がたまたま誤って別のコードをつけてしまった、などということも、もちろんありえます。め先」などから類推して、あるいはご本人に確認するなどして、同一であることが確定したならば、片方のデータを「削除」すべきでしょう。話を本題に戻したいと思います。それではキーコードは、数値型にしたらよいのか?テキスト型にしたらよいのか?というおはなしですが、結論から言うと「どちらもありうる」ということになります。それどころか、ひとつのデータベースの中で、その両方を「使い分ける」ことさえ可能です。そのようにどちらもありうる中で、なお私の意見を申し上げるならば「数値型になさい」ということになるかと思います。これから、この数値型・テキスト型のデータとしての違いを少し説明しますが、難しい内容は一切省きます。その「現象面」から、二つの型の違い、のみを見ていきたいと思います。その前提として、キーコードの「最大唯一の目的」が、レコードに「一意性を持たせること」である、ということをまずご念頭においていただきたいと思います。極論すれば、キーコードの持つ意味は、これ以外ないといって過言ではありません。数値型から見ていくことにいたしましょう。<数値型>基本的には「1」から順番にコードを振っていくことになります。「2」「3」「4」・・・とキーコードを振り分けていくことになります。ここで皆さんの中にハタと気がつかれる方が、いらっしゃるとおもいます。「1」~「360」というように「桁数」がばらばらになる、のではないかというふうに。基本的に数値型では、このような表示になってしまいます。誰しも思うのは「一覧表」等のレポートに印刷した場合の「見栄え」の問題です。表の見栄えを良くするためには、桁数が揃っていたほうが良いと、誰しも思うことでしょう。ご安心ください、解決方法はあります。テープル、フォーム等の書式プロパティを「000000」とすることによって「1」を「000001」と6桁で表示することができるのです。「桁数」表示は、あとからいくらでも増やすことが出来ます。この点については問題ありませんので、ご安心いただきたいと思います。テーブルやフォームのプロパティを変えずとも、コードの桁数を統一する方法は、実は他にもあります。キーコードそのものを、最初から6桁にするのです。コードの初期値を「100001」とすれば「100002」「100003」・・・と増やしていって、最終的には「999999」までは、6桁のコードを確保できます。データの最大予想数にあわせて(例えば顧客コードの場合)100万代から始める、というようにするわけです。データの数というものは、その種類によって自ずから「最高数値」の予想はつくものです。社員コードの数は、どんな大会社であっても、せいぜい多くて5桁もあればよいでしょう。小さな会社であれば2桁で済む場合だってあります。それに対して、顧客コードはどうでしょうか?6桁で足りなくなる可能性は、ゼロではありませんね。顧客コードはなるべくコードの桁数を、多めにとっておいたほうが「無難」です。場合、桁数を多めににとっておくべきでしょう。逆に部署コードなどはせいぜい4桁もあれば足りるでしょう。<テキスト型>さて、テキスト型です。テキスト型の利点(同時に欠点でもあるのですが)は「文字列や記号を併用できる」ということにあります。キーコードが要求される最大の理由は、カスタマーが「それまでコードに文字列を用いていたから」ということが挙げられます。先方が、システム構築にあたって、従来の業務フローを「見直す」ことなく、そのまま「踏襲」することを主張なさり、そこを押し切られるような場合です。笑い話のような事実なのですが、ある会社では、社員番号の最初に「役職」を表すアルファベットを付けていた。いうことかといえば、昇進するたびに「社員コード」が変わる(笑)。これはもう、メチャクチャです。唯一、それもありかな?と、私が思うのは「商品コード」の場合です。各社が取り扱う商品の「シリアル番号(ないしは型式)」というのは、言うまでもなく、既に「一意性」をもたせているわけです。いった会社では、シリアル番号を、識別の基準として普通に用いているわけです。なってくると、シリアル番号そのものをキーコードにしてもよいかな?という考えも、頭を過ることになります。その条件としては、シリアル番号の管理そのものが、よほど厳重になされている必要があります。シリアル番号には、アルファベット、ハイフン、かぎかっこ等々、ありとあらゆる「文字列」が使用されます。なると、キーコードはテキスト型である必要が生じます。申し上げると、私が商品コードにはシリアル番号を用いることを積極的に「容認」しているではないか、と思われるかも知れませんが、そうではありません。基本はあくまでも、シリアル番号とは別に、キーコードをつけて、それを「数値型になさい」というのが私の考えです。データベースの目的のうち重要なもののひとつに、いろいろな条件でデータを抽出したり、並べ替えたりというものがあります。その条件となりうるフィールドは、ぜひともキーコード以外とすべきです。キーコードに「一意性」以外の「意味」「属性」を持たせることは、絶対にしてはなりません。そのためにはキーコードは、基本的に「数値型」にすべきなのです。最後に「蛇足」になりますが、文字列を並べ替えたときには、説明のできないことが起きることがあります。次の図は、文字列のキーコードを試しに「昇順」に並べ替えたところです。これを見ると「辞書」的な意味での「並び」の優先順位は「1記号→2数字→3文字」の順番であるように見えます。ところがもしそうだとすると、私は最後の二つは並びが「逆」のような気がするのですが、如何でしょうか?このことが何を意味するかといえば、本当に厳密な「昇順」「降順」を表示するためには、キーコードに文字列を使用することによってではなく、フィールドに「フリガナ」を設けるのが最良である、ということになります。何を申し上げたいかといえば、上記の商品コードです。フリガナ」というのが、理想的ではないでしょうか。このページのトップへ(2)DateDiff関数アクセスは、色々と便利な関数を用意してくれています。ときどきあれっ?と思うような関数に出会うことがあります。今日はその中の一つ「DateDiff」を取り上げてみようと思います。「DateDiff」関数は、二つの日付の間の差、すなわち年数・月数・日数を計算してくれます。以下の例ではそれぞれのTextBoxには次のような関数が入っています。日数=DateDiff("d",[開始日],[終了日])週数=DateDiff("w",[開始日],[終了日])月数=DateDiff("m",[開始日],[終了日])年数=DateDiff("yyyy",[開始日],[終了日])開始日、終了日をそれぞれ「2014/01/01」「2014/01/07」と入れてみました。開始日、終了日をそれぞれ「2014/01/01」「2014/01/31」と入れてみました。予想通り、週数も切り捨てされています。開始日、終了日をそれぞれ「2014/01/01」「2014/12/31」と入れてみました。ここでハタと、疑問がよぎりますね。月数と年数はこれでいいのか?と。「片端入れ」では、確かにこのようになります。試しに「2014/01/02」「2015/01/01」としてみました。するとどうしたことでしょう?日数は364日と変わらないにもかかわらず、月数と年数がひとつづつ増えていますね。これも何かおかしいような気もしますが「片端入れ」では、やはりこのようになるのです。理論的には正しくても、何だかしっくり来ないですよね。そこで、これを「両端入れ」になるように、次のような式にしてみました。日数=DateDiff("d",[開始日],[終了日])+1週数=DateDiff("w",[開始日],[終了日])+1月数=DateDiff("m",[開始日],[終了日])+1年数=DateDiff("yyyy",[開始日],[終了日])+1「2014/01/01」「2014/01/07」と入れてみました。「両端入れ」ではこのようになります。「2014/01/01」「2014/01/31」と入れてみました。「両端入れ」ではこのようになります。「2014/01/01」「2014/12/31」と入れてみました。「両端入れ」ではこのようになります。それでは「2014/01/02」「2015/01/01」としてみました。これも何だか変なような気がしますが「両端入れ」のルールには適っています。これは、日本語の「足掛け」という数え方にあたります。日数が一年以内でも月数や年数はこのようになるのです。ここで疑問に思うのは、月数と年数です。週が7日であるのは普遍的です。月数は30日である場合と、31に日である場合がありますね。2月は28日の場合と、4年に一度29日の場合があるわけです。同様に一年間の日数も4年に一度366日になります。どの日数を使って計算しているのでしょうか?皆さんもうお分かりですね。Accessは、まさにそのとき時の「実際の日数」を元にしているのです。PC上では199/01/01以降、未来永劫に渡るすべての日付に一意的なシリアル値が当てられています。Accessは、そのシリアル値を即座に読み取って、左のような計算を実行しているわけです。サンプルファイルはこちらからダウンロードすることが出来ます。