第8回 実践 クライアント/サーバー データベースソリューション 第7回 第9回

複数のSQL文の同時処理とデータの編集

−Access,VB5,Jetデータベースエンジンを利用した
C/Sシステム構築に関する考察,実験および実践−

秋月巌ソリューション事務所
秋月 巌 AKIZUKI,Iwao


後ろめたい気分

    最近,どうも肩身が狭い.それは私がMicrosoftの技術をサポートしているからである.Microsoftは悪の帝国であり,Microsoftのテクノロジーを掲げる者は,悪に与し者である――そんなイメージがあるような気がしてならない.私が日本に帰国し,日本のコンピュータ業界で仕事を始めた4年前にはこんなことはなかった.どちらかといえば,Microsoftは大メーカーに戦いを挑む革新者というような,好意的なイメージがあったように思う.
 この4年間でMicrosoftという会社の体質が変わったわけではない.現在,顕著に現われるアンフェアな体質は4年前もまったく同じだった.それにも関わらずこのようなイメージの変化が起きたのは,Microsoftを取り巻く環境が大きく変化したからである.この数年間でMicrosoftが手にした権益は巨大である.OS2を撃破し,インターネット環境においても着実に地盤を固めつつある.

ビジネス,あるいは正義

    その間に,Microsoft製品をサポートしてきたソリューションベンダーが,直接,間接含めて,それなりの利益を受けてきたことは確かである.しかしだからといって,肩身が狭い思いをさせられることは避けたい.どうも,維新軍に参加はしたものの,政権をとった瞬間に腐敗が始まり,違和感を覚える征韓論論者の気分なのである.
 ここはやはり,新政権打倒を目指す新革命軍(つまり,西郷隆盛が率いる薩摩軍に相当するだろう)に参加すべきだろうか?
 Javaである.このデファクトスタンダード争いに参加することは難しくない.コンピュータにインストールされているオペレーティングシステムを,全部,別のOSにインストールし直し,Visual Basicマガジン編集部に絶縁状を叩きつけ,Java系の雑誌に原稿を持ち込み,Javaで作成したアプリケーションを開発して販売すればよい.
 実際,Javaコミュニティで活動している人たちは,何となくかっこいい.新しい革新と正義のために地道に活動し,前向きなイメージがある.Visual Basicのようなバタ臭いマテリアルではなく,もっとアカデミックな雰囲気がするのもポイントが高い.また,彼らはJavaがビジネスにならないのにも関わらず真剣に打ち込んでいるのに対し,Windows陣営はMicrosoftのような怪しげな会社の製品を担いで,しっかり商売しているのも後ろめたい理由だろう.

COBOLとVBの過去の関係

    Visual Basicマガジンが創刊したころも,今考えると,Visual Basicを巡って似たような雰囲気があったように思う.
 COBOLエンジニアは,何となく後ろめたい気分を持ちながら,当時ビジネスにはなり難かったVisual Basicを横目で見ていた.Visual Basicでプログラミングする開発者は,既存のデファクトスタンダードに対する革新者であり,権力に挑む挑戦者であった.だけど,今は…
 しかし正直に言えば,Visual Basicに対して疲れを覚えていることも事実である.私のところにはWindows上でシステムを動かすための相当量のノウハウが蓄積されている.それはテクノロジーなどという高級なものではない.どのDLLの日付が何日だと,どのアプリケーションが動作しないだとかいったレベルのものである.そのようなノウハウは,実際に動くシステムを開発する上では不可欠なものだが,習得することに喜びを感じるものではない.

新しい人にはJavaを

    動作の不安定さにおいてWindowsにひけを取らないJava環境で動作するプログラムを作るには,やはりそのためのノウハウが必要となるだろう.Javaという言語を学習し,クラスライブラリを理解することは苦痛ではない.環境のバグの所在を把握し,その回避方法を考案し,実用レベルの製品にクオリティを引き上げるために発生する消耗を負担に感じるのである.
 そう,正義を掲げるには体力が必要なのである.こうして人は,年齢を経るに従って正義感を失ってゆくのだろう.
 だから,今からプログラミングの学習を始める人には,Javaをおすすめする.少なくとも罪の意識をおぼえることは,当分の間ないだろう.それはつまり,しばらくの間Javaがビジネスにはならないということでもある.

