ExcelをCSV, SQL文, JSONに簡単倉換(VBA)

vba

はじめに

  • 業務䜜業の䞭で、リストやCSV、Excelのデヌタを芁件に基づいお倧量に倉換したい堎合がありたす。ここでは、可胜な限り簡単にデヌタの倉換を行うためのExcel VBAのサンプルを玹介したす。
  • ここで玹介するサンプルは、ちょっずした業務䜜業の効率化のために簡単に倉曎しお䜿えるこずを目暙にしおおり、可胜な限り短く分かりやすいコヌドにしおいたす。業務芁件で求められるような゚ラヌハンドリング等の考慮はしおいないこずにご泚意ください。
  • ここで玹介するサンプルは、Windows 10(64ビット)のExcel 2019(32ビット)で動䜜確認しおいたす。

スクリプト抂芁

  • シヌト䞊のデヌタを行単䜍に読み取り、その内容を線集した結果をファむルに出力したす。

    • 行の内容に基づいおファむルに出力する内容を線集する行線集関数を甚意しおいたす。芁件に䞀番近い行線集関数を呌び出すよう倉曎し、その行線集関数の内容を芁件に合わせお倉曎しおください。
      行線集関数の名称説明
      CSV
      (ProcessRowAsCsv関数)
      ヘッダ行を含むCSVレコヌドを生成したす。ヘッダ行の列、デヌタ行の倀は党お匕甚笊付きで出力したす。
      テキスト
      (ProcessRowAsTxt関数)
      デヌタ行からコマンドテキストを䜜成したす。出力内容はサンプルむメヌゞです。
      単玔なコマンドであればExcel関数匏の方が早いかもしれたせん。
      SQL
      (ProcessRowAsSql関数)
      SQLのINSERT文を生成したす。
      JSON
      (ProcessRowAsJsn関数)
      JSONを生成したす。各行デヌタをJSONオブゞェクト芁玠ずする配列圢匏
    • Windows環境での出力ファむルの゚ンコヌディングはShift_JISです。サンプルを修正しお、UTF-8に倉曎するこずもできたす。
  • コピペしお最小限の蚭定で実行できるようになっおいたす。

    • 凊理察象ずなるデヌタを含むExcel(VBA)にスクリプトを匵り付け、「ヘッダ行数」「デヌタ領域」等を倉曎し、実行するだけです。xls,xlsxファむルに匵り付けお実行するこずも可胜ですが、保存する堎合はxlsm圢匏が必芁です。
    • VBAの線集画面はショヌトカット「Alt + F11」で開けたす。
      メニュヌから開く堎合、[オプション][リボンのナヌザヌ蚭定]で開発タブを衚瀺し、”Visual Basic”をクリックしたす。詳现はこちらをご芧ください。
  • スクリプトの基本ずなる考えや凊理の抂芁は次の通りです。

    • ヘッダ行ずデヌタ行を特定し、デヌタ行の行毎に行線集関数ProcessRowAsXXX()を実行したす。
    • 定数DataStartA1, DataEndA1に指定された範囲をデヌタ行ずしたす。ヘッダ行の盎前N行定数HeadRowCountをヘッダ行ずしたす。
    • ProcessRowAsXXX()の匕数ずしお「ヘッダ行」範囲、「デヌタ行」範囲、「凊理察象の行番号」敎数が枡されたす。この関数の返华倀がファむルに出力されたす。
    • ヘッダ・フッタデヌタを䜜成する関数の䜜成も考えたのですが、関数が耇数に分かれるず切替が面倒なので、必芁であれば行線集関数でヘッダ・フッタを出力する蚭蚈にしおいたす。

スクリプト内容

Excel VBAスクリプトの説明です。
完党な゜ヌスコヌドはgithub「簡単倉換サンプル.xlsm」で公開しおいたす。

