- ホーム
- Other
- exceltable
エクセルのテーブルからAccessデータベースに接続して、データを取得してみた
エクセルのテーブルの機能で、Accessのデータベースに接続してデータを取得できる事を知り使ってみた。
でも、Accessのテーブルや、既存のクエリーからデータを取得するだけなのに飽き足らなくなって、
Excel VBAからアクセスのクエリの抽出条件の変更を試みた。
一応は出来たけれど、エクセルのテーブルはAccessのデータベースに接続すると、占有してしまう事が分かった。
Accessにエクセルテーブルから接続した状態でAccessデータベースを開こうとすると、読み取り専用で開かれるし
逆にAccessデータベースを開いた状態でエクセルの接続ファイルを開くと、接続更新の際にエラーになる。
「このデータベースは、マシン 'hoge' のユーザー 'Admin' が開いています。データベースが使用可能になった時点で、再度実行してください。」
結局VBA使いにとっては、ADOやDAOで接続する方が、Accessデータベースを占有せず、使い勝手が良い事が分かった。
AccessDBに接続したテーブル(QueryTable?)を生成するコード(ADOでクエリの条件設定用テーブル操作を含む)と、
どうせADOを使うなら、やっぱりADOオンリーの方が良いじゃん。ということで、RecordSetを貼り付け後、
テーブル(LinkObject)に変換するコードを載せます。
'Microsoft ActiveX Data Objects x.x Library に参照設定
'====================================================
'Access側で抽出期間設定用のTableを抽出条件に用いたクエリを作成しておく
'抽出期間設定用のTableは、抽出開始日と抽出終了日の二つのフィールドを持ち、使用都度レコード全削除後、一レコードのみ収納する。
'エクセルからADOで抽出期間設定用のTableに条件を設定後、エクセルのテーブルから、その条件を用いたクエリに接続する。
'エクセルでTableがACCDBに接続していると、ADOでのアクセスが出来ない様なので、一旦Tableのリンク削除、テーブル削除後、
'抽出用テーブルに値を設定し、クエリをデータソースにしたTableを設定する
'そのテーブルを元にグラフを更新する。(グラフ用のシートにグラフが一個だけ貼り付けてある事が前提)
'DAOでQueryDefしても同じだけどね...
Sub changeQueryCondition()
Dim con As New ADODB.Connection
Dim rs As New ADODB.Recordset
Dim myDataSource As String
Dim sourcefilename As String
Dim destSh As Worksheet
Dim graphSh As Worksheet
Dim mySQL As String
Dim myStartDate As String, myEndDate As String
Dim myTable As ListObject
Dim myTableName As String
Dim myTableRange As Range
Dim i As Long
Const dataSheetName As String = "Sheet4"
Const graphSheetName As String = "Sheet3"
Const periodStartAddress As String = "$N$2"
Const periodEndAddress As String = "$N$4"
sourcefilename = "20150620抽出テストデータ.accdb"
myDataSource = ThisWorkbook.Path & "\" & sourcefilename
myTableName = "myTable2"
Set destSh = ThisWorkbook.Sheets(dataSheetName)
Set graphSh = ThisWorkbook.Sheets(graphSheetName)
'check myTable1 exists
Set myTable = destSh.Range("A1").ListObject
If Not myTable Is Nothing Then
If myTable.Name = myTableName Then myTable.Delete
End If
'date error check
With graphSh
myStartDate = .Range(periodStartAddress).Value
myEndDate = .Range(periodEndAddress).Value
If (Not IsDate(myStartDate)) Or (Not IsDate(myEndDate)) Then Exit Sub
If DateValue(.Range(periodStartAddress).Value) >= DateValue(.Range(periodEndAddress).Value) Then Exit Sub
End With
'カレントディレクトリのデータベースパスを取得
myDataSource = ThisWorkbook.Path & "\" & sourcefilename
'エクセルのテーブルを削除
On Error Resume Next
myTableName = "テーブル__" & sourcefilename
destSh.ListObjects(myTableName).Unlink
destSh.ListObjects(myTableName).Delete
On Error GoTo 0
'データベースに接続する
con.ConnectionString = "provider=Microsoft.ACE.OLEDB.12.0;" _
& "Data Source=" & myDataSource & ""
con.Open
con.Execute "DELETE * FROM T_period;"
'endというフィールド名を用いると、INSERT文の誤りという訳の分からんエラーが出た
'SQLでもendというのは何か意味を持つ様だ
mySQL = "INSERT INTO T_period (startDate,endDate) VALUES (#myStartDate#,#myEndDate#);"
mySQL = Replace(mySQL, "myStartDate", myStartDate)
mySQL = Replace(mySQL, "myEndDate", myEndDate)
con.Execute mySQL
con.Close
Set con = Nothing
DoEvents: DoEvents: DoEvents
'クエリに接続したテーブル(エクセルの)を作成
makeLinkedTable myDataSource, "Q_selectWithDate", destSh.Range("A1")
destSh.Range(myTableName).Columns(1).Cells.NumberFormatLocal = "yyyy/m/d h:mm;@"
'redraw graph
graphSh.ChartObjects(1).Chart.SetSourceData destSh.Range(myTableName)
End Sub
'ACCDBに接続したテーブルを生成する
'引数 ACCDBファイルへの絶対パス、対象とするテーブル・クエリ名、貼り付け先のセル)
Sub makeLinkedTable(myDataSource As String, Target As String, destCell As Range)
Dim myQueryTable As QueryTable
Dim myListObj As ListObject
Dim currentSh As Worksheet
Set currentSh = ActiveSheet
Application.ScreenUpdating = False
'TableはActivesheetにしか設けられないらしい
destCell.Parent.Activate
' https://msdn.microsoft.com/ja-jp/library/office/ff198271.aspx Officeデベロッパーセンター QueryTableオブジェクト
Set myQueryTable = ActiveSheet.ListObjects.Add(SourceType:=0, Source:=Array( _
"OLEDB;Provider=Microsoft.ACE.OLEDB.12.0;Password="""";User ID=Admin;Data Source=" & myDataSource _
, ";Mode=Share Deny Write;Extended Properties="""";Jet OLEDB:System database="""";Jet OLEDB:Registry Path="""";Jet OLEDB:Database Password=""" _
, """;Jet OLEDB:Engine Type=6;Jet OLEDB:Database Locking Mode=0;Jet OLEDB:Global Partial Bulk Ops=2;Jet OLEDB:Global Bulk Transactions=1;" _
, "Jet OLEDB:New Database Password="""";Jet OLEDB:Create System Database=False;Jet OLEDB:Encrypt Database=False;" _
, "Jet OLEDB:Don't Copy Locale on Compact=False;Jet OLEDB:Compact Without Replica Repair=False;Jet OLEDB:SFP=False;" _
, "Jet OLEDB:Support Complex Data=False;Jet OLEDB:Bypass UserInfo Validation=False"), Destination:=destCell).QueryTable
With myQueryTable
.CommandType = xlCmdTable
'これがリンクするクエリ(またはテーブル)指定箇所
.CommandText = Array(Target)
'行番号を含まない
.RowNumbers = False
'クエリ テーブルを更新するとき、指定されたクエリ テーブルの右側の数式を自動的に更新しない
.FillAdjacentFormulas = False
'先頭の 5 行のデータに共通する書式をクエリ テーブルの新しい行のデータに適用
.PreserveFormatting = True
'ファイルオープン時に更新しない
.RefreshOnFileOpen = False
'バックグラウンドクエリ
.BackgroundQuery = True
'変更されたレコード(行)のデータ更新時の処理
.RefreshStyle = xlInsertDeleteCells
.SavePassword = False
'ピボットテーブル レポートのデータをブックと共に保存
.SaveData = True
'列の幅を調整する
.AdjustColumnWidth = True
.RefreshPeriod = 0
'クエリ テーブルが更新されるごとに列の並べ替え、フィルタ設定、レイアウト情報を保存(旧バージョン互換はfalse)
.PreserveColumnInfo = True
'ファイル ベースのDB (Accessなど) の場合ソース データ ファイルの絶対パスが含まれる
.SourceDataFile = myDataSource
.ListObject.DisplayName = "テーブル__" & Right(myDataSource, Len(myDataSource) - InStrRev(myDataSource, "\"))
.Refresh BackgroundQuery:=False
End With
currentSh.Activate
Application.ScreenUpdating = True
End Sub
'====================================================
'結局普通にADOで全部やる方が、ACCDBを占有する事も無くGood
Sub extract()
Dim con As ADODB.Connection
Dim rs As ADODB.Recordset
Dim myDataSource As String
Dim sourcefilename As String
Dim destSh As Worksheet
Dim graphSh As Worksheet
Dim mySQL As String
Dim myStartDate As String, myEndDate As String
Dim myTable As ListObject
Dim myTableName As String
Dim myTableRange As Range
Dim i As Long
Const dataSheetName As String = "Sheet2"
Const graphSheetName As String = "Sheet1"
Const periodStartAddress As String = "$N$2"
Const periodEndAddress As String = "$N$4"
sourcefilename = "20150620抽出テストデータ.accdb"
myDataSource = ThisWorkbook.Path & "\" & sourcefilename
myTableName = "myTable1"
Set destSh = ThisWorkbook.Sheets(dataSheetName)
Set graphSh = ThisWorkbook.Sheets(graphSheetName)
'check myTable1 exists
Set myTable = destSh.Range("A1").ListObject
If Not myTable Is Nothing Then
If myTable.Name = myTableName Then myTable.Delete
End If
'date error check
With graphSh
myStartDate = .Range(periodStartAddress).Value
myEndDate = .Range(periodEndAddress).Value
If (Not IsDate(myStartDate)) Or (Not IsDate(myEndDate)) Then Exit Sub
If DateValue(.Range(periodStartAddress).Value) >= DateValue(.Range(periodEndAddress).Value) Then Exit Sub
End With
Set con = New ADODB.Connection
Set rs = New ADODB.Recordset
rs.CursorLocation = adUseClient 'for Use recordcount
'connection
con.ConnectionString = "provider=Microsoft.ACE.OLEDB.12.0;" & "Data Source=" & myDataSource
con.Open
'make SQL
mySQL = "SELECT dateAndTime, Data1, Data2, Data3 FROM T_table1 WHERE ((dateAndTime)>=#myStartDate# And (dateAndTime)<=#myEndDate#);"
mySQL = Replace(mySQL, "myStartDate", myStartDate)
mySQL = Replace(mySQL, "myEndDate", myEndDate)
rs.Open mySQL, con, adOpenKeyset, adLockReadOnly
With destSh
'Column Header
For i = 1 To rs.Fields.Count
.Cells(1, i).Value = rs.Fields(i - 1).Name
Next i
.Cells(2, 1).CopyFromRecordset rs
Set myTableRange = .Range("A1").Resize(rs.RecordCount, rs.Fields.Count) ' - 1)
myTableRange.Columns(1).Cells.NumberFormatLocal = "yyyy/m/d h:mm;@"
End With
Set myTable = destSh.ListObjects.Add(xlSrcRange, myTableRange, , xlYes)
myTable.Name = myTableName
'redraw graph
'graphSh.ChartObjects(1).Chart.SetSourceData myTableRange
'Using structured references
With graphSh.ChartObjects(1).Chart
.SetSourceData destSh.Range(myTableName & "[#Data]")
For i = 1 To .SeriesCollection.Count
'Headerにおける列指定は列見出しの文字列でしかできないらしい destSh.Range(myTableName & "[[#Headers],[FieldName1]]") みたいな
.SeriesCollection(i).Name = destSh.Range(myTableName & "[#Headers]").Cells(i + 1)
Next i
End With
errHandle:
If rs.State = 1 Then rs.Close
Set rs = Nothing
If con.State = 1 Then con.Close
Set con = Nothing
End Sub