Type5サーバーの完成

    今回はType5 DB Serverを紹介する.Type4まではデータベースアクセスとデータ転送の方法の違いだったが,Type5はType4と同じデータアクセスと転送を使用している.変更点は以下の通りである.
  1. 実行されたSQL文が間違っていた場合のエラーハンドリング
  2. SELECT文以外のSQL文も実行が可能
  3. 複数のSQL文の処理が一度の送信で可能
  4. ログイン時の並行処理が可能
 SQLのエラーバンドリングをすることで,間違ったSQL文が実行されてもサーバーがクラッシュしなくなった.SELECT以外のSQL文が実行できることで,データの検索だけでなく編集も行なうことができる.また,複数のSQL文の同時実行は,一度のリクエストで複数の結果セットを同時に取得したり,データ検索とデータ編集を同時に行なうことを可能にする.
 これらの変更はサーバー側で行なわれているため,クライアント側は従来と変わらない.しかし,複数SQL文の同時実行に対応して,SQL文の入力テキストボックスと,結果の表示ボックスのサイズを大きくしてある(図1).

とりあえず,DBサーバー完成

    Type5で,いったんデータベースサーバーは完成する.次号以降はクライアントの実装について書くつもりである.クライアントの実装について一通り終わったあと,再び,新しいサーバーを開発することを考えている.その場合もクライアントの実装方法は共通化される.
 新しいサーバーは,最初からマルチスレッドで動作することを前提とするようになるだろう.その頃には,マルチスレッドでも信頼できる,新しいデータベースエンジンが発表されているはずである.また,現行のデータベースサーバーで使用しているコールバック方式も廃止したい.やはり,サーバーが利用するポートを無制限に開いておくのには問題がある.
 このまま新しいサーバーの開発にかかることも可能だが,いつまでもクライアントがテキストしか処理できないのでは問題がある.やはりもう少し高水準なプログラミングインターフェイスが必要である.最終的にはマスター詳細型のフォームを持つデータベースアプリケーションを完成して,この連載は終了する.その時点で,そのシステムは汎用性があり,かつ安定していなければならない.もちろんパフォーマンスも大切な要素である.もうひとつはインターネット,エクストラネットへの対応である.

インターネットへの対応も…

    ここまでで作成したデータベースサーバーは,完全に,そのままインターネット上で利用できる.しかしクライアントは専用クライアントなので,Webベースで利用することはできない.それでもクライアントアプリケーションをインストールしておけば,インターネット経由でデータベースにアクセスできる.これは,現在のODBCベースのデータ接続に対して有利な点である.ODBCを使わないOLE DBインターフェイスが,このあたりをクリアしているのかどうか,まだ私は知らない.
 また,クライアントアプリケーションをWebベースで動作させる方法については,「ドクター・ドブズ・ジャーナル日本版」に掲載している連載の中で説明してゆくつもりである.本記事はデータベースソリューションの連載であって,Webアプリケーションの連載ではない.

   複数SQL文の同時実行に対応して,SQL文の入力テキストボックスと,結果の表示ボックスのサイズを大きく
図1:Type5 DB Serverの実行画

旧タイプとの互換性,および実行方法

    Type5 DB Serverは,外見的にはType4と何も変わらない.また,インストール方法や利用方法も同じである.Type5クライアントは外見は変化したが,機能は変わらない.つまり,Type3ないしType4クライアントを利用してType5 DB Serverにアクセスすることができる.もちろん,逆にType5クライアントでType4 DB Serverにアクセスすることもできる.
 だから,Visual Basic 5.0がインストールされている環境ならば,DBsvr.exeをActiveX EXEとして登録し,DBsvrRB.exeを起動することで利用できる.
 今試して初めて知ったのだが,Regsvr32.exeはActiveX EXEの登録には使用できない.ソースコードを所有しているVisual Basicプログラマならば,プロジェクトファイルから実行ファイルの作成をすればいいし,セットアッププログラムを作成してもよい.しかし,実行ファイルだけが手元にある場合は,どのように登録すればよいのだろう? Books Onlineでも実行ファイルの作成方法しか記述されていない.
 とにかく,この場合はソースがあるので,Visual Basicの開発環境で実行ファイルを作成すれば,登録は自動的に行なわれる.実行ファイルの作成時に気をつけなければいけないのは,プロジェクトのプロパティで,バージョン間の互換性でバイナリ互換を選択し,互換をとるプログラムファイルにDBsvr.exeを選択することである(図2).でないと,Visual Basicは新しいクラスIDを発行するので,このコンポーネントを利用するマルチユーザーマネージメントサーバーが保持しているクラスIDと違いが出てしまう.
 図2でスタートモードに「独立型」が選択されているのは,デバッグ時にスタンドアロンとして起動することがあるからである.通常の利用ではActiveXコンポーネントを選択すればよい.

   プロジェクトのプロパティで、バージョン間の互換性でバイナリ互換を選択し、互換をとるプログラムファイルにDBsvr.exeを選択
