福岡寿和 TOSHIKAZU, Fukuoka
図1
では、一つ一つの方法について、Microsoft SQL Server 6.5(MS SQL Server)と接続するには、具体的にどのようにプログラムすればよいのかを見ていきたいと思います。Excelでプログラムするときは、Visual Basic for Applications (以下、VBA)を使ってプログラムします。VBAは、Visual Basicと兄弟のようなもので若干の差はあるものの基本的な部分はほぼ同じ文法になっています。ですから、VBAを使ったサンプルがないときは、Visual Basicのサンプルから使い方を類推することも可能です。ただし、そのオブジェクト構造は、Applicationオブジェクトを頂点としたExcelに特化した構造になっています(図2)。
図2
なお、本記事は、Microsoft Office97 Professional VersionのMicrosoft Excel 97をMicrosoft Windows 95 Ver4.00.950B / IE 4.0 4.71.1712.6で動作させて確認した内容に基いて記述しています。Microsoft SQL Server 6.5にはService Pack 3を当ててあります。細かなバージョンの違いで動作が異なることはないと思いますが、動作がおかしいときには、まずソフトウェアやOSのバージョンを疑って見てください。
|
Excelプログラミングの基本 〜オブジェクト関連のVBA〜 |
|---|
|
Range("A1").Value = 1 |
|
Worksheets("Sheet1").Range("A1").Value = 1 |
|
Dim <変数名> As <オブジェクト型名> |
|
Dim xlsshtUI As Sheet |
| Set <オブジェクト変数> = <オブジェクト> |
|
Worksheets("Sheet1").Range("A1").Value = 1 |
|
Set xlsshtUI = Worksheets("Sheet1") : XlsshtUI.Range("A1").Value = 1 |
|
With <オブジェクト> : .<プロパティ> .<メソッド> .<下層のオブジェクト> : End With |
|
With Worksheets("Sheet1").Cells(1, 1) .Formula = "=SQRT(50)" With .Font .Name = "Arial" .Bold = True .Size = 8 End With End With |
|
For Each <変数> In <コレクション> <処理> Next |
|
Excelプログラミングの基本 〜UIシートとデータシートの分離〜 |
|---|
図3
この形態ならば、データシートには書式設定も必要ないので、データ量が増加しても1ワークシートにしたときよりも、ファイルサイズが余り増加せず、転記スピードの点でも有利です。
それではまず、どのようにデータシートにMS SQL Serverのデータを転記したらよいのかを検討したいと思います。
|
データシートを作成する 〜Microsoft Query(MS Query)を使う |
|---|
図4
図5
図6
図7
図8
図9
図10
図11
|
データシートを作成する 〜DAO/Jet3.5を使う〜 |
|---|
リスト1 DAO/Jet3.5サンプル
Private Sub Workbook_Open()
Dim strRange As String 'コピー先
Dim ilngLoop As Long 'ループカウンタ
Dim ilngCol As Long 'ループカウンタ
Dim dbsSQL As Database 'データベース
Dim rdynSQL As Recordset 'レコードセット
Dim strSQL As String 'SQL文
Dim strConnect As String 'Connect文字列
strSQL = "SELECT * from authors"
strConnect = "ODBC; UID=sa;PWD="
'1. ODBCデータソースを指定してデータベースを開く
Set dbsSQL = Workspaces(0).OpenDatabase("SQLS", False, False, strConnect)
'2. SQL文を指定してレコードセットを開く
Set rdynSQL = dbsSQL.OpenRecordset(strSQL, dbOpenDynaset)
With Worksheets("データシート")
ilngLoop = 2
'3. レコードセットをすべて読み切るまでC〜Dを繰り返す
Do While Not rdynSQL.EOF
'4. カレントレコードの全フィールドをExcelワークシートのカレント行の列に設定
For ilngCol = 0 To rdynSQL.Fields.Count - 1
.Cells(ilngLoop, ilngCol + 1) = rdynSQL(ilngCol)
Next
'5. ワークシートのカレント行を移動し、次のレコードを取得
ilngLoop = ilngLoop + 1
rdynSQL.MoveNext
Loop
'6. レコードセットを閉じる
rdynSQL.Close
'7. データベースを閉じる
dbsSQL.Close
End With
With Worksheets("UIシート")
.Range("5:6").Copy
For ilngLoop = 7 To 27 Step 2
strRange = CStr(ilngLoop) & ":" & CStr(ilngLoop + 1)
.Range(strRange).Select
ActiveSheet.Paste
Next
End With
exitOpen:
On Error Resume Next
rdynSQL.Close
dbsSQL.Close
Exit Sub
errOpen:
MsgBox "WorkBook_Open:" & Error&, vbOKOnly & vbExclamation, app.Title
Resume exitOpen:
End Sub
|
|
データシートを作成する 〜ODBC Directを使う〜 |
|---|
リスト2 ODBC Directサンプル(抜粋)
Private Sub Workbook_Open()
Dim strRange As String 'コピー先
Dim ilngLoop As Long 'ループカウンタ
Dim ilngCol As Long 'ループカウンタ
Dim wrkODBC As Workspace 'ワークスペース
Dim dbsSQL As Database 'データベース
Dim rdynSQL As Recordset 'レコードセット
Dim strSQL As String 'SQL文
Dim strConnect As String 'Connect文字列
strSQL = "SELECT * from authors"
strConnect = "ODBC; UID=sa;PWD="
'ODBC Directワークスペースを開く
Set wrkODBC = CreateWorkspace("ODBC", "admin", "", dbUseODBC)
'1. ODBCデータソースを指定してデータベースを開く
Set dbsSQL = wrkODBC.OpenDatabase("SQLS", False, False, strConnect)
'2. SQL文を指定してレコードセットを開く
Set rdynSQL = dbsSQL.OpenRecordset(strSQL, dbOpenDynaset)
With Worksheets("データシート")
ilngLoop = 2
'3. レコードセットをすべて読み切るまでC〜Dを繰り返す
Do While Not rdynSQL.EOF
'4. カレントレコードの全フィールドをExcelワークシートのカレント行の列に設定
For ilngCol = 0 To rdynSQL.Fields.Count - 1
.Cells(ilngLoop, ilngCol + 1) = rdynSQL(ilngCol)
Next
'5. ワークシートのカレント行を移動し、次のレコードを取得
ilngLoop = ilngLoop + 1
rdynSQL.MoveNext
Loop
End With
'6. レコードセットを閉じる
rdynSQL.Close
'7. データベースを閉じる
dbsSQL.Close
'ODBC Directワークスペースを閉じる
wrkODBC.Close
Set wrkODBC = Nothing
With Worksheets("UIシート")
.Range("5:6").Copy
For ilngLoop = 7 To 27 Step 2
strRange = CStr(ilngLoop) & ":" & CStr(ilngLoop + 1)
.Range(strRange).Select
ActiveSheet.Paste
Next
End With
exitOpen:
On Error Resume Next
rdynSQL.Close
dbsSQL.Close
Exit Sub
errOpen:
MsgBox "WorkBook_Open:" & Error&, vbOKOnly & vbExclamation, app.Title
Resume exitOpen:
End Sub
|
|
データシートを作成する 〜RDO2.0を使う〜 |
|---|
図12
リスト3 RDO2.0サンプル
Private Sub Workbook_Open()
Dim strRange As String 'コピー先
Dim ilngLoop As Long 'ループカウンタ
Dim ilngCol As Long 'ループカウンタ
Dim rdcnnRDO As rdoConnection 'コネクション
Dim rdrslRDO As rdoResultset '結果セット
Dim strSQL As String 'SQL文
Dim strConnect As String 'Connect文字列
strSQL = "SELECT * from authors"
strConnect = "DSN=SQLS;UID=sa;PWD=;"
'1. ODBCデータソースを指定してデータベースを開く
Set rdcnnRDO = rdoEnvironments(0).OpenConnection("", rdDriverNoPrompt, True, strConnect)
'2. SQL文を指定して結果セットを開く
Set rdrslRDO = rdcnnRDO.OpenResultset(strSQL, rdOpenKeyset)
With Worksheets("データシート")
ilngLoop = 2
'3. 結果セットをすべて読み切るまでC〜Dを繰り返す
Do While Not rdrslRDO.EOF
'4. カレントレコードの全フィールドをExcelワークシートのカレント行の列に設定
For ilngCol = 0 To rdrslRDO.rdoColumns.Count - 1
.Cells(ilngLoop, ilngCol + 1) = rdrslRDO(ilngCol)
Next
'5. ワークシートのカレント行を移動し、次のレコードを取得
ilngLoop = ilngLoop + 1
rdrslRDO.MoveNext
Loop
End With
'6. 結果セットを閉じる
rdrslRDO.Close
'7. データベースを閉じる
rdcnnRDO.Close
With Worksheets("UIシート")
.Range("5:6").Copy
For ilngLoop = 7 To 27 Step 2
strRange = CStr(ilngLoop) & ":" & CStr(ilngLoop + 1)
.Range(strRange).Select
ActiveSheet.Paste
Next
End With
exitOpen:
On Error Resume Next
rdrslRDO.Close
rdcnnRDO.Close
Exit Sub
errOpen:
MsgBox "WorkBook_Open:" & Error&, vbOKOnly & vbExclamation, app.Title
Resume exitOpen:
End Sub
|
|
検索速度を向上させるには |
|---|
リスト4 検索速度改善後のODBC Directサンプル
Option Explicit
Public plngRow As Long
Private Sub cmdExit_Click()
ActiveWorkbook.Close savechanges:=False
End Sub
Private Sub cmdNext_Click()
Dim ilngLoop As Long
plngRow = plngRow + 10
If plngRow >= Worksheets("データシート").Cells(1, 1) - 10 Then
cmdNext.Enabled = False
End If
For ilngLoop = 0 To 9
Cells(ilngLoop * 2 + 5, 1) = "=データシート!A" & CStr(plngRow + ilngLoop + 2)
Cells(ilngLoop * 2 + 5, 2) = "=データシート!B" & CStr(plngRow + ilngLoop + 2)
Cells(ilngLoop * 2 + 5, 3) = "=データシート!C" & CStr(plngRow + ilngLoop + 2)
Cells(ilngLoop * 2 + 5, 4) = "=データシート!D" & CStr(plngRow + ilngLoop + 2)
Cells(ilngLoop * 2 + 6, 2) = "=データシート!E" & CStr(plngRow + ilngLoop + 2) & " & "","" & " & _
"データシート!F" & CStr(plngRow + ilngLoop + 2) & " & "","" & " & _
"データシート!G" & CStr(plngRow + ilngLoop + 2) & " & "","" & " & _
"データシート!H" & CStr(plngRow + ilngLoop + 2)
Next
cmdPrev.Enabled = True
End Sub
Private Sub cmdPrev_Click()
Dim ilngLoop As Long
plngRow = plngRow - 10
If plngRow = 0 Then
cmdPrev.Enabled = False
End If
For ilngLoop = 0 To 9
Cells(ilngLoop * 2 + 5, 1) = "=データシート!A" & CStr(plngRow + ilngLoop + 2)
Cells(ilngLoop * 2 + 5, 2) = "=データシート!B" & CStr(plngRow + ilngLoop + 2)
Cells(ilngLoop * 2 + 5, 3) = "=データシート!C" & CStr(plngRow + ilngLoop + 2)
Cells(ilngLoop * 2 + 5, 4) = "=データシート!D" & CStr(plngRow + ilngLoop + 2)
Cells(ilngLoop * 2 + 6, 2) = "=データシート!E" & CStr(plngRow + ilngLoop + 2) & " & "","" & " & _
"データシート!F" & CStr(plngRow + ilngLoop + 2) & " & "","" & " & _
"データシート!G" & CStr(plngRow + ilngLoop + 2) & " & "","" & " & _
"データシート!H" & CStr(plngRow + ilngLoop + 2)
Next
If plngRow <= Worksheets("データシート").Cells(1, 1) - 10 Then
cmdNext.Enabled = True
End If
End Sub
|
図13
|
ユーザーフォームを作成する |
|---|
図14
ここでのポイントは、入力データはあくまでExcel上のデータシートに反映し、RDBMSには即座に反映しない点にあります。これは速度向上による操作性向上も目的の一つですが、RDBMS上のデータに影響を与えずに、合計値や分析結果の変化を確認する仮更新機能を目的としているからです。仮更新機能があれば、他のユーザーに気兼ねなくデータ値の変更を試行錯誤できて、Excelの分析ツールとしての局面を有効利用することができます。
図15
図16
Windowsユーザーインターフェイスデザインガイドなどを参考にして、ユーザーフォームにコントロールを配置します。
リスト5 ユーザーフォームサンプル(抜粋)
Private Sub Worksheet_BeforeRightClick(ByVal Target As Excel.Range, Cancel As Boolean)
Dim lngRow As Long
'1. ユーザーフォームに表示情報を設定します。
lngRow = (Row - 5) \ 2 + 2
With frmUser
.txtID = Worksheets("データシート").Cells(lngRow, 1)
.txtlname = Worksheets("データシート").Cells(lngRow, 2)
.txtFname = Worksheets("データシート").Cells(lngRow, 3)
.txtPhone = Worksheets("データシート").Cells(lngRow, 4)
.txtAddr = Worksheets("データシート").Cells(lngRow, 5)
.txtCity = Worksheets("データシート").Cells(lngRow, 6)
.txtState = Worksheets("データシート").Cells(lngRow, 7)
.txtZIP = Worksheets("データシート").Cells(lngRow, 8)
End With
'2. ユーザーフォームのShowメソッドを使って、ユーザーフォームを表示します。
frmUser.Show
'5. ユーザーフォームを消去します。
Set frmUser = Nothing
End Sub
Private Sub cmdOK_Click()
Dim lngRow As Long
'3. 表示情報を「データシート」に転記して、フォームを閉じます。
lngRow = CLng(Tag)
With Worksheets("データシート")
.Cells(lngRow, 1) = txtID
.Cells(lngRow, 2) = txtLname
.Cells(lngRow, 3) = txtFname
.Cells(lngRow, 4) = txtPhone
.Cells(lngRow, 5) = txtAddr
.Cells(lngRow, 6) = txtCity
.Cells(lngRow, 7) = txtState
.Cells(lngRow, 8) = txtZIP
End With
Unload Me
End Sub
Private Sub cmdCancel_Click()
'4. 転記は行わずフォームを閉じます。
Unload Me
End Sub
|
|
データとアルゴリズムを分離するには |
|---|
リスト6 データとアルゴリズムの分離サンプル
Option Explicit
Private Sub Workbook_Open()
Dim strRange As String 'コピー先
Dim ilngLoop As Long 'ループカウンタ
Dim ilngCol As Long 'ループカウンタ
Dim wrkODBC As Workspace 'ワークスペース
Dim dbsSQL As Database 'データベース
Dim rdynSQL As Recordset 'レコードセット
Dim strSQL As String 'SQL文
Dim strConnect As String 'Connect文字列
Dim strName As String '生成時ブック名
Dim strNewName As String '新ブック名
'1. テンプレートを開きます。
Workbooks.Add (ActiveWorkbook.Path & "\SEP.xlt")
strName = ActiveWorkbook.Name & ".xls"
'2. 保存先のファイル名を指定します。
If Application.Dialogs(xlDialogSaveWorkbook).Show Then
strNewName = ActiveWorkbook.Name
End If
'3. 開いたテンプレートのデータシートに値を設定します。
strSQL = "SELECT * from authors"
strConnect = "ODBC; UID=sa;PWD="
'ODBC Directワークスペースを開く
Set wrkODBC = CreateWorkspace("ODBC", "admin", "", dbUseODBC)
'ODBCデータソースを指定してデータベースを開く
Set dbsSQL = wrkODBC.OpenDatabase("SQLS", False, False, strConnect)
'SQL文を指定してレコードセットを開く
Set rdynSQL = dbsSQL.OpenRecordset(strSQL, dbOpenDynaset)
With ActiveWorkbook.Worksheets("データシート")
ilngLoop = 2
'レコードセットをすべて読み切るまでC〜Dを繰り返す
Do While Not rdynSQL.EOF
'カレントレコードの全フィールドをExcelワークシートのカレント行の列に設定
For ilngCol = 0 To rdynSQL.Fields.Count - 1
.Cells(ilngLoop, ilngCol + 1) = rdynSQL(ilngCol)
Next
'ワークシートのカレント行を移動し、次のレコードを取得
ilngLoop = ilngLoop + 1
rdynSQL.MoveNext
Loop
End With
'レコードセットを閉じる
rdynSQL.Close
'データベースを閉じる
dbsSQL.Close
'ODBC Directワークスペースを閉じる
wrkODBC.Close
Set wrkODBC = Nothing
If ilngLoop >= 12 Then
ActiveWorkbook.Worksheets("UIシート").cmdNext.Enabled = True
End If
exitOpen:
On Error Resume Next
rdynSQL.Close
dbsSQL.Close
'総レコード数をデータシートに設定する
Workbooks(strName).Worksheets("データシート").Cells(1, 1) = ilngLoop - 2
If strNewName <> "" Then
'4. 指定されたファイル名で値が設定されたブックを保存します。
Workbooks(strName).Activate
ActiveWorkbook.SaveAs (strNewName)
End If
Exit Sub
errOpen:
MsgBox "WorkBook_Open:" & Error$, vbOKOnly & vbExclamation
Resume exitOpen:
End Sub
|
|
おまけ 〜Oracle Objects for OLE(oo4o)を使う〜 |
|---|
リスト7 oo4oサンプル
Private Sub Workbook_Open()
Dim objSess As Object
Dim objDb As Object
Dim objDs As Object
Dim objCol As Object
Dim strError As String
Dim ilngLoop As Long
Dim strRange As String
On Error GoTo errOpen:
'1. ActiveXDLLであるoo4oを起動します。
Set objSess = CreateObject("OracleInProcServer.XOraSession")
'2. SQL*Net接続文字列を指定して、Oracleと接続します。
Set objDb = objSess.OpenDatabase("PCDN", "scott/tiger", 0&)
'3. SQL文を指定してレコードを取得します。
Set objDs = objDb.DbCreateDynaset("select * from emp", 12&)
Set objCol = objDs.Fields
For ilngLoop = 1 To objCol.Count
Worksheets("データシート").Cells(1, ilngLoop).Value = objCol(ilngLoop - 1).Name
Next
'4. CopyToClipboardを使って、クリップボードに全レコードを転記します。
objDs.CopyToClipboard -1
'5. クリップボードからデータシートに転記します。
Sheets("データシート").Select
Range("A2").Select
ActiveSheet.Paste
Sheets("UIシート").Select
'6. レコードセットを開放します。
Set objDs = Nothing
'7. Oracleとの接続を開放します。
Set objDb = Nothing
'8. oo4oとの接続を開放します。
Set objSess = Nothing
With Worksheets("UIシート")
.Range("5:6").Copy
For ilngLoop = 7 To 27 Step 2
strRange = CStr(ilngLoop) & ":" & CStr(ilngLoop + 1)
.Range(strRange).Select
ActiveSheet.Paste
Next
End With
exitOpen:
On Error Resume Next
Set objDs = Nothing
Set objDb = Nothing
Set objSess = Nothing
Exit Sub
errOpen:
MsgBox "WorkBook_Open:" & Error$, vbOKOnly & vbExclamation
Resume exitOpen:
End Sub
|
さて、ExcelのVBAを使ってRDBMSと連携する方法を何通りか紹介してきました。特に参照系や集計系などのシステムではVisual Basicで作成したときに匹敵するものがExcelで作れると思います。また、RDBMSの値を使ってシュミレーション(試算)するようなものでは、Visual Basicを使うよりも柔軟なシステムを構築できる可能性があります。Microsoft Officeの中でも一番完成度が高く、最も利用されているExcelをクライアントアプリケーションの中核にしたシステム開発を検討する価値は充分あると言えるでしょう。
VB Magazine ライブラリ | Visual Basic Workgroup
int21 ホームページ | PCDN ホームページ