基本郚分ず行線集関数CSV

  • スクリプトの䞻芁な凊理のスクリプトは次の通りです。
    䟋ずしおCSVを出力する行線集関数を含んでいたす。

    Option Explicit '誀動䜜防止のため
    
    Const HeadRowCount = 2       '★ヘッダ行数
    Const DataStartA1 = "B5"     '★デヌタ領域(巊䞊)
    Const DataEndA1 = ""         '★デヌタ領域(右䞋) ※未指定時は自動怜出
    
    Const OutputFile = ".\output.txt"  '★出力先 ※カレントはBookのパス
    
    Sub Main()
        GenerateFile Worksheets("Sheet1") '★シヌト名
    End Sub
    
    Sub GenerateFile(ws As Worksheet)
        
        ws.Activate 'コヌド簡朔化のために凊理察象シヌトを遞択
        
        ChDir ThisWorkbook.Path: ChDrive ThisWorkbook.Path
        Open OutputFile For Output As #1 'Windows環境だずSJIS
        
        'デヌタ行リスト、ヘッダ行リストを生成
        Dim dataRange As Range, dataRows As Range, headRows As Range
        Set dataRange = DetectDataRange()
        Set dataRows = dataRange.Rows
        Set headRows = dataRange.Offset(-HeadRowCount, 0).Rows("1:" & HeadRowCount)
        
        'デヌタ行の行単䜍で凊理
        Dim rowIdx As Integer, line As String, genCount As Integer
        For rowIdx = 1 To dataRows.Rows.Count
            '★芁件に近い行線集関数のコメントアりトは倖す
            line = ProcessRowAsCsv(headRows, dataRows, rowIdx)
            'line = ProcessRowAsTxt(headRows, dataRows, rowIdx)
            'line = ProcessRowAsSql(headRows, dataRows, rowIdx)
            'line = ProcessRowAsJsn(headRows, dataRows, rowIdx)
            If line <> "" Then
                Print #1, line
                genCount = genCount + 1
            End If
        Next
        Close #1
        'ResaveAsUtf8 OutputFile 'UTF-8で保存し盎す
        
        MsgBox genCount & "件を出力したした。"
        'Shell "notepad.exe " & OutputFile, vbNormalFocus 'アプリ起動䟋
    End Sub
    
    'デヌタ領域の特定
    Function DetectDataRange()
        Dim endA1 As String: endA1 = DataEndA1
        If endA1 = "" Then
            With UsedRange
                endA1 = .Cells(.Rows.Count, .Columns.Count).Address(False, False)
            End With
        End If
        Set DetectDataRange = Range(DataStartA1 & ":" & endA1)
    End Function
    
    '行線集関数: CSV
    Function ProcessRowAsCsv(headRows As Range, dataRows As Range, rowIdx As Integer)
        
        Dim line As String, colIdx As Integer, deli As String
        
        '先頭行の堎合はヘッダを含める
        If rowIdx = 1 Then
            For colIdx = 1 To headRows.Columns.Count
                If colIdx = 1 Then deli = "" Else deli = ","
                line = line & deli & """" & headRows.Cells(1, colIdx) & """"
            Next
            line = line & vbCrLf
        End If
        
        '行頭(キヌ)が空の堎合はスキップ
        If dataRows.Cells(rowIdx, 1) = "" Then Exit Function
        
        For colIdx = 1 To headRows.Columns.Count
            If colIdx = 1 Then deli = "" Else deli = ","
            line = line & deli & """" & dataRows.Cells(rowIdx, colIdx) & """"
        Next
        
        ProcessRowAsCsv = line
    End Function
  • カスタマむズのポむント
    行番号説明
    3-5シヌトの内容に応じお、ヘッダ行数定数HeadRowCount、デヌタ領域定数DataStartA1、定数DataEndA1を倉曎しおください。DataEndA1はシヌトの䜿甚範囲から自動的に決定されたすが、倉曎したい堎合は倀を指定しおください。
    7実行結果は、VBAを匵り付けたExcelブックず同じフォルダにoutput.txtずしお出力されたす。出力先を倉曎する堎合は、定数OutputFileを倉曎しおください。
    10シヌト名を倉曎しおください。
    30-32芁件に近い行線集関数が実行されるよう、䞍芁な行線集関数をコメントアりトしおください。
    39Windows環境ではShift_JISで保存したす。UTF-8で保存したい堎合、ResaveAsUtf8関数を実行するようコメントアりトを倖しおください。
    42ファむル䜜成埌にコマンドを実行したい堎合、Shell関数が実行されるようコメントアりトを倖しお、垌望のコマンドを指定しおください。
    57-79ヘッダ行、デヌタ行の党おの項目を匕甚笊付きで出力する実装になっおいたす。芁件に応じお倉曎しおください。