図2:DBsvr.vbpのプロパティ設定

追加されたコード

    DBアクセスサーバーの変更点は, WinsockコントロールのDataArrivalイベントプロシージャに集中している(リスト1).このプロシージャは,クライアントからデータ(リクエスト)を着信したときにデータアクセス処理を行ない,結果データを返送する.
 複数のSQL文を一度のリクエストで処理できるようにしたため,データベースへのアクセス処理全体が大きなループになっていることに注意してほしい.つまり,3つのSQL文が同時にリクエストされた場合は,ループ内の処理が3回実行される.ひとつのSQL文は改行によって区切って送信されることを前提としている.そのため,普通にSQL文を記述するときのように,ひとつのSQL文中に改行を含めると複数のSQL文として判断してしまい,実行エラーが発生する.もし,ターゲットデータベースエンジンがJetデータベース専用でよいならば,改行コードのかわりにセミコロンを使用すれば,この問題を回避することができる.

SELECT文以外のSQL

    次のコードは,実行されるSQLがSELECT文かどうかを判断する部分である.SELECT文以外が指定されている場合は結果を受けとる必要がないので,ConnectionオブジェクトのOpenResultsetメソッドではなく,Executeメソッドを使用する.
      If Left(UCase(sqlStat), 6) <> "SELECT" Then
  On Error GoTo ErrorHandler
  cn.Execute sqlStat, rdExecDirect
  On Error GoTo 0
Else

SQLのエラーハンドラの実装

    Execuetメソッドを実行する直前に指定されているエラーハンドラは,SQL文の実行時に,次のラベルに処理を移動する.
      ErrorHandler:
  SQLerrFlg = 1
  Winsock1.SendData "SQL Error" & vbCrLf
  Resume Next
    エラーフラグを格納する変数SQLerrFlgに1が格納され,クライアントにエラーの発生した旨が通知される.処理はExceuteメソッドの直後にもどる.実はエラーフラグに格納された内容は,この後参照していない.このフラグを利用するのは,SELECT文に間違ったSQLが指定された場合である.

SQLがSELECT文の場合

    次のコードはSQLにSELECT文が指定され,OpenResultsetメソッドが実行される部分である.
      SQLerrFlg = 0
On Error GoTo ErrorHandler
Set rs = cn.OpenResultset(sqlStat, rdOpenStatic, rdConcurReadOnly, rdExecDirect)
On Error GoTo 0
If SQLerrFlg = 0 Then
    ここでもSQLの実行直前にエラーハンドラが指定され,同様の処理が実行される.エラーが発生した場合は,変数SQLerrFlgに1が格納されるので,条件文以降の処理は実行されない.結果として結果セットの返信処理は実行されずに処理は終了する.
