今回は『ベースとなるExcelファイル(列項目可変)を加工して必要なフィールドだけに整形して新しいExcelファイルを作成する方法』をご紹介します。
なんでもAccessでする必要があるか?と言われそうですが…(;^_^A
背景としてはQlikSenseでエクスポートしたデータファイルを顧客毎に加工して出力するのが面倒だという課題がありました。
顧客によって出力する項目も様々だし、QlikSenseからダウンロードしたデータの項目も可変…。
何もかも可変なので変換ツールを作ってしまおう!というものです。
手順の概要
大まかな処理の手順は以下の通りです。
- 元となるExcelファイルをインポート
- インポートしたテーブルから各フィールド名をワークテーブルに格納
- 利用者にはワークテーブルのフィールド名リストに出力ON/OFFを指定させる
- 出力フラグがONのフィールドだけを取得するSQLを作成しレコードセットをEXCEL出力
各手順の解説
元となるExcelファイルをインポート
インポートする元ファイルの項目数や項目名は可変であるという前提なのでインポート先のテーブルは毎回新しく作成する作りにします。
よって、指定のインポート先テーブルが存在した場合は削除するというロジックを入れておきます。
'既にテーブルが存在したら削除 If IsTable("TMPデータ") = True Then DoCmd.DeleteObject acTable, "TMPデータ" End If 'インポート DoCmd.TransferSpreadsheet , acSpreadsheetTypeExcel12, "TMPデータ", strTmpFileName, True, "Sheet1!"
▼IsTabel()関数は以下の記事を参考に▼
実はこの時ちょっとした問題が発生しました。
Accessのインポートって、インポート元データの最初の8行位でその項目の型を判断するのです。
このため本来文字列でたまたま先頭8行が数字だった項目などがインポートエラーになってしまいました。
全て文字列で取り込めば問題ないのですが可変なので事前にテーブルを作成しておくわけにもいかず…。
結局元のExcelファイルの2行目(ヘッダー行の下)に全列ダミー文字列を挿入するという姑息なことをしました…。
インポートしたテーブルから各フィールド名をワークテーブルに格納
①フィールド名を格納するテーブルを事前に用意しておきます。
【TMPフィールド名リスト】テーブル
フィールド名 | データ型 | 備考 |
ID | 数値型 | 出力順がずれないようフィールドの順番をセット |
フィールド名 | テキスト型 | インポートテーブルのフィールド名を格納 |
サンプルデータ | テキスト型 | 1行目の生データをサンプルとして格納 |
出力フラグ | Yes/No型 | Outputファイルに必要な項目を指定するフラグ |
②インポートしたテーブルの全フィールドのフィールド名を取得して【TMPフィールド名リスト】に格納。
ついでに、サンプルデータとして1行目のデータをセットする。
'------------------------------------------------------------ ' 処理内容:フィールド名取得 ' 引数:元テーブル名,格納先テーブル名 ' 戻り値:True/False ' 更新: '------------------------------------------------------------ Function フィールド名取得(TableName As String, ListTable As String) As Boolean Dim strsql As String Dim cn As ADODB.Connection Dim DataRs As New ADODB.Recordset Dim ListRs As New ADODB.Recordset Dim i As Integer Dim topFieldName As String On Error GoTo Err_Exit '戻り値初期化 フィールド名取得 = False Set cn = Application.CurrentProject.Connection DoCmd.SetWarnings False 'リストテーブルレコードセットクリア strsql = "DELETE * FROM " & ListTable DoCmd.RunSQL strsql '指定のテーブルのレコードセットを作成 DataRs.Open TableName, cn, adOpenDynamic, adLockOptimistic '格納先のリストテーブルレコードセットを作成 ListRs.Open ListTable, cn, adOpenDynamic, adLockOptimistic With DataRs '事前にデータが存在していることを確認済み '1フィールド目にDummy文字列が入っているレコードは削除 .Find .Fields(0).Name & "='" & csDummy & "'" .Delete .MoveFirst For i = 0 To (DataRs.Fields.Count - 1) ListRs.AddNew 'フィールド名をセット ListRs.Fields("ID") = i + 1 ListRs.Fields("フィールド名") = .Fields(i).Name 'サンプルデータをセット If IsNull(.Fields(i).VALUE) Then ListRs.Fields("サンプルデータ") = "" Else ListRs.Fields("サンプルデータ") = .Fields(i).VALUE End If ListRs.Update Next End With '正常終了 フィールド名取得 = True End_Proc: DataRs.Close: Set DataRs = Nothing ListRs.Close: Set ListRs = Nothing cn.Close: Set cn = Nothing DoCmd.SetWarnings True Exit Function Err_Exit: MsgBox "フィールド名取得()" & Err.Number & ":" & Err.Description, vbOKOnly + vbCritical GoTo End_Proc End Function
利用者にはワークテーブルのフィールド名リストに出力ON/OFFを指定させる
格納した項目を表示する画面を用意し、利用者に出力のON/OFFを指定させExcel出力します。
出力フラグがONのフィールドだけを取得するSQLを作成しレコードセットをEXCEL出力
'------------------------------------------------------------ ' 処理内容:指定のフィールドをExcel出力 ' 引数:なし ' 戻り値: ' 更新: '------------------------------------------------------------ Private Sub OutputExcel() Dim strsql As String Dim strFileName As String Dim xlapp As Object Dim myCn As New ADODB.Connection Dim myRs As New ADODB.Recordset Dim colcnt As Integer On Error GoTo Err_Exit 'ファイル名作成 strFileName = csOutputFileName & "_" & Format(Date, "yyyymmdd") & ".xlsx" '出力対象のデータを絞り込むsqlを作成 If MkOutputSql(strsql) = False Then Exit Sub End If 'EXCELアプリケーションを起動 Set xlapp = CreateObject("Excel.Application") Set myCn = CurrentProject.Connection 'レコードセットオープン myRs.Open strsql, myCn, adOpenForwardOnly, adLockReadOnly With xlapp 'セットする過程が見えないよう一旦不可視 .Visible = False '新しいBookを追加 .Workbooks.Add .Worksheets("Sheet1").Select 'レコードセットのフィールド名(見出し)出力処理 For colcnt = 0 To myRs.Fields.Count - 1 .Worksheets("Sheet1").Cells(1, colcnt + 1).VALUE = myRs.Fields(colcnt).Name Next '結果値出力処理(1行目はヘッダーなので、2行目1列目からセット .Cells(2, 1).CopyFromRecordset myRs '列幅自動調整 .Cells.EntireColumn.AutoFit '全列文字列で保存されている数値を数値に変換…(A) colcnt = 1 While Nz(.Cells(1, colcnt)) <> "" .Columns(colcnt).TextToColumns Comma:=True colcnt = colcnt + 1 Wend .Visible = True MsgBox "出力しました。", vbOKOnly + vbInformation End With End_Proc: Set myRs = Nothing: Close Set myCn = Nothing: Close xlapp.Quit Exit Sub Err_Exit: MsgBox Err.Number & ":" & Err.Description, vbOKOnly + vbCritical, "OutputExcel()" GoTo End_Proc End Sub
'------------------------------------------------------------ ' 処理内容:出力用のレコードセット用SQL文を作る ' 引数:なし ' 戻り値:True:OK False:NG ' 更新: '------------------------------------------------------------ Private Function MkOutputSql(ByRef strsql) As Boolean Dim myRs As Recordset '戻り値初期化 MkOutputSql = False strsql = "" Set myRs = CurrentDb.OpenRecordset("SELECT * FROM TMPフィールド名リスト where 出力フラグ=TRUE") With myRs If Not .EOF Then .MoveFirst While Not .EOF If strsql <> "" Then strsql = strsql & "," End If 'フィールド名に不正な文字を含む場合があるので大括弧囲み strsql = strsql & "[" & .Fields("フィールド名") & "]" .MoveNext Wend End If End With Set myRs = Nothing: Close 'フィールドが1つも指定されていなかったらエラー If strsql <> "" Then strsql = "SELECT " & strsql & " FROM TMP進捗データ" MkOutputSql = True Else MsgBox "出力項目が選択されていません。", vbOKOnly + vbInformation End If Exit Function Err_Exit: MsgBox "MkOutputSql()" & Err.Number & ":" & Err.Description, vbOKOnly + vbCritical End Function
このプログラムでは出力したExcelを保存するロジックを含みませんので、出力した利用者が目視確認を自分で保存する形になっています。
「全列文字列で保存されている数値を数値に変換…(A)」の処理は以下の記事を参考に…
まとめ
使う人がいるかどうか不明ですが、可変フィールドのデータを取得して後処理をするシチュエーションはそこそこあるかと思います。
その際の参考になれば幸いです。