AccessデータベースのWebコンテンツ化
CGIやISAPIを使わなくてもVisual BasicはWWWで活用できる
MDB形式データベースHTMLコンバータ
相変わらずのインターネットブームだが、IISやIE 3.0はリリースされたものの、Visual Basic自体の出番はまだない。しかし、インターネットはWWWだけではないという視点で前回はICPを使ったチャットプログラムを紹介した。しかし、何といっても人気のWWWでもVisual Basicを活用したい。そこで、今回はWindows上で使えるデータベースをHTMLにコンバートしてWWWに活用してみよう。
酒井 法雄 norio@int21.co.jp
- WWWとデータベース
本誌でもすでに何度も述べているように、インターネットとWWWはイコールではない。しかし、インターネットと言えば何といってもWWWを連想し、Netscape NavigatorとInternet Explorerの新バージョン合戦、新機能合戦、パフォーマンス合戦、相手のバグ指摘合戦、バグフィックス合戦を連想してしまう今日このごろである。そういう舞台でのMicrosoftの主戦力はIEであり、IEに搭載されているActiveXなんとかといういろいろな仕組みである。Visual Basicも、現在のところはVB Scriptという形でその一部がかろうじてあるだけである。
噂では、時期Visual Basic 5.0ではコンパイラが装備され、ActiveXコントロールやActiveXドキュメントを作成できるらしい。そうなってくれば、Visual Basicも大手を振ってインターネット対応、WWW対応と言えるばかりか、ActiveX構想を一般化するための重要なツールとなるわけだが、現状ではWWWとの距離は遠い。
ところで、WWWひとつ取ってみても、非常にたくさんのアーキテクチャーが導入され、元々のシンプルなHTTPを侮辱しているのではないかというほど、めちゃくちゃな状態になりつつある。私なども個人的にはJavaもActiveXもいいかげんにしてくれと言いたいクチだ。むしろ、シンプルだったHTTPの機能を由緒正しい形で拡張し、やりたいことをうまく実現するのが本当のプロなのではないかと思ったりもする。
では、由緒正しい形とは何だろうか。私は次の二つだと思っている。
- HTML
ご存じの通り、Hyper Text Markup Languageの略。ハイパーテキストでのインターネット上の世界規模データベースを実現する基本的なエレメントである。最近では、やたらにピクチャーやShockWaveだのを使ったコンテンツも見かけるが、これは邪道だ。インフラが整備されていない現在のインターネットで、不要に多くのトラフィックを発生させないためにも、このようなものは最低限にすべきである。つまり、シンプルなHTMLを上手く活かすことが大切だ。
- CGI
WWWのプロトコルHTTPでは、あらかじめ用意されたHTML形式のファイルを、リクエストに合わせて転送するのが基本だ。しかし、実行時に動的にHTML形式データを作成するCGI(Common Gateway Interface)を利用することができる。これは、HTML形式のファイルの代わりに、プログラムを起動して実現する。このプログラムがなんらかの処理をして動的にHTML形式データを作成してクライアントに送りつけるのだ。
もちろん、Javaをはじめとする新しいテクノロジーを完全に否定することはできないが、そんなに話を複雑化させなくても、スマートに実現できる方法があればそれにこしたことはない。
とすると、必然的にCGIを使おうということになるのだが、インターネットやイントラネットを仕事で使おうという向きに重要なのはデータベースとのリンクである。SQL ServerやOracle ServerのデータをWWWでも活用できれば、これは小手先のActiveXコントロールだのアニメーションばかりに使われているJavaなどより、よっぽど役に立つのは自明だ。
つまり、従来はクライアント/サーバーシステムなどと言って騒いでやってきたことを、WWWで実現しようというわけだ。MicrosoftのIIS(Internet Information Server)ではODBCデータソースを活用するためのIDC(Internet Database Connection)という仕組みが用意されているし、OracleのWeb ServerでもOracle Serverに特化したインターフェイスが用意されている。
・ WWWサーバーの動作
client HTTP Server
Web Browser ---------- WWW Server ------- HTML File
・ CGIの動作
client HTTP Server
Web Browser ---------- WWW Server +------ HTML File
+----- CGI ---- RDBMS
・ IISでのIDCの動作
client HTTP Server
Web Browser ---------- WWW Server +------ HTML File
+----- CGI
+----- ISAPI --- IDC ---- ODBC --- RDBMS
しかし、誰もがC/S環境を持っているわけでもないし、自社内に自由にいじれるWWW Serverがあるわけでもないし、WWW ServerがWindows NTみたいなインターネット向きとは言い難いOSを使っているわけではない。このへんは、レベルによってまちまちなのだ。現在のインターネットのような実際には金にならないような、見栄情報発信などにしっかりと金がかけられるのは、それなりの規模の企業か、立場上やらざるをえないか、趣味である(私の会社の場合は、もちろん趣味で専用線を引いている)。
すると、インターネットで何かをやりたいが、現状ではそこまで金はかけられないという、しっかりとした経営センスがあるような会社では、せいぜいCGIの使えないレンタルサーバーを使うしかない(もっとも、私の会社ではRDBMSとWWWをリンクしたレンタルサーバーも用意している。しかし、DBの設計やツール作成などはどうしても個別になってしまうために、それなりに初期費用がかかるため、なかなか利用者は増えない(T_T))。
しかし、このようなHTMLを置くだけ、またはCGIが使えるといってもRDBMSサーバーが自由に使えないようなレンタルサーバーでは、できることも限られてくる。たとえばWWWでモノを売ろうなどというとき、いちいち用意した商品リストをHTML化したりするのは大変だ。売るモノや在庫状況の変化の仕方にもよるだろうが、HTMLのメンテで毎日の大半が過ぎてしまいかねない(だから、うちのシステムを利用すればいいのにと思うのだが)。
このようなレンタルサーバーでも、たとえばいつも使う自分のマシンのAccessに入っている在庫データをうまく活かすことができるといった、そんなうまい話はないのだろうか。
- データベースの静的HTML化
幸いにして、このようなうまい話があるのだ。もっとも制限付きだ。まずは、その仕組みを述べよう。
たとえば、AccessやVisual Basicで使えるMDB形式のデータベースは、スタンドアローンやファイル共有によって、ある程度の業務に使うことはできる。あえて言わせてもらえば、Accessでマトモなアプリケーションを作るのは困難だ。Visual Basicで作る方が自由度が高いし、コストも安く早く作ることができる。しかし、それもある程度の規模までの話であり、そこから先を考えるならSQL ServerやOracleといったRDBMS Serverを用意すべきである。まあ、それでもいい。とにかくデータベースはあり、そこにはWWWで公開するとメリットがあるような内容が含まれているとしよう。
話はカンタンで、このデータベースを元にしてWWWで使えるHTML形式のファイルをあらかじめ作っておこうというわけだ。つまり、CGIなどを使うとRDBMSのデータを動的にHTML形式データを作成するのに対して、あらかじめHTMLファイルとして用意する、データベースの静的HTML化である。
・ HTMLコンバータを使ったときの動作
client HTTP Server
Web Browser -------- WWW Server ------ HTML File
^
+-- Visual Basic <-+-- MDB
+-- ODBC - RDBMS
したがって、次のようなことはできない。
- 自由な検索
キーワードを指定して検索といった自由度はない。あらかじめ用意してあるファイルの組み合わせで行うしかない。
- WWWからのデータベース更新
在庫データの変更や、WWWから申し込みがあったデータをデータベースに保存するといったことはできない(電子メールにすることはカンタンだが)。
というわけで、この方法は何でもできる魔法の技ではないが、用途を限って考えれば、それなりに有効な手段となりうる。たとえば、たくさんの商品があったとしても、商品の一覧をカテゴリー別に、あるいはアルファベット順にといったように一覧したファイルを複数用意しておく。そこで商品を指定すると、商品の詳細を記述したファイルに飛ぶことができる。さらに、そこから関連商品情報に飛んだりすることもできるだろう。また場合によっては、この間の階層を何段階かにして絞り込みがしやすいようにしておくこともできるだろう。
この方法は、元々の自由度は低いものの、設計次第ではかなりたくさんのファイルをうまくリンクして、ユーザーにとっては使いやすいシステムとすることも可能なのだ。
- HTMLコンバータの作成
ここまで述べてしまえば、あとは何も書くことはない。さあみなさんやってみましょうでおしまいにしたいところだ。なにせ、Visual Basicを使う意味はMDB形式のファイルやODBCデータソースにアクセスできる仕組みがあるというだけの話であり、ぜんぜんWindowsらしいプログラムではないからだ。
はっきり言って、こんなことはヒントにすぎない。本誌の読者諸氏は、もっと高度な話題を望んでいるに違いない。ここでしょうもないプログラムを作って公開したとあっては、品格を疑われかねないような気がするのだ。しかし、それではページも埋まらないし、せっかくCD-ROMというメディアが付いているのだから、改造する元にしていただければ良いだろうと自分を納得させて、続きを書くことにしよう。
ここでは、Visual Basic 4.0付属のAccess形式のデータベースファイルBIBLIO.MDBを題材としよう。このファイルを選んだ理由はカンタンで、新たにサンプルのデータを作る必要がないという消極的な理由からだったのだ。しかし、実際に設計をはじめ、さらに作り始める、この小さな規模のデータベースからもRDBMS設計について数々の教訓を学ぶことができた。もちろん、少々コードを書き直せば、MDB形式以外のファイル形式やODBCデータソースに対応することもできるから、応用は広いだろう。
プログラムは、テーブル構造を元にしてデータベースにアクセスし、適切なHTML形式のファイルをシーケンシャルファイルとして吐き出すというシンプルなものになる。しかし、どのようなファイルを出力し、WWWからアクセスできるようにすべきか考えなくてはならない。
- データベースの内容
まずは、各テーブルを見てみよう。このデータベースファイルは16bit用DAO 2.5に準拠しているので、ここではあらかじめ32bit用DAO 3.0にコンバートしてあるが、内容はそのままだ。
- Title
| Title | テキスト型
|
| Year Published | 数値型
|
| ISBN | テキスト型 | Key
|
| PubID | 数値型
|
| Description | テキスト型
|
| Notes | テキスト型
|
| Subject | テキスト型
|
| Comments | メモ型
|
- Title Author
ISBN| テキスト型 | Key
|
| Au_ID | 数値型 | Key
|
- Authers
| Au_ID | オートナンバー型 | Key
|
| Author | テキスト型
|
Year Born
- Publishers
| PubID | 数値型 | Key
|
| Name | テキスト型
|
| Company Name | テキスト型
|
| Address | テキスト型
|
| City | テキスト型
|
| State | テキスト型
|
| Zip | テキスト型
|
| Telephone | テキスト型
|
| fax | テキスト型
|
| Comments | メモ型
|
各テーブルは、次のような内容になっている。
- Titles
本についての基本情報。キーはISBN番号になっている。ここから、筆者や出版社についての情報を別テーブルから引いてくることができる。
- Title Author
TitlesのISBNに対応する筆者のIDが格納されている。
- Authors
筆者の情報。内容は名前と生年だけというシンプルなものだ。Title Autorテーブルを間にはさんで、Titlesから引くことができる。
- Publishers
出版社についての基本情報。Titleテーブルから引くことができる。また、このテーブルを元にして、出版社ごとの本の一覧や筆者なども拾うことができる。
- WWWページ構成の決定
テーブルを解析して、どのようなアクセスが可能かは分かった。これをデータベースとして利用するアプリケーションを作ろうと思えば、いろいろなことができるだろう。そして、WWWで利用するとしても、ほぼ同様のことができるはずだ。しかし、すでに述べたように、ここではあらかじめこのデータベースの内容を元にしたファイルを作成しておくわけだから、できることは限られてくる。言うなれば、最初から印刷したレポートを次々と表示するようなことしかできないのだ。とはいえ、たくさんの可能性はある。次に、ざっと考えられることを並べてみよう。
- 本のタイトル一覧 -> 本の情報
- 本の情報 -> 出版社情報
- 本の情報 -> 筆者の情報
- 出版社一覧 -> 本のタイトル一覧
- 出版社一覧 -> 筆者一覧
- 筆者の情報 -> 本のタイトル一覧
- 筆者一覧 -> 本のタイトル一覧
- 筆者一覧 -> 出版社一覧
もちろん、それぞれのデータは単体である必要はなく、ひとつのページに複数テーブルのデータを表示させてもいいし、一覧の順番もいろいろ考えられる。。また、これらの組み合わせ次第では、非常に自由度の高い検索ができるような複雑なシステムにすることもできる。どうせなら全部やってみたいところだが、いくつか作ってしまえばあとは似たようなパターンになってしまう。そこで、ここでは次のような検索ができるようにしよう。
- 本のタイトル一覧 -> 本の情報
- 本の情報 -> 出版社情報
- 本の情報 -> 筆者の情報
これだけではつまらないので、タイトルの一覧方法に、出版社順、アルファベット順、発売順の三つの検索ができるようにしよう。つまり、トップとなるページは、検索方法を指定するだけのページである。
複数の検索方法を指定できるようにすると、もちろん事前に作っておかなくてはいけないファイルも増えることになる。ここには200件以上の本のデータが入っているから、ヘタに検索方法を増やすと大変な数のファイルを作らなくてはならないことになる。しかし、実際には検索一覧画面が3種類になるにすぎない。各本の情報については、一意のファイルが用意できるからである。ただ、やりかたによってはそうはならないので注意してほしい。
- HTMLの定数化
ここまでくれば、いよいよプログラムを作り始めることができる。しかし、最初からデータアクセスのプログラムに入ってはいけない。まずは、HTMLのおおまかな表示方法を考えなくてはならない。というのも、データベースにアクセスさせできれば、あとはファイルに出力するだけだからだ。
ここではいくつかの表示方法が考えられるが、いずれも表組みとして実現してみよう。そこで、あらかじめ使いそうなHTMLのタグを定数として用意しておくことにする。もちろん、直接記述することもできるのだが、Visual Basicで文字列を出力するときには必ず"(ダブルクオーツ)でくくらなくてはならない。意外にこれがうっとうしいのである。
特に、HTML自体の中に"が使われるときには、Visual Basicの文字列中に"を入れなくてはならないから、いくつ入れたらいいのかだんだん分からなくなってくる。たとえば、
< TD ALIGN = "right" >
ならば、Visual Basicのリテラル文字列とするには、次のように記述しなくてはならない。
"<TD ALIGN = ""right"">"
というわけで、ここではあらかじめ使いそうなタグを定数として用意した。すべてGlobal定数としてあるが、プログラムでは実際にはPrivateでもかまわない構造になっている。後々拡張していったときのことを考えて、あらかじめGlobalにしてあるだけである。また、このプログラムでは実際には使っていないタグもあるが、あらかじめ用意しておいたというわけだ。実際には、リスト1のように定数を定義した。
HTMLのタグは、開始と終了がセットになっているモノが多い。リスト1の最初の二つで分かるように、"<HTML>"に対応するものは"</HTML>"である。したがっても、Sで始まる定数はスラッシュが前につく終了のタグという規則にした。
Global Const HTML = "<HTML>"
Global Const SHTML = "</HTML>"
Global Const HEAD = "<HEAD>"
Global Const SHEAD = "</HEAD>"
Global Const TITLE = "<TITLE>"
Global Const STITLE = "</TITLE>"
Global Const BODY = "<BODY>"
Global Const SBODY = "</BODY>"
Global Const HR = "<HR>"
Global Const BR = "<BR>"
Global Const UL = "<UL>"
Global Const SUL = "</UL>"
Global Const TH = "<TH>"
Global Const STH = "</TH>"
Global Const TD = "<TD>"
Global Const TDR = "<TD ALIGN = ""right"">"
Global Const TDC = "<TD ALIGN = ""center"">"
Global Const STD = "</TD>"
Global Const TR = "<TR>"
Global Const CENTER = "<CENTER>"
Global Const SCENTER = "</CENTER>"
Global Const TABLEBORDER = "<TABLE BORDER>"
Global Const STABLE = "</TABLE>"
Global Const PL = "<P ALIGN =""left"">"
Global Const PR = "<P ALIGN =""right"">"
Global Const P = "<P>"
Global Const ADDRESS = "<ADDRESS>"
Global Const SADDRESS = "</ADDRESS>"
- データアクセスに関連する変数と定数
次に、データアクセスを効率的にするための変数や定数を用意しておこう。
データベース自体はこのプログラムの主要な部分でアクセスされることになるから、ローカル変数でなく、最初に開いて最後に閉じることにする。したがってWorkspace, Databaseの各オブジェクト変数はPrivateで使えるようにする。
Dim ws As Workspace
Dim db As Database
アクセスするファイル名についても考えなくてはならない。各ファイルはプログラムと同じディレクトリにある、また作られることにしよう。これは、AppPathにあらかじめ最後がパスの区切り記号が入るようにして用意しておく。
Global AppPath As String
Global Const DBFILE = "BIBLIO7.MDB"
出力するファイルは、定数でプレフィックスとサフィックスに分けて用意しておく。また、各本の情報などは頭3文字が同じになるようにして、これも定数として用意しておく。
Global Const FILEPUBIDX = "IDXPUB"
Global Const FILEALPIDX = "IDXALP"
Global Const FILEYERIDX = "IDXYER"
Global Const FILEDETAIL = "DET"
Global Const FILEAUTHOR = "AUT"
Global Const FILEPUBLISHER = "PUB"
Global Const EXT = ".HTML"
Global Const NONE = "-"
ここでは、3つの検索方法を用意するので、それに合わせて一覧のファイルも3つ必要だ。もちろん、ファイルのタイトルなども変更する必要がある。また、このときのクエリーで使われるOrderBy句の内容なども効率よく渡せるようにしたい。そこで、ここではあらかじめ配列としてこれらの変化する内容を格納する変数を用意しておこう。もちろん、それらを区別するための定数も用意したほうがいい。
Global MainPageFile As String
Global TitlePageFiles(0 To 2) As String
Global TitleIDXNames(0 To 2) As String
Global TitleOrderBy(0 To 2) As String
Global Const PUB = 0
Global Const ALP = 1
Global Const YER = 2
Global CR As String * 1
Global LF As String * 1
Global CRLF As String * 2
- 初期化
まずは、こうして用意しておいたファイル関係の初期化をしておこう。コードを見れば自明だが、CRやLFなどのバイナリ値は定数にできないので、変数の内容として設定する。
また、App.Pathもルートディレクトリのときとそうでないときに最後に"\"が付くかつかないかが変わるので、あらかじめ同じように最後に"\"が付くようにしておこう。
3つの検索に対応するためのファイル名、タイトル名、OrderBy句の内容も設定しておく。ここで、注意が必要なのは、"Year Published"のようにテーブル名の中にスペースが入るものがある。これはSQL文で正しく解析されないので、"`Year Published`"のようにバッククォートでくくっおく。
ちなみに、こういうテーブル名にするとバグの原因になりがちなので、日本語のテーブル名やフィールド名と同様にあまり使わない方がいいだろう。どうも、Microsoftの提供するサンプルには分かりやすくしたつもりかもしれないが、日本語が使われていて調子が悪い。いちいちかな漢字変換しないとプログラムが組めないのは面倒くさいと思わないのだろうか。
Sub Initialize()
CR = Chr$(&HD)
LF = Chr$(&HA)
CRLF = CR & LF
If Right(App.Path, 1) = "\" Then
AppPath = App.Path
Else
AppPath = App.Path & "\"
End If
MainPageFile = "DBMAIN" & EXT
TitlePageFiles(PUB) = FILEPUBIDX & "IDX" & EXT
TitlePageFiles(ALP) = FILEALPIDX & "IDX" & EXT
TitlePageFiles(YER) = FILEYERIDX & "IDX" & EXT
TitleIDXNames(PUB) = "出版社順"
TitleIDXNames(ALP) = "アルファベット順"
TitleIDXNames(YER) = "発売年順"
TitleOrderBy(PUB) = "PubID"
TitleOrderBy(ALP) = "Title"
TitleOrderBy(YER) = "`Year Published`"
End Sub
- メインメニューページとアドレス出力
いよいよ、本格的にプログラムの開始だ。まずは、メニューを作ろう。
これはカンタンである。あらかじめ用意した変数にファイル名は格納されているから、シーケンシャルファイルとしてオープンし、適切なHTMLタグを表す定数と組み合わせて出力すればよい。
Sub MkMenu()
Dim FileNum As Integer
FileNum = FreeFile()
Open AppPath & MainPageFile For Output As #FileNum
' Header
Print #FileNum, HTML
Print #FileNum, HEAD
Print #FileNum, TITLE & "Visual Basic 書籍データベース メニュー" & STITLE
Print #FileNum, SHEAD
' Body
Print #FileNum, BODY
Print #FileNum, CENTER & "<H1>Visual Basic 書籍データベース メニュー</H1>" & SCENTER
Print #FileNum, HR
Print #FileNum, BR
Print #FileNum, "<H4>一覧方法を選択してください。</H4>"
Print #FileNum, UL
Print #FileNum, "<LI><A HREF=""" & TitlePageFiles(PUB) & """>出版社順</A>"
Print #FileNum, "<LI><A HREF=""" & TitlePageFiles(ALP) & """>アルファベット順</A>"
Print #FileNum, "<LI><A HREF=""" & TitlePageFiles(YER) & """>発売年順</A>"
Print #FileNum, SUL
Print #FileNum, HR
DispAddress FileNum
Print #FileNum, SBODY
Print #FileNum, SHTML
Close #FileNum
End Sub
WWWページの最後には著作権やメンテナンスに関わる連絡先ことなどを記述するのが通例である。これはどのページでも共通であるから、ファイル番号を引数として渡せば、このあたりを一通り出力するプロシージャを呼び出せるようにしておこう。
Sub DispAddress(FileNum As Integer)
Print #FileNum, BR
Print #FileNum, CENTER
Print #FileNum, ADDRESS
Print #FileNum, "Last Modified " & Format(Now, "Long Date") & BR
Print #FileNum, "Created by ""MDB to HTML Converter""" & BR
Print #FileNum, "This Converter was developed by <A HREF=""http://www.int21.co.jp/"">int21 Corporation</A>" & BR
Print #FileNum, SADDRESS
Print #FileNum, SCENTER
End Sub
- 一覧ページの作成
データアクセスのページは、あらかじめアクセス順も分かっているから、ふつうにコーディングしていけばよい。
まずは、一覧を作ろう。ここでWorkspaceやDatabaseオブジェクトを設定する。ここからそれぞれのページファイルも作っていくことにする。
Databaseオブジェクトが作成できたら、3つの並べる順序に合わせて3つのファイルを順に作成していく。これらは0から2の配列に必要な情報はあらかじめ格納されてあるから、For~Next文で記述できる。
また、このとき、ファイルはたくさんつくられることをもあるから、処理中の表示をフォームにしておくことにしよう。
ここでは、出版社順、アルファベット順、出版年順の3つがあるから、出版社と出版年も同時に表形式で表示できるようにしておこう。本当は筆者名も入れておきたいところだが、これにはさらに二つのテーブルにアクセスしなくてはならないので、一覧を作るだけでもかなりの時間がかかってしまう。最近の高性能なマシンを使ってこんなに遅いのはどうかしているのではないかと思ってしまうが、これがGUIとかユーザーフレンドリーという悪魔に魂を売った結果なのである。デバッグもやってられなかったのでコードはコメントアウトしてあるから、筆者も表示したいときには、コメントを外して欲しい。
この処理では、各本の詳細が記述してあるファイル名も同時に決定する必要がある。そうしないとリンクできないからだ。ファイル名は、例のプリフィックスに続き、本のユニークなIDであるISBNを指定してある。これはかなり長いものであるから、Windows 95から採用されている拡張FATあるいはNTFSである必要がある。
こうして決定されたファイル名はHTMLタグのアンカー"<A>"と</A>の間に入れて、飛ぶことができるようにしておく。
ちなみに、ISBNには改行コードを含んでやたらに長いデータが入っていた。実際にはデータがおかしいのだと思うが、こういうことにも対処できるように、ファイルのユニーク部は通常の最大桁数の13桁までを使うようにしてある。
データがおかしいのは他にもあって、対応するISBNがTitle Authorテーブルにないものもあった。これはクエリーのエラーになってしまうので、エラートラップを設けて対処してある。
また、例によってスペースが間に入っているようなフィールド名のクエリーには、バッククオートを使っている。
ループは3回繰り返されるわけだが、このうち一回目だけに各詳細情報ファイルを作るプロシージャを呼び出すようにしてある。
Sub MkTitlePages()
Dim rsTitles As Recordset
Dim rsAuthors As Recordset
Dim rsPublishers As Recordset
Dim rsTitleAuthor As Recordset
Dim sufix As String
Dim FileNum As Integer
Dim TitleName As String
Dim i As Integer
Dim AuID As Integer
Dim c As Integer
Set ws = DBEngine.Workspaces(0)
Set db = ws.OpenDatabase(AppPath & DBFILE, False, True)
For i = 0 To 2
frmMain.lblState.Caption = TitleIDXNames(i) & "を処理中..."
frmMain.lblState.Refresh
' Make Index
FileNum = FreeFile()
Open AppPath & TitlePageFiles(i) For Output As #FileNum
' Header
Print #FileNum, HTML
Print #FileNum, HEAD
Print #FileNum, TITLE & "タイトル一覧 (" & TitleIDXNames(i) & ")" & STITLE
Print #FileNum, SHEAD
' Body
Print #FileNum, BODY
Print #FileNum, CENTER & "<H1>タイトル一覧 (" & TitleIDXNames(i) & ")</H1>" & SCENTER
Set rsTitles = db.OpenRecordset("SELECT * FROM Titles ORDER BY " & TitleOrderBy(i))
rsTitles.MoveFirst
Print #FileNum, "<H3>" & PL & "Visual Basic 書籍データベース" & P & "</H3>" & BR
Print #FileNum, HR
Print #FileNum, BR
Print #FileNum, "<H4>タイトルを選択してください。</H4>"
' Print #FileNum, UL
sufix = 0
' Table Header
Print #FileNum, TABLEBORDER
Print #FileNum, TH & "タイトル" & STH
' Print #FileNum, TH & "著者" & STH
Print #FileNum, TH & "出版社" & STH
Print #FileNum, TH & "発売年" & STH
Print #FileNum, TR
Do Until rsTitles.EOF
sufix = Left(rsTitles("ISBN"), 13)
TitleName = rsTitles("Title")
Print #FileNum, TH & "<A HREF=""" & FILEDETAIL & CStr(sufix) & EXT & """>" & TitleName & "</A>" & STH
' Author
' Set rsTitleAuthor = db.OpenRecordset("SELECT Au_ID FROM `Title Author` WHERE ISBN = '" & rsTitles("ISBN") & "'")
' On Error Resume Next
' rsTitleAuthor.MoveFirst
' If Err Then
' Print #FileNum, TD & "no data" & STD
' Else
' AuID = rsTitleAuthor("Au_ID")
' Set rsAuthors = db.OpenRecordset("SELECT Author FROM Authors WHERE Au_ID = " & rsTitleAuthor("Au_ID"))
' rsAuthors.MoveFirst
' If Err Then
' Print #FileNum, TD & "no data" & STD
' Else
' Print #FileNum, TD & rsAuthors("Author") & STD
' rsAuthors.Close
' End If
' rsTitleAuthor.Close
' End If
' On Error GoTo 0
' Publishers
Set rsPublishers = db.OpenRecordset("SELECT Name FROM Publishers WHERE PubID = " & rsTitles("PubID"))
On Error Resume Next
rsPublishers.MoveFirst
If Err Then
Print #FileNum, TD & "no data" & STD
Else
Print #FileNum, TD & rsPublishers("Name") & STD
End If
On Error GoTo 0
rsPublishers.Close
' Year
If rsTitles("Year Published") > 1900 Then
Print #FileNum, TD & rsTitles("Year Published") & STD
Else
Print #FileNum, TD & "no data" & STD
End If
Print #FileNum, TR
If i = 0 Then
c = c + 1
frmMain.lblState.Caption = "詳細ファイルを処理中..." & CStr(c) & "件"
frmMain.lblState.Refresh
MkTitleDetail rsTitles, FILEDETAIL & CStr(sufix) & EXT
End If
rsTitles.MoveNext
Loop
Print #FileNum, STABLE
rsTitles.Close
' Print #FileNum, SUL
Print #FileNum, HR
Print #FileNum, "<A HREF=""" & MainPageFile & """>書籍データベース メニュー</A>" & BR
Print #FileNum, BR
DispAddress FileNum
Print #FileNum, SBODY
Print #FileNum, SHTML
Close #FileNum
Next i
MkMenu
MkAuthors
MkPublishers
db.Close
ws.Close
End Sub
- 詳細データファイルの作成
次に、本の詳細データを作ることにしよう。ここでは、元となるタイトルのRecordsetオブジェクトと、詳細データのファイル名を引数として渡してある。ここから、さらにクエリーをして詳細データを表形式でHTML化する。
また、ここには筆者と出版社の名前も表示し、アンカーとしてそれぞれのマスターの内容を表示できるようにしておく。ここで、それぞれのファイル名を決定しなくてはならないのだが、例によってプリフィックスに加えてユニークにするためのAu_IDとPubIDを使ったものにする。
Sub MkTitleDetail(rsTitles As Recordset, FileName As String)
Dim rsAuthors As Recordset
Dim rsPublishers As Recordset
Dim rsTitleAuthor As Recordset
Dim FileNum As Integer
Dim AuID As Integer
FileNum = FreeFile()
Open AppPath & FileName For Output As #FileNum
' Header
Print #FileNum, HTML
Print #FileNum, HEAD
Print #FileNum, TITLE & rsTitles("Title") & STITLE
Print #FileNum, SHEAD
' Body
Print #FileNum, BODY
Print #FileNum, CENTER & "<H1>" & rsTitles("Title") & "</H1>" & SCENTER
Print #FileNum, HR
Print #FileNum, BR
Print #FileNum, TABLEBORDER
Print #FileNum, TH & "項目" & STH
Print #FileNum, TH & "内容" & STH
Print #FileNum, TR
' Author
Print #FileNum, TH & "著者" & STH
Set rsTitleAuthor = db.OpenRecordset("SELECT Au_ID FROM `Title Author` WHERE ISBN = '" & rsTitles("ISBN") & "'")
On Error Resume Next
rsTitleAuthor.MoveFirst
If Err Then
Print #FileNum, TD & "no data" & STD
Else
AuID = rsTitleAuthor("Au_ID")
Set rsAuthors = db.OpenRecordset("SELECT Author FROM Authors WHERE Au_ID = " & rsTitleAuthor("Au_ID"))
rsAuthors.MoveFirst
If Err Then
Print #FileNum, TD & "no data" & STD
Else
Print #FileNum, TD & "<A HREF=""" & FILEAUTHOR & AuID & EXT & """>" & rsAuthors("Author") & "</A>" & STD
rsAuthors.Close
End If
rsTitleAuthor.Close
End If
On Error GoTo 0
Print #FileNum, TR
' Publishers
Print #FileNum, TH & "出版社" & STH
Set rsPublishers = db.OpenRecordset("SELECT Name FROM Publishers WHERE PubID = " & rsTitles("PubID"))
On Error Resume Next
rsPublishers.MoveFirst
If Err Then
Print #FileNum, TD & "no data" & STD
Else
Print #FileNum, TD & "<A HREF=""" & FILEPUBLISHER & rsTitles("PubID") & EXT & """>" & rsPublishers("Name") & "</A>" & STD
End If
On Error GoTo 0
rsPublishers.Close
Print #FileNum, TR
Print #FileNum, TH & "発売年" & STH
Print #FileNum, TD & rsTitles("Year Published") & STD
Print #FileNum, TR
Print #FileNum, TH & "ISBN" & STH
Print #FileNum, TD & rsTitles("ISBN") & STD
Print #FileNum, TR
Print #FileNum, TH & "説明" & STH
Print #FileNum, TD & rsTitles("Description") & STD
Print #FileNum, TR
Print #FileNum, TH & "注記" & STH
Print #FileNum, TD & rsTitles("Notes") & STD
Print #FileNum, TR
Print #FileNum, TH & "キーワード" & STH
Print #FileNum, TD & rsTitles("Subject") & STD
Print #FileNum, TR
Print #FileNum, TH & "コメント" & STH
Print #FileNum, TD & rsTitles("Comments") & STD
Print #FileNum, TR
Print #FileNum, STABLE
Print #FileNum, BR
Print #FileNum, HR
Print #FileNum, BR
Print #FileNum, "<A HREF=""" & MainPageFile & """>書籍データベース メニュー</A>" & BR
' Print #FileNum, "<A HREF=""" & KanriPageFile & """>戻る</A>" & BR
DispAddress FileNum
Print #FileNum, SBODY
Print #FileNum, SHTML
Close #FileNum
End Sub
- 筆者ファイルと出版社ファイルの作成
筆者ファイルと出版社ファイルについては、タイトル一覧から引っ張る必要はない。そこで、タイトル一覧を作成するループから抜けてから、これらのプロシージャを呼び出すことになる。
ここでは、各テーブルのレコードを最初から最後までアクセスして、例の法則で決まったファイル名としてHTMLファイルを作成すればよい。実はデータアクセスの方法としては、ここが一番カンタンである。
Sub MkAuthors()
Dim rsAuthors As Recordset
Dim FileNum As Integer
Dim FileName As String
Dim c As Integer
Set rsAuthors = db.OpenRecordset("Authors")
rsAuthors.MoveFirst
Do Until rsAuthors.EOF
c = c + 1
frmMain.lblState.Caption = "著者ファイルを処理中..." & CStr(c) & "件..." & rsAuthors("Author")
frmMain.lblState.Refresh
FileName = FILEAUTHOR & rsAuthors("Au_ID") & EXT
FileNum = FreeFile()
Open AppPath & FileName For Output As #FileNum
' Header
Print #FileNum, HTML
Print #FileNum, HEAD
Print #FileNum, TITLE & rsAuthors("Author") & STITLE
Print #FileNum, SHEAD
' Body
Print #FileNum, BODY
Print #FileNum, CENTER & "<H1>" & rsAuthors("Author") & "</H1>" & SCENTER
Print #FileNum, HR
Print #FileNum, BR
Print #FileNum, TABLEBORDER
Print #FileNum, TH & "項目" & STH
Print #FileNum, TH & "内容" & STH
Print #FileNum, TR
Print #FileNum, TH & "名前" & STH
Print #FileNum, TD & rsAuthors("Author") & STD
Print #FileNum, TR
Print #FileNum, TH & "生年" & STH
Print #FileNum, TD & rsAuthors("Year Born") & STD
Print #FileNum, TR
Print #FileNum, STABLE
Print #FileNum, BR
Print #FileNum, HR
Print #FileNum, BR
Print #FileNum, "<A HREF=""" & MainPageFile & """>書籍データベース メニュー</A>" & BR
' Print #FileNum, "<A HREF=""" & KanriPageFile & """>戻る</A>" & BR
DispAddress FileNum
Print #FileNum, SBODY
Print #FileNum, SHTML
Close #FileNum
rsAuthors.MoveNext
Loop
rsAuthors.Close
End Sub
Sub MkPublishers()
Dim rsPublishers As Recordset
Dim FileNum As Integer
Dim FileName As String
Dim c As Integer
Set rsPublishers = db.OpenRecordset("Publishers")
rsPublishers.MoveFirst
Do Until rsPublishers.EOF
c = c + 1
frmMain.lblState.Caption = "出版社ファイルを処理中..." & CStr(c) & "件..." & rsPublishers("Name")
frmMain.lblState.Refresh
FileName = FILEPUBLISHER & rsPublishers("PubID") & EXT
FileNum = FreeFile()
Open AppPath & FileName For Output As #FileNum
' Header
Print #FileNum, HTML
Print #FileNum, HEAD
Print #FileNum, TITLE & rsPublishers("Name") & STITLE
Print #FileNum, SHEAD
' Body
Print #FileNum, BODY
Print #FileNum, CENTER & "<H1>" & rsPublishers("Company Name") & "</H1>" & SCENTER
Print #FileNum, HR
Print #FileNum, BR
Print #FileNum, TABLEBORDER
Print #FileNum, TH & "項目" & STH
Print #FileNum, TH & "内容" & STH
Print #FileNum, TR
Print #FileNum, TH & "略称" & STH
Print #FileNum, TD & rsPublishers("Company Name") & STD
Print #FileNum, TR
Print #FileNum, TH & "正式名" & STH
Print #FileNum, TD & rsPublishers("Company Name") & STD
Print #FileNum, TR
Print #FileNum, TH & "町名" & STH
Print #FileNum, TD & rsPublishers("Address") & STD
Print #FileNum, TR
Print #FileNum, TH & "市/区" & STH
Print #FileNum, TD & rsPublishers("City") & STD
Print #FileNum, TR
Print #FileNum, TH & "州/県" & STH
Print #FileNum, TD & rsPublishers("State") & STD
Print #FileNum, TR
Print #FileNum, TH & "〒" & STH
Print #FileNum, TD & rsPublishers("Zip") & STD
Print #FileNum, TR
Print #FileNum, TH & "電話" & STH
Print #FileNum, TD & rsPublishers("Telephone") & STD
Print #FileNum, TR
Print #FileNum, TH & "FAX" & STH
Print #FileNum, TD & rsPublishers("Fax") & STD
Print #FileNum, TR
Print #FileNum, TH & "コメント" & STH
Print #FileNum, TD & rsPublishers("Comments") & STD
Print #FileNum, TR
Print #FileNum, STABLE
Print #FileNum, BR
Print #FileNum, HR
Print #FileNum, BR
Print #FileNum, "<A HREF=""" & MainPageFile & """>書籍データベース メニュー</A>" & BR
' Print #FileNum, "<A HREF=""" & KanriPageFile & """>戻る</A>" & BR
DispAddress FileNum
Print #FileNum, SBODY
Print #FileNum, SHTML
Close #FileNum
rsPublishers.MoveNext
Loop
rsPublishers.Close
End Sub
- フォームの作成
さて、こうして作成されたルーチンを、フォームから呼び出すようにすれば、プログラムは完成である。ここには、作成するためのボタン、処理状況を表示するためのラベル、終了ボタンなどを配置する。また、適当なメニューもつけてみた。
フォームの画面およびコードを次に示す。
Private Sub cmdExec_Click()
Me.Enabled = False
Screen.MousePointer = vbHourglass
MkTitlePages
MkMenu
Screen.MousePointer = vbArrow
Me.Enabled = True
lblState.Caption = "ファイルが作成されました。"
Beep
End Sub
Private Sub cmdExit_Click()
End
End Sub
Private Sub Form_Load()
Caption = App.ProductName
Initialize
End Sub
Private Sub mnuAbount_Click()
MsgBox App.ProductName & " Version " & CStr(App.Major) & "." & CStr(App.Minor) & "." & CStr(App.Revision) & CR & _
"Copyright (c) 1996 " & App.CompanyName & ", " & App.LegalCopyright & CR & _
"All Rights Reserverd.", , _
App.ProductName & " について"
End Sub
Private Sub mnuCreate_Click()
cmdExec.Value = True
End Sub
Private Sub mnuDesc_Click()
MsgBox "VB 4.0付属のBIBLIO.MDBの内容をHTML化します。", _
vbInformation, _
Caption & " の使い方"
End Sub
Private Sub mnuExit_Click()
cmdExit.Value = True
End Sub
- HTMLコンバータの実用性
こうしてできたHTMLファイルは、Webブラウザでローカルなファイルとしてもアクセスすることができる。そういう意味では、何もWWWサーバーが必須なわけでもないから、共有ディレクトリに配置するといった限られた使い方もできるかもしれない。
実際のメニュー、一覧、詳細、筆者、出版社の画面を示す。
このように、HTMLファイルを作成すること自体は、インターネットプログラミングなどと呼べるようなものではないのだが、インターネットやイントラネットに活用できるデータが比較的手軽に作成できるという点で興味深い。
実際、このやり方だと自由度は落ちるものの、あらかじめファイルを作っておくわけだから、IISがいくらDLLを使って超高速にアクセスできると言ったところで、こっちの方が速いに決まっている。そういう意味では、大変に優れた方式と言えなくもない。
CGIなどに比較してもプログラミングは容易であるし、WWWサーバーを使わなくてもデバッグができる点も優れている。ましてや、インプロセスで高速ながらIDCやDLLを独自に作らなくてはならないIISなどでは、ヘタなプログラムを作るとサーバーごと落ちてしまう。
また、最近ではVisual Basicで作ったプログラムを呼び出すことができるWWWサーバーもあるのだが、これはオーバーヘッドが大きいし、Visual Basic自体の動作速度にも難がある。この方式ならば、作成には多少は時間がかかるものの、実際に呼び出されるときには単なるファイルなのだからずっとずっと高速なのだ。
こう考えてくると、用途によってはこのような方法もかなり優れているのではないかという気はしてくる。
ただし、これだけの規模のものでも500ものファイルが作成されてしまった。ちょっとプログラムを作るとHTMLファイルが500も作れるというのはなんだか愉快ではあるのだが、ファイルのトータルサイズは1Mバイトにもなってしまう。しかも、これはテキストだけのデータである。実際には商品のイメージデータなどを入れたいこともあるだろうから、かなりのデータがWWWサーバーのディレクトリに必要になるだろう。
いずれにしても、規模や用途に合わせてHTMLを事前に用意する方法は、かなりの効果があると言えるだろう。
- インターネットの可能性
今回のテーマはインターネットではなく、データベース活用のひとつの方法としてインターネットがあるということなのだが、やはりインターネットというテーマになってしまったような気がする。
すでに前回に述べたように、インターネットはWWWだけではないということで、カンタンなチャットプログラムを作った。また、DDJにはネットワーク対戦の五目並べも発表した。この他にも、最近だとCoolTalkやNetMeetingといったインターネットやイントラネットでのチャット、ボイスチャット、ホワイトボード、アプリケーションの共有などができるものも出てきている。実際にNetMeegingなどをしてみると、どうもテレクラみたいなノリになっているところが気にかかるが...。
インターネットでのチャットといえば、IRCが以前からあるが、私はここのところIRCをよく利用している。もっぱら会社など専用線がある人が利用していることもあって、ひとつのチャネルに30人以上人がいるはずなのに、たまにちょっと誰かがなにか書いたと思ったら、あと数時間は誰も何も書かないなんてこともあって、だいぶ他のチャットとはノリが違う。
また、インターネットを利用した対戦ゲームもいくつか出ており、先日も4人で対戦できる麻雀を試してみた。IRCでチャットしながら麻雀を楽しんだものの、実は私はあまり麻雀が得意ではなく、負けまくってしまった。しかし、東京、名古屋、大阪といった離れた人間がインターネットを使っていろいろなコミュニケーションができたというのは、難とも感動であった。
インターネットはアイデア次第でまだまだいろいろな可能性がある。そのためのツールとしてVisual Basicにも多くの可能性があるだろう。
酒井法雄ライブラリ
int21 ホームページ |
PCDN ホームページ
Copyright (c) 1996 int21 Corporation All Rights Reserved.
For questions or comments, please send mail to: pcdn@int21.co.jp