リスト1:WinsockコントロールのDataArrivalイベントプロシージャ
      Private Sub Winsock1_DataArrival(ByVal bytesTotal As Long)
  Dim strData As String
  Dim RetResult As String
  Dim i As Integer
  Dim MaxfieldCount As Integer
  Dim RecCount As Integer
  Dim startPoint As Long
  Dim sqlStatLen As Long
  Dim strPosition As Long
  Dim sqlStat As String
  Dim SQLerrFlg As Integer
  Dim Delimit As String


  ' 計測を開始
  mlngStartTime = timeGetTime()
  Delimit = vbCrLf

  ' クライアントからSQL文を受け取る
  Winsock1.GetData strData, vbString
  strData = Trim(strData)
  txt_sql.Text = strData
  strPosition = -1
  Do While True
    startPoint = strPosition + 2 ' SQL文の開始位置は前回の終了位置の2文字後ろ
    ' SQL文の開始位置以降の改行位置を取得し、SQL文の終了位置を判断
    strPosition = InStr(startPoint, strData, Delimit)
    If strPosition = 0 Then ' 切り出すSQL文が最後の場合
      sqlStatLen = Len(strData) + 1 - startPoint
      If sqlStatLen < 1 Then
        Exit Do
      Else
        strPosition = Len(strData) + 1
      End If
    Else
      sqlStatLen = strPosition - startPoint
    End If

    sqlStat = Trim(Mid(strData, startPoint, sqlStatLen))
    If Len(sqlStat) > 1 Then ' SQL文がある場合のみ処理

      ' SQL文がSELECT文であるかどうかを判断して処理を変更
      ' Winsock1.SendData vbCrLf & "SQL statement : _
      ' [" & sqlStat & "]" & vbCrLf & vbCrLf
      If Left(UCase(sqlStat), 6) <> "SELECT" Then
        On Error GoTo ErrorHandler
        cn.Execute sqlStat, rdExecDirect
        On Error GoTo 0
        ' Dim er As rdoErrors
      Else
        ' 検索を実行
        SQLerrFlg = 0
        On Error GoTo ErrorHandler
        Set rs = cn.OpenResultset(sqlStat, rdOpenStatic, _
             rdConcurReadOnly, rdExecDirect)
      On Error GoTo 0
        ' 検索内容の送信
        If SQLerrFlg = 0 Then ' エラーがなければ、返信処理を実施
          MaxfieldCount = rs.rdoColumns.Count ' 項目数を取得
          RetResult = ""
          RecCount = 0
          Winsock1.SendData "Result Start : _
           [" & sqlStat &amp "]" & vbCrLf & vbCrLf

          Do While Not rs.EOF ' 結果セットをクライアントに送信
            RecCount = RecCount + 1
            For i = 0 To MaxfieldCount - 1 ' 全項目を返信結果に含める
              RetResult = RetResult & rs(i) & ";"
            Next
            RetResult = RetResult & vbCrLf
            rs.MoveNext
            ' 100レコードごとに結果を返信しバッファをクリア
            If RecCount = 100 Or rs.EOF Then
              RecCount = 0
              Winsock1.SendData RetResult
              RetResult = ""
              If rs.EOF Then
                Exit Do
              End If
            End If
          Loop

          Winsock1.SendData vbCrLf & "Result End : _
           [" & sqlStat & "]" & vbCrLf & vbCrLf
          rs.Close
          Set rs = Nothing
        End If ' SQLerrflg
      End If ' If Left(UCase(sqlStat), 6) <> "SELECT" Then
    End If ' If Len(Trim(sqlStat)) > 1 Then ' SQL文がある場合のみ処理
  Loop

  ' 計測を終了
  mlngEndTime = timeGetTime()
  lbl_Time.Caption = (mlngEndTime - mlngStartTime) / 1000
Exit Sub

ErrorHandler: ' SQLの実行エラー
  SQLerrFlg = 1 'エラーフラグをたてる
  Winsock1.SendData "SQL Error" & vbCrLf
  Resume Next
End Sub

クライアントの実装に備えて

    コードはたいぶ複雑なったが,データベースサーバーとしての実用性は格段に向上した.次号以降では,今回新しく追加した機能を,クライアントから活用する.そのためType5クライアントでは,結果セットの出力スタイルが変更されている.
 たとえば,次のようなSQL文を実行した場合,取得される結果はリスト2のようになる.
      SELECT * FROM Authors WHERE Au_ID < 5
SELECT * FROM Authors WHERE Au_ID >= 5 AND Au_ID < 10

リスト2:Type5 DBサーバーによって取得される結果セット
      Result Start :[SELECT * FROM Authors WHERE Au_ID < 5]

1;Jacobs, Russell;;
2;Metzger, Philip W.;;
3;Boddie, John;;
4;Sydow, Dan Parks;;

Result End :[SELECT * FROM Authors WHERE Au_ID < 5]

Result Start :[SELECT * FROM Authors WHERE Au_ID >= 5 AND Au_ID < 10]

6;Lloyd, John;;
8;Thiel, James R.;;

Result End :[SELECT * FROM Authors WHERE Au_ID >= 5 AND Au_ID < 10]
    各結果セットの前後にRsult StartとResult Endという文字列が挿入されている.これらの文字列により,結果セットを区切ることでクライアント側での判別を容易にしている.

 今後,クラアント側を強化してゆくしたがって,今までのように単にデータを表示するだけのアプリケーションから,より実用的なアプリケーションへと成長してゆく.サーバー側は多分に実験的な要素が多かったのに対して,クライアント側はより現実的になるはずである.
 実はクライアント側の実装に関しては,Visual Basic 6.0で搭載される新しいコントロールや機能に期待するものがあったのだが,私が現在把握している範囲では,あまり,新しいアイテムはない.次号の原稿ではVisual Basic 6.0を使うかどうかは,微妙なところである.私のところでは,パッケージ製品の開発には,引き続きVisual Basic 5.0を使用することを決定した.


VB Magazine ライブラリ | Visual Basic WorkGroup
int21 ホームページ | PCDN ホームページ


Copyright (c) 1998 PCDN All Rights Reserved.
For questions or comments, please send mail to: pcdn@int21.co.jp