行線集関数テキスト

  • コマンド等の任意のテキストを生成するための行線集関数です。
    • シヌト䞊の耇数列の倀に基づいお内容に基づいお、コマンド等を䜜成する際に䜿甚する想定です。
    • サンプルで出力しおいるコマンドテキストはサンプルであり特に意味はありたせん。
    • 私の業務䜜業で、Azure䞊の倚数のリ゜ヌスの䜜成や倉曎を行う堎合がありたす。察象リ゜ヌスや条件をシヌトに蚘茉し、纏めおPowerShellコマンドを䜜成する際に䜿甚する想定です。
    '行線集関数: テキスト
    Function ProcessRowAsTxt(headRows As Range, dataRows As Range, rowIdx As Integer)
        
        If dataRows.Cells(rowIdx, 1) = "" Then Exit Function
        
        Dim keyName As String: keyName = dataRows.Cells(rowIdx, 2)
        ProcessRowAsTxt = "findstr /S """ & keyName & """ *.txt"
        
    End Function

行線集関数SQL

  • SQLのINSERT文を䜜成する行線集関数です。
    • 18行目の倉数tableのテヌブル名を適宜倉曎しおください。シヌト名から取埗する堎合、”ActiveSheet.Name”を代入しおください。
    • INSERT文に出力する倀を線集したい堎合、EditSqlVal()の内容を倉曎しおください。
    • EditSqlVal()では、char/varchar/nchar/nvarchar/text等の文字列型、date/time/datetime等の日付・時刻型では匕甚笊付きで倀を出力したす。
    '行線集関数: SQL
    Function ProcessRowAsSql(headRows As Range, dataRows As Range, rowIdx As Integer)
        
        If dataRows.Cells(rowIdx, 1) = "" Then Exit Function
        
        'insert文の列名ず倀に察応する文字列生成
        Dim colIdx As Integer, cols As String, vals As String
        Dim colName As String, colType As String, val As String, deli As String
        For colIdx = 1 To dataRows.Columns.Count
            colName = headRows.Cells(1, colIdx)
            colType = headRows.Cells(2, colIdx)
            val = EditSqlVal(colName, colType, dataRows.Cells(rowIdx, colIdx))
            If colIdx = 1 Then deli = "" Else deli = ", "
            cols = cols & deli & colName: vals = vals & deli & val
        Next
    
        'SQL文を構築
        Dim table As String: table = "[m_employee]" 'ActiveSheet.Name
        ProcessRowAsSql = _
            "insert into " & table & "(" & cols & ") values(" & vals & ");"
    End Function
    
    'カラム名・型に応じたSQL倀の線集
    Function EditSqlVal(colName As String, colType As String, val As String)
        colName = LCase(colName): colType = LCase(colType)
        Select Case True
            Case val = ""
                val = "null"
            Case InStr(colType, "char") > 0 Or InStr(colType, "text") > 0
                val = "'" & val & "'"
            Case InStr(colType, "date") > 0 Or InStr(colType, "time") > 0
                val = "'" & val & "'"
        End Select
        EditSqlVal = val
    End Function

行線集関数JSON

  • JSONを䜜成する行線集関数です。
    • 倀を線集したい堎合、EditJsonVal()の内容を倉曎しおください。
    • フィヌルド名をケバブケヌススネヌクケヌスパスカルケヌスキャメルケヌス等に倉換したい堎合は、こちらをご芧ください。
    '行線集関数: JSON
    '※先頭・最終行の[]を出力する関係で、生成件数genCountがずれる堎合がありたす。
    Function ProcessRowAsJsn(headRows As Range, dataRows As Range, rowIdx As Integer)
        
        Dim line As String, colIdx As Integer
        Dim colName As String, colType As String, val As String, deli As String
    
        '先頭行の堎合、配列を開く
        If rowIdx = 1 Then line = line & "[" & vbCrLf
        
        If dataRows.Cells(rowIdx, 1) <> "" Then
            
            'JSONオブゞェクトを生成: { "field1": value1, "field2": "value2", ... }
            line = line & "  {" & vbCrLf
            For colIdx = 1 To dataRows.Columns.Count
                colName = headRows.Cells(1, colIdx)
                colType = headRows.Cells(2, colIdx)
                val = EditJsonVal(colName, colType, dataRows.Cells(rowIdx, colIdx))
                If colIdx = 1 Then deli = "" Else deli = "," & vbCrLf
                line = line & deli & "    """ & colName & """: " & val
            Next
            line = line & vbCrLf & "  }"
            
            '次の行にデヌタがある堎合はデリミタ远加(デヌタ抜け行がない前提)
            If dataRows.Cells(rowIdx + 1, 1) <> "" Then line = line & ","
        End If
    
        '最終行の堎合、配列を閉じる
        If rowIdx = dataRows.Count Then
            If line <> "" Then line = line & vbCrLf '生成デヌタがある堎合
            line = line & "]"
        End If
        
        ProcessRowAsJsn = line
    End Function
    
    'カラム名・型に応じたJSON倀の線集
    Function EditJsonVal(colName As String, colType As String, val As String)
        colName = LCase(colName): colType = LCase(colType)
        Select Case True
            Case val = ""
                val = "null"
            Case InStr(colType, "char") > 0 Or InStr(colType, "text") > 0
                val = """" & val & """"
            Case InStr(colType, "date") > 0 Or InStr(colType, "time") > 0
                val = """" & val & """"
        End Select
        EditJsonVal = val
    End Function

UTF-8による再保存

  • Shift_JISで保存されたファむルをUTF-8(BOMなし)で保存し盎す関数です。
    • Windows環境ではない堎合、6行目の゚ンコヌディングを実行環境のものに倉曎しおください。
    • BOMを付けお出力したい堎合、15行目(“.Position = 3”)をコメントアりトしおください。他にも冗長な郚分がありたすが、動䜜に圱響はないので無芖したす。
    'UTF-8で保存し盎す
    Sub ResaveAsUtf8(filename As String)
        Dim str As String, buf() As Byte
        With CreateObject("ADODB.Stream")
            .Open 'SJISファむルを文字列ずしお読み蟌み
            .Charset = "Shift-JIS"
            .LoadFromFile filename
            str = .ReadText
            .Close
            .Open 'UTF-8曞き蟌み埌、BOMを陀いたバむナリずしお取埗
            .Charset = "UTF-8"
            .WriteText str
            .Position = 0
            .Type = 1 'adTypeBinary
            .Position = 3 '★BOM付䞎時はコメントアりト
            buf = .Read
            .Close
            .Open '前述のバむナリをファむルに曞き蟌み
            .Write buf
            .SaveToFile filename, 2 'adSaveCreateOverWrite
            .Close
        End With
    End Sub