vba:EXCELSQL(エラー出力を実装)

sheet1

Sub SetData()

    ' 画面を初期化
    Rows("1:19").Select
    
    Selection.Delete Shift:=xlUp
    
    ' 列の選択状態を解除
    Sheet1.Range("A1").Select
    
    ' 実行ボタンを生成
    With ActiveSheet.Buttons.Add(Range("B5").Left, _
                                 Range("B5").Top, _
                                 Range("B5:D5").Width, _
                                 Range("B5:D6").Height)
        .Name = "btn"
        .OnAction = "Sheet1.MakeMaxValueTable"
        .Characters.Text = "exe"
    End With

    ' テストデータを出力
    Dim arr_id_data As Variant: arr_id_data = Array("e_01", "e_02", "e_03", "e_04", "e_05", "e_06", "e_07", "e_08", "e_09", "e_10")
    Dim arr_val_data As Variant: arr_val_data = Array(1000, 1300, 800, 1100, 700, 1000, 2100, 1100, 500, 800)

    For r = 0 To UBound(arr_id_data)
        Cells(r + 9, 2).Value = arr_id_data(r)
        Cells(r + 9, 3).Value = arr_val_data(r)
    Next r
    
    ' ヘッダーを設定
    Sheet1.Range("B8").Value = "ID"
    Sheet1.Range("C8:C8").Value = "Value"
    Sheet1.Range("E8").Value = "閾値"
    
    ' 閾値を設定
    Sheet1.Range("E9").Value = 800
    
    
    ' *** 全体的な見た目の設定 ****
    
    Call SetFormat(Sheet1, Sheet1.Range("B8:C8"), "id")
    Call SetFormat(Sheet1, Sheet1.Range("B9:B18"), "id")
    Call SetFormat(Sheet1, Sheet1.Range("C9:C18"), "val")
     
    Call SetFormat(Sheet1, Sheet1.Range("E8:E8"), "id")
    Call SetFormat(Sheet1, Sheet1.Range("E9:E9"), "val")
    
    
    ' フォーカスを所定の位置へ
    Sheet1.Range("A1").Select
    
End Sub


' 最大値を抽出し強調表示する
Sub MakeMaxValueTable()

On Error GoTo ErrorHandler

    ' =====================
    ' 開始処理
    ' =====================
    Dim comUtil As CommonUtil
    Set comUtil = New CommonUtil
    comUtil.ExeInit
    
    'err.Raise 513
    
    '画面初期化
    ' 条件付き書式をクリア
    Dim ws As Worksheet
    For Each ws In Worksheets
        ws.Cells.FormatConditions.Delete
    Next ws
    
    ' グラフをクリア
    With Sheet1
    
        ' 実行結果出力範囲をクリア
        .Rows("2:3").Clear
    
        Dim r As Long
        For r = .ChartObjects.Count To 1 Step -1
            .ChartObjects(r).Delete
        Next r
    End With
    
    
    ' =====================
    ' 入力チェック
    ' =====================
    'リストの値を取得して、配列に格納
    Dim arr_ori As Variant: arr_ori = Sheet1.Range("B9:C18").Value
    
    ' 閾値を取得
    Dim line_val As Long: line_val = Sheet1.Range("E9").Value
    
    ' 最大値を取得
    Dim max_val As Long: max_val = Application.WorksheetFunction.Max(arr_ori)
    
    ' 最大値 <= 閾値は処理終了
    If (max_val <= line_val) Then
    
        Dim rc As Integer
            
        rc = MsgBox("閾値は最大値より小さい値を設定してください。" & vbCrLf & "最大値:" & max_val, vbOKOnly + vbExclamation)
        
        If rc = vbOK Then
            Exit Sub
        End If
    
    End If
    
    
    ' =====================
    ' コネクションOPEN
    ' =====================
    ' 処理対象ファイルのフルパス
    Dim fileFullName As String: fileFullName = ThisWorkbook.FullName

    Dim dbUtil As DatabaseUtil
    Set dbUtil = New DatabaseUtil
    If Not dbUtil.IsConnectedDB("excelsql", fileFullName) Then
        ' エラーログをtxtに出力する処理を追加する
        Debug.Print "システムエラー:DB接続に失敗しました。"
        Exit Sub
    End If
    
    
    ' =====================
    ' SQLを作成して実行
    ' =====================
    Dim sqlStr As String
'    ' シート名のパラメータ化が必要
'    sqlStr = ""
'    sqlStr = sqlStr & " select"
'    sqlStr = sqlStr & "     *"
'    sqlStr = sqlStr & " from"
'    sqlStr = sqlStr & "     [Sheet1$]"
'    sqlStr = sqlStr & " where"
'    sqlStr = sqlStr & "         Value > " & line_val
    sqlStr = "select * from [Sheet1$] where Value > " & line_val
    Set rs = dbUtil.ExeSelect(sqlStr)
    
    
    ' =====================
    ' SQL実行結果出力
    ' =====================
    ' 抽出結果を出力・・・縦
'    Sheet1.Range("G2").CopyFromRecordset rs

    ' 抽出結果を出力・・・横
    Dim fieldCnt As Long: fieldCnt = rs.Fields.Count
    Dim recordCnt As Long: recordCnt = rs.RecordCount
    
    ' これで、横に格納してるみたいです
    'ReDim recordArray(fieldCnt, recordCnt) As Variant: recordArray = rs.GetRows
    
    'シートに出力
    ' 横に出力する場合は、起点のセルだけ指定だとうまくいかないみたいなので、出力するRangeを指定する。
    'Sheet1.Range(Cells(2, 2), Cells(fieldCnt, recordCnt + 1)) = recordArray
    ' 抽出結果で特に何もしないなら、直接設定する方が良い
    Sheet1.Range(Cells(2, 2), Cells(fieldCnt, recordCnt + 1)) = rs.GetRows
    
      
    ' =====================
    ' 最大値の設定
    ' =====================
    Dim fc As FormatCondition
    
    ' range・・・入れ子だと見辛いので、変数に格納
    Dim resultDataRange As Range: Set resultDataRange = Sheet1.Range(Cells(3, 2), Cells(fieldCnt, recordCnt + 1))

    ' 条件付き書式を生成・・・入れ子だと見辛いので、変数に格納(.Addressの絶対参照を忘れないこと)
    ' MAX関数は、最大値が複数ある場合は、存在するだけ最大値と判定します
    Dim strFormula As String: strFormula = "=B3=MAX(" & resultDataRange.Address & ")"
    
    Set fc = resultDataRange.FormatConditions.Add( _
        Type:=xlExpression, _
        Formula1:=strFormula)
        
    ' 最大値の見た目を設定
    fc.Interior.Color = RGB(255, 0, 0)
    fc.Font.Color = RGB(255, 255, 255)
    fc.Font.Bold = True
        
        
    ' =====================
    ' グラフを生成
    ' =====================
    Call MakeChart(Sheet1, fieldCnt, recordCnt, line_val)


    ' =====================
    ' 全体的な見た目の設定
    ' =====================
    Call SetFormat(Sheet1, Sheet1.Range(Cells(2, 2), Cells(2, recordCnt + 1)), "id")
    Call SetFormat(Sheet1, Sheet1.Range(Cells(3, 2), Cells(3, recordCnt + 1)), "val")


    ' フォーカスを所定の位置へ
    Sheet1.Range("A1").Select
    
    'Finally:へ飛ぶ
    GoTo Finally

ErrorHandler:

    'エラーメッセージを表示する
'    MsgBox "[No:" & Err.Number & "]" & Err.Description, vbCritical & vbOKOnly, "エラー"
    
    Dim logUtil As logUtil
    Set logUtil = New logUtil
    
    Call logUtil.OutputErrorLog(err)
    
'    If err.Number <> 0 Then
'        ' クリアしないとループするみたいなので、クリアする
'        err.Clear
'    End If
    
    'Finally:へ飛ぶ
    Resume Finally

Finally:

    If Not err Is Nothing Then
        err.Clear
    End If

    comUtil.ExeFinal
    
End Sub


' 書式を設定
' 項目:id 、値:val
Sub SetFormat(ByVal sh As Worksheet, ByVal rng As Range, ByVal item As String)

    sh.Select

    rng.Select
    
    With Selection
        ' 外枠の設定・・・ちなみにこれは、中太
        '.BorderAround Weight:=xlMedium
        .Borders.LineStyle = True
        
        Select Case item
            
            Case "id"
                .HorizontalAlignment = xlCenter
                .Interior.Color = rgbTurquoise
                
            Case "val"
                .NumberFormatLocal = "#,###"
                
        End Select
        
    End With

End Sub

' グラフを生成
Sub MakeChart(ByVal sh As Worksheet, ByVal fieldCnt As Long, ByVal recordCnt As Long, ByVal line_val As Long)

    sh.Select

    ' 配列に入れなおさないとうまくいかないので、入れなおす。(この辺は、要検証)
    Dim arr_ans() As Variant: arr_ans = Sheet1.Range(Cells(2, 2), Cells(fieldCnt, recordCnt + 1))
    
    ' グラフ生成用に、ヘッダーと値の配列を作成
    Dim arr_header As Variant: arr_header = WorksheetFunction.Transpose(WorksheetFunction.Index(arr_ans, 1, 0))
    Dim arr_val As Variant: arr_val = WorksheetFunction.Transpose(WorksheetFunction.Index(arr_ans, 2, 0))

    Dim co As ChartObject
   
    ' グラフの外枠?を設置
    Set co = ActiveSheet.ChartObjects.Add(Left:=Range("G8").Left, Top:=Range("G8").Top, Width:=324, Height:=206)
  
    ' グラフの中身を生成
    With co
        With .Chart.SeriesCollection.NewSeries
            .Name = "Value"
            .Values = arr_val
            .XValues = arr_header
        End With
    End With
   
    With co.Chart
    
        'グラフ形式を設定
        co.Chart.ChartType = xlColumnClustered
    
        '凡例非表示
        co.Chart.HasLegend = True
        
        ' グラフの左線を強調(線の太さと色・・・結構見やすいかも)
        .Axes(xlValue).format.Line.Weight = 2
        .Axes(xlValue).format.Line.ForeColor.RGB = RGB(0, 0, 0)
        
        ' グラフの下線を強調
        .Axes(xlCategory).format.Line.Weight = 2
        .Axes(xlCategory).format.Line.ForeColor.RGB = RGB(0, 0, 0)
        
         
        '基準線を新規系列として追加
        With .SeriesCollection.NewSeries
            .Name = "=""閾値""" '特に必要はない
            .Values = "={" & Join(Array(line_val, line_val), ",") & "}"
            .ChartType = xlLine '折れ線
            .AxisGroup = 2 '第2軸
            .format.Line.ForeColor.RGB = vbRed 'RGB(255, 0, 0)
        End With
    
        '第2縦軸のScaleを第1縦軸に合わせる
        .Axes(xlValue, 2).MinimumScale = .Axes(xlValue, 1).MinimumScale
        .Axes(xlValue, 2).MaximumScale = .Axes(xlValue, 1).MaximumScale
        .Axes(xlValue, 2).Delete '第2軸を消す
    
        'グラフ内左右余白を消す:基準線を第2横軸に設定
        .HasAxis(xlCategory, 2) = True '第2横軸表示
        .Axes(xlCategory, 2).AxisBetweenCategories = False '軸位置を目盛
        .Axes(xlCategory, 2).TickLabelPosition = xlNone '目盛ラベルなし
    
    End With
    
End Sub

CommonUtil

Option Explicit

Public Sub ExeInit()
    
    With ActiveWorkbook.Application
        .Calculation = xlCalculationManual
        .ScreenUpdating = False
        .EnableEvents = False
    End With

End Sub

Public Sub ExeFinal()

    With ActiveWorkbook.Application
        .Calculation = xlCalculationAutomatic
        .ScreenUpdating = True
        .EnableEvents = True
    End With

End Sub
        

Public Function ConvertDateToString(ByVal dt As Date, ByVal formatNo As Long) As String
   
    Select Case formatNo
    
        Case 1
            ConvertDateToString = format(dt, "yyyymmdd")
            
    End Select

End Function

DatabaseUtil

Option Explicit

Private cn As ADODB.Connection
Private rs As ADODB.Recordset

'-------------------------------------------------------------------------------------
' コンストラクタ
'-------------------------------------------------------------------------------------
Private Sub class_initialize()

    If Not rs Is Nothing Then rs.Close
    
End Sub

'-------------------------------------------------------------------------------------
' デストラクタ
'-------------------------------------------------------------------------------------
Private Sub class_terminate()
    
    On Error Resume Next
    If Not rs Is Nothing Then rs.Close
    Set rs = Nothing
    
    cn.Close
    Set cn = Nothing
    
End Sub

'-------------------------------------------------------------------------------------
' データベース接続
'【引数】DBType    接続するDBを指定
'                  excelsql … Excelをデータベースとして接続
'【戻値】接続成功:True / 接続失敗:False(Boolean)
'-------------------------------------------------------------------------------------
Public Function IsConnectedDB(ByVal useDb As String, ByVal fileFullName As String) As Boolean

    Dim ConnectingString As String
    
    Select Case useDb
        
        Case "excelsql"
            ConnectingString = "PROVIDER = MSDASQL; Driver={Microsoft Excel Driver (*.xls, *.xlsx, *.xlsm, *.xlsb)}; " & "DBQ=" & fileFullName & "; ReadOnly=False;"
        
        Case Else
            GoTo ErrHandler
    
    End Select
    
    On Error GoTo ErrHandler
    
    Set cn = New ADODB.Connection
    cn.ConnectionString = ConnectingString
    cn.ConnectionTimeout = 2
    cn.Open
    IsConnectedDB = True
    
    Exit Function
    
ErrHandler:
    IsConnectedDB = False
    
    
End Function

'-------------------------------------------------------------------------------------
' SQL文を実行する(Select 文)
'【引数】strSQL    SQL文
'【戻値】Recordset オブジェクト
'-------------------------------------------------------------------------------------
Public Function ExeSelect(ByVal strSQL As String) As ADODB.Recordset

    Set rs = New ADODB.Recordset
    
    'SQL文実行(読み取り専用、共有ロック)
    rs.Open strSQL, cn, adOpenKeyset, adLockOptimistic, adCmdText
    
    Set ExeSelect = rs

End Function

'-------------------------------------------------------------------------------------
' SQL文を実行する(Insert into文、Delete 文など)
'【引数】strSQL    SQL文(String)
'【戻値】変更されたレコード数(Long)
'-------------------------------------------------------------------------------------
Public Function ExeUpdate(strSQL As String) As Long

    Dim updateCnt As Long
    
    cn.Execute strSQL, updateCnt
    
    ExeUpdate = updateCnt

End Function

'-------------------------------------------------------------------------------------
'トランザクション開始
'-------------------------------------------------------------------------------------
Public Sub BeginTr()
    cn.BeginTrans
End Sub

'-------------------------------------------------------------------------------------
' トランザクションコミット
'-------------------------------------------------------------------------------------
Public Sub CommitTr()
    cn.CommitTrans
End Sub

'-------------------------------------------------------------------------------------
' トランザクションロールバック
'-------------------------------------------------------------------------------------
Public Sub RollbackTr()
    cn.RollbackTrans
End Sub

LogUtil

Option Explicit

'-----------------------------------------------------
' エラーログをファイルに出力する
' 出力先は、実行ファイルと同様のフォルダ、出力先のフォルダが無い場合は、生成する。
' 出力ファイ名:yyyymmdd_error.log ※日付は、出力当日
'
' 引数:エラーオブジェクト
'
'-----------------------------------------------------
Public Sub OutputErrorLog(ByVal err As ErrObject)
    
    Dim comUtil As CommonUtil
    Set comUtil = New CommonUtil
    
    ' ファイル名から拡張子を削除→フォルダ名
    Dim logFolderName As String: logFolderName = Left(ThisWorkbook.Name, Len(ThisWorkbook.Name) - 5)
    ' 実行当日からファイル用の日付を抽出し、ファイル名を生成
    Dim logFileName As String: logFileName = comUtil.ConvertDateToString(Now, 1) & "_error.log"
    Dim folderPath As String: folderPath = ThisWorkbook.Path & "\" & logFolderName
    
    Dim fso As Object
    Set fso = CreateObject("Scripting.FileSystemObject")
    
    ' フォルダが無い場合、生成
    If Dir(folderPath, vbDirectory) = "" Then fso.CreateFolder (folderPath)
    
    Dim strPath As String: strPath = ThisWorkbook.Path & "\" & logFolderName & "\" & logFileName
    Dim logMsg As String: logMsg = Now & vbTab & "NO:" & err.Number & vbTab & "DESC:" & err.Description
    
    ' エラー内容を出力
    With fso
        If Not .FileExists(strPath) Then
            .CreateTextFile (strPath)
        End If
        With .OpenTextFile(strPath, 8)
            .WriteLine logMsg
            .Close
        End With
    End With
    
    Set fso = Nothing

End Sub

vba:CommonUtil

  • 呼び出しは、こんな感じ
    ' =====================
    ' 開始処理
    ' =====================
    Dim comUtil As CommonUtil
    Set comUtil = New CommonUtil
    comUtil.ExeInit
  • クラス:CommonUtilで実装
Option Explicit

Public Sub ExeInit()
    
    With ActiveWorkbook.Application
        .Calculation = xlCalculationManual
        .ScreenUpdating = False
        .EnableEvents = False
    End With

End Sub

Public Sub ExeFinal()

    With ActiveWorkbook.Application
        .Calculation = xlCalculationAutomatic
        .ScreenUpdating = True
        .EnableEvents = True
    End With

End Sub
        

Public Function ConvertDateToString(ByVal dt As Date, ByVal formatNo As Long) As String
   
    Select Case formatNo
    
        Case 1
            ConvertDateToString = format(dt, "yyyymmdd")
            
    End Select

End Function

vba:エラー処理

  • クラス:LogUtilで実装
  • エラーまでをがっつり実装するメリットがあまりない気がする。
Option Explicit

'-----------------------------------------------------
' エラーログをファイルに出力する
' 出力先は、実行ファイルと同様のフォルダ、出力先のフォルダが無い場合は、生成する。
' 出力ファイ名:yyyymmdd_error.log ※日付は、出力当日
'
' 引数:エラーオブジェクト
'
'-----------------------------------------------------
Public Sub OutputErrorLog(ByVal err As ErrObject)
    
    Dim comUtil As CommonUtil
    Set comUtil = New CommonUtil
    
    ' ファイル名から拡張子を削除→フォルダ名
    Dim logFolderName As String: logFolderName = Left(ThisWorkbook.Name, Len(ThisWorkbook.Name) - 5)
    ' 実行当日からファイル用の日付を抽出し、ファイル名を生成
    Dim logFileName As String: logFileName = comUtil.ConvertDateToString(Now, 1) & "_error.log"
    Dim folderPath As String: folderPath = ThisWorkbook.Path & "\" & logFolderName
    
    Dim fso As Object
    Set fso = CreateObject("Scripting.FileSystemObject")
    
    ' フォルダが無い場合、生成
    If Dir(folderPath, vbDirectory) = "" Then fso.CreateFolder (folderPath)
    
    Dim strPath As String: strPath = ThisWorkbook.Path & "\" & logFolderName & "\" & logFileName
    Dim logMsg As String: logMsg = Now & vbTab & "NO:" & err.Number & vbTab & "DESC:" & err.Description
    
    ' エラー内容を出力
    With fso
        If Not .FileExists(strPath) Then
            .CreateTextFile (strPath)
        End If
        With .OpenTextFile(strPath, 8)
            .WriteLine logMsg
            .Close
        End With
    End With
    
    Set fso = Nothing

End Sub

vba:EXCELSQL(DBconnectionを部品化)

  • こちらのサイトを参考にさせていただきました。

excelwork.info

Sub SetData()

    ' 画面を初期化
    Rows("1:19").Select
    Selection.Delete Shift:=xlUp
    
    ' 列の選択状態を解除
    Sheet1.Range("A1").Select
    
    ' 実行ボタンを生成
    With ActiveSheet.Buttons.Add(Range("B5").Left, _
                                 Range("B5").Top, _
                                 Range("B5:D5").Width, _
                                 Range("B5:D6").Height)
        .Name = "btn"
        .OnAction = "Sheet1.MakeMaxValueTable"
        .Characters.Text = "exe"
    End With

    ' テストデータを出力
    Dim arr_id_data As Variant: arr_id_data = Array("e_01", "e_02", "e_03", "e_04", "e_05", "e_06", "e_07", "e_08", "e_09", "e_10")
    Dim arr_val_data As Variant: arr_val_data = Array(1000, 1300, 800, 1100, 700, 1000, 2100, 1100, 500, 800)

    For r = 0 To UBound(arr_id_data)
        Cells(r + 9, 2).Value = arr_id_data(r)
        Cells(r + 9, 3).Value = arr_val_data(r)
    Next r
    
    ' ヘッダーを設定
    Sheet1.Range("B8").Value = "ID"
    Sheet1.Range("C8:C8").Value = "Value"
    Sheet1.Range("E8").Value = "閾値"
    
    ' 閾値を設定
    Sheet1.Range("E9").Value = 800
    
    
    ' *** 全体的な見た目の設定 ****
    
    Call SetFormat(Sheet1, Sheet1.Range("B8:C8"), "id")
    Call SetFormat(Sheet1, Sheet1.Range("B9:B18"), "id")
    Call SetFormat(Sheet1, Sheet1.Range("C9:C18"), "val")
     
    Call SetFormat(Sheet1, Sheet1.Range("E8:E8"), "id")
    Call SetFormat(Sheet1, Sheet1.Range("E9:E9"), "val")
    
    
    ' フォーカスを所定の位置へ
    Sheet1.Range("A1").Select
    
End Sub
' 最大値を抽出し強調表示する
Sub MakeMaxValueTable()

    ' =====================
    ' 開始処理
    ' =====================
    ' 結果表示時のちらつきが気持ち悪いので、非表示
    Application.ScreenUpdating = False
    
    '画面初期化
    ' 条件付き書式をクリア
    Dim ws As Worksheet
    For Each ws In Worksheets
        ws.Cells.FormatConditions.Delete
    Next ws
    
    ' グラフをクリア
    With Sheet1
    
        ' 実行結果出力範囲をクリア
        .Rows("2:3").Clear
    
        Dim r As Long
        For r = .ChartObjects.Count To 1 Step -1
            .ChartObjects(r).Delete
        Next r
    End With
    
    
    ' =====================
    ' 入力チェック
    ' =====================
    'リストの値を取得して、配列に格納
    Dim arr_ori As Variant: arr_ori = Sheet1.Range("B9:C18").Value
    
    ' 閾値を取得
    Dim line_val As Long: line_val = Sheet1.Range("E9").Value
    
    ' 最大値を取得
    Dim max_val As Long: max_val = Application.WorksheetFunction.Max(arr_ori)
    
    ' 最大値 <= 閾値は処理終了
    If (max_val <= line_val) Then
    
        Dim rc As Integer
            
        rc = MsgBox("閾値は最大値より小さい値を設定してください。" & vbCrLf & "最大値:" & max_val, vbOKOnly + vbExclamation)
        
        If rc = vbOK Then
            Exit Sub
        End If
    
    End If
    
    
    ' =====================
    ' コネクションOPEN
    ' =====================
    ' 処理対象ファイルのフルパス
    Dim fileFullName As String: fileFullName = ThisWorkbook.FullName

    Dim dbCon As DBconnection
    Set dbCon = New DBconnection
    If Not dbCon.IsConnectedDB("excelsql", fileFullName) Then
        ' エラーログをtxtに出力する処理を追加する
        Debug.Print "システムエラー:DB接続に失敗しました。"
        Exit Sub
    End If
    
    
    ' =====================
    ' SQLを作成して実行
    ' =====================
    Dim sqlStr As String
'    ' シート名のパラメータ化が必要
'    sqlStr = ""
'    sqlStr = sqlStr & " select"
'    sqlStr = sqlStr & "     *"
'    sqlStr = sqlStr & " from"
'    sqlStr = sqlStr & "     [Sheet1$]"
'    sqlStr = sqlStr & " where"
'    sqlStr = sqlStr & "         Value > " & line_val
    sqlStr = "select * from [Sheet1$] where Value > " & line_val
    Set rs = dbCon.ExeSelect(sqlStr)
    
    
    ' =====================
    ' SQL実行結果出力
    ' =====================
    ' 抽出結果を出力・・・縦
'    Sheet1.Range("G2").CopyFromRecordset rs

    ' 抽出結果を出力・・・横
    Dim fieldCnt As Long: fieldCnt = rs.Fields.Count
    Dim recordCnt As Long: recordCnt = rs.RecordCount
    
    ' これで、横に格納してるみたいです
    'ReDim recordArray(fieldCnt, recordCnt) As Variant: recordArray = rs.GetRows
    
    'シートに出力
    ' 横に出力する場合は、起点のセルだけ指定だとうまくいかないみたいなので、出力するRangeを指定する。
    'Sheet1.Range(Cells(2, 2), Cells(fieldCnt, recordCnt + 1)) = recordArray
    ' 抽出結果で特に何もしないなら、直接設定する方が良い
    Sheet1.Range(Cells(2, 2), Cells(fieldCnt, recordCnt + 1)) = rs.GetRows
    
      
    ' =====================
    ' 最大値の設定
    ' =====================
    Dim fc As FormatCondition
    
    ' range・・・入れ子だと見辛いので、変数に格納
    Dim resultDataRange As Range: Set resultDataRange = Sheet1.Range(Cells(3, 2), Cells(fieldCnt, recordCnt + 1))

    ' 条件付き書式を生成・・・入れ子だと見辛いので、変数に格納(.Addressの絶対参照を忘れないこと)
    ' MAX関数は、最大値が複数ある場合は、存在するだけ最大値と判定します
    Dim strFormula As String: strFormula = "=B3=MAX(" & resultDataRange.Address & ")"
    
    Set fc = resultDataRange.FormatConditions.Add( _
        Type:=xlExpression, _
        Formula1:=strFormula)
        
    ' 最大値の見た目を設定
    fc.Interior.Color = RGB(255, 0, 0)
    fc.Font.Color = RGB(255, 255, 255)
    fc.Font.Bold = True
        
        
    ' =====================
    ' グラフを生成
    ' =====================
    Call MakeChart(Sheet1, fieldCnt, recordCnt, line_val)


    ' =====================
    ' 全体的な見た目の設定
    ' =====================
    Call SetFormat(Sheet1, Sheet1.Range(Cells(2, 2), Cells(2, recordCnt + 1)), "id")
    Call SetFormat(Sheet1, Sheet1.Range(Cells(3, 2), Cells(3, recordCnt + 1)), "val")


    ' =====================
    ' 終了処理
    ' =====================
    ' フォーカスを所定の位置へ
    Sheet1.Range("A1").Select
    Application.ScreenUpdating = True

End Sub
' 書式を設定
' 項目:id 、値:val
Sub SetFormat(ByVal sh As Worksheet, ByVal rng As Range, ByVal item As String)

    sh.Select

    rng.Select
    
    With Selection
        ' 外枠の設定・・・ちなみにこれは、中太
        '.BorderAround Weight:=xlMedium
        .Borders.LineStyle = True
        
        Select Case item
            
            Case "id"
                .HorizontalAlignment = xlCenter
                .Interior.Color = rgbTurquoise
                
            Case "val"
                .NumberFormatLocal = "#,###"
                
        End Select
        
    End With

End Sub

' グラフを生成
Sub MakeChart(ByVal sh As Worksheet, ByVal fieldCnt As Long, ByVal recordCnt As Long, ByVal line_val As Long)

    sh.Select

    ' 配列に入れなおさないとうまくいかないので、入れなおす。(この辺は、要検証)
    Dim arr_ans() As Variant: arr_ans = Sheet1.Range(Cells(2, 2), Cells(fieldCnt, recordCnt + 1))
    
    ' グラフ生成用に、ヘッダーと値の配列を作成
    Dim arr_header As Variant: arr_header = WorksheetFunction.Transpose(WorksheetFunction.Index(arr_ans, 1, 0))
    Dim arr_val As Variant: arr_val = WorksheetFunction.Transpose(WorksheetFunction.Index(arr_ans, 2, 0))

    Dim co As ChartObject
   
    ' グラフの外枠?を設置
    Set co = ActiveSheet.ChartObjects.Add(Left:=Range("G8").Left, Top:=Range("G8").Top, Width:=324, Height:=206)
  
    ' グラフの中身を生成
    With co
        With .Chart.SeriesCollection.NewSeries
            .Name = "Value"
            .Values = arr_val
            .XValues = arr_header
        End With
    End With
   
    With co.Chart
    
        'グラフ形式を設定
        co.Chart.ChartType = xlColumnClustered
    
        '凡例非表示
        co.Chart.HasLegend = True
        
        ' グラフの左線を強調(線の太さと色・・・結構見やすいかも)
        .Axes(xlValue).Format.Line.Weight = 2
        .Axes(xlValue).Format.Line.ForeColor.RGB = RGB(0, 0, 0)
        
        ' グラフの下線を強調
        .Axes(xlCategory).Format.Line.Weight = 2
        .Axes(xlCategory).Format.Line.ForeColor.RGB = RGB(0, 0, 0)
        
         
        '基準線を新規系列として追加
        With .SeriesCollection.NewSeries
            .Name = "=""閾値""" '特に必要はない
            .Values = "={" & Join(Array(line_val, line_val), ",") & "}"
            .ChartType = xlLine '折れ線
            .AxisGroup = 2 '第2軸
            .Format.Line.ForeColor.RGB = vbRed 'RGB(255, 0, 0)
        End With
    
        '第2縦軸のScaleを第1縦軸に合わせる
        .Axes(xlValue, 2).MinimumScale = .Axes(xlValue, 1).MinimumScale
        .Axes(xlValue, 2).MaximumScale = .Axes(xlValue, 1).MaximumScale
        .Axes(xlValue, 2).Delete '第2軸を消す
    
        'グラフ内左右余白を消す:基準線を第2横軸に設定
        .HasAxis(xlCategory, 2) = True '第2横軸表示
        .Axes(xlCategory, 2).AxisBetweenCategories = False '軸位置を目盛
        .Axes(xlCategory, 2).TickLabelPosition = xlNone '目盛ラベルなし
    
    End With
    
End Sub
  • DBconnectionクラス
Option Explicit

Private cn As ADODB.Connection
Private rs As ADODB.Recordset

'-------------------------------------------------------------------------------------
' コンストラクタ
'-------------------------------------------------------------------------------------
Private Sub class_initialize()

    If Not rs Is Nothing Then rs.Close
    
End Sub

'-------------------------------------------------------------------------------------
' デストラクタ
'-------------------------------------------------------------------------------------
Private Sub class_terminate()
    
    On Error Resume Next
    If Not rs Is Nothing Then rs.Close
    Set rs = Nothing
    
    cn.Close
    Set cn = Nothing
    
End Sub

'-------------------------------------------------------------------------------------
' データベース接続
'【引数】DBType    接続するDBを指定
'                  excelsql … Excelをデータベースとして接続
'【戻値】接続成功:True / 接続失敗:False(Boolean)
'-------------------------------------------------------------------------------------
Public Function IsConnectedDB(ByVal useDb As String, ByVal fileFullName As String) As Boolean

    Dim ConnectingString As String
    
    Select Case useDb
        
        Case "excelsql"
            ConnectingString = "PROVIDER = MSDASQL; Driver={Microsoft Excel Driver (*.xls, *.xlsx, *.xlsm, *.xlsb)}; " & "DBQ=" & fileFullName & "; ReadOnly=False;"
        
        Case Else
            GoTo ErrHandler
    
    End Select
    
    On Error GoTo ErrHandler
    
    Set cn = New ADODB.Connection
    cn.ConnectionString = ConnectingString
    cn.ConnectionTimeout = 2
    cn.Open
    IsConnectedDB = True
    
    Exit Function
    
ErrHandler:
    IsConnectedDB = False
    
    
End Function

'-------------------------------------------------------------------------------------
' SQL文を実行する(Select 文)
'【引数】strSQL    SQL文
'【戻値】Recordset オブジェクト
'-------------------------------------------------------------------------------------
Public Function ExeSelect(ByVal strSQL As String) As ADODB.Recordset

    Set rs = New ADODB.Recordset
    
    'SQL文実行(読み取り専用、共有ロック)
    rs.Open strSQL, cn, adOpenKeyset, adLockOptimistic, adCmdText
    
    Set ExeSelect = rs

End Function

'-------------------------------------------------------------------------------------
' SQL文を実行する(Insert into文、Delete 文など)
'【引数】strSQL    SQL文(String)
'【戻値】変更されたレコード数(Long)
'-------------------------------------------------------------------------------------
Public Function ExeUpdate(strSQL As String) As Long

    Dim updateCnt As Long
    
    cn.Execute strSQL, updateCnt
    
    ExeUpdate = updateCnt

End Function

'-------------------------------------------------------------------------------------
'トランザクション開始
'-------------------------------------------------------------------------------------
Public Sub BeginTr()
    cn.BeginTrans
End Sub

'-------------------------------------------------------------------------------------
' トランザクションコミット
'-------------------------------------------------------------------------------------
Public Sub CommitTr()
    cn.CommitTrans
End Sub

'-------------------------------------------------------------------------------------
' トランザクションロールバック
'-------------------------------------------------------------------------------------
Public Sub RollbackTr()
    cn.RollbackTrans
End Sub

vba:課題(EXCELSQLバージョン)

EXCELSQLバージョン

  • 速度は、よくわからない。
  • ただ、閾値以上の配列を作成する処理が、SQLで代替できるので、保守性は高いと思う。
Sub SetData()

    ' 画面を初期化
    Rows("1:19").Select
    Selection.Delete Shift:=xlUp
    
    ' 列の選択状態を解除
    Sheet1.Range("A1").Select
    
    ' 実行ボタンを生成
    With ActiveSheet.Buttons.Add(Range("B5").Left, _
                                 Range("B5").Top, _
                                 Range("B5:D5").Width, _
                                 Range("B5:D6").Height)
        .Name = "btn"
        .OnAction = "Sheet1.MakeMaxValueTable"
        .Characters.Text = "exe"
    End With

    ' テストデータを出力
    Dim arr_id_data As Variant: arr_id_data = Array("e_01", "e_02", "e_03", "e_04", "e_05", "e_06", "e_07", "e_08", "e_09", "e_10")
    Dim arr_val_data As Variant: arr_val_data = Array(1000, 1300, 800, 1100, 700, 1000, 2100, 1100, 500, 800)

    For r = 0 To UBound(arr_id_data)
        Cells(r + 9, 2).Value = arr_id_data(r)
        Cells(r + 9, 3).Value = arr_val_data(r)
    Next r
    
    ' ヘッダーを設定
    Sheet1.Range("B8").Value = "ID"
    Sheet1.Range("C8:C8").Value = "Value"
    Sheet1.Range("E8").Value = "閾値"
    
    ' 閾値を設定
    Sheet1.Range("E9").Value = 800
    
    
    ' *** 全体的な見た目の設定 ****
    
    Call SetFormat(Sheet1, Sheet1.Range("B8:C8"), "id")
    Call SetFormat(Sheet1, Sheet1.Range("B9:B18"), "id")
    Call SetFormat(Sheet1, Sheet1.Range("C9:C18"), "val")
     
    Call SetFormat(Sheet1, Sheet1.Range("E8:E8"), "id")
    Call SetFormat(Sheet1, Sheet1.Range("E9:E9"), "val")
    
    
    ' フォーカスを所定の位置へ
    Sheet1.Range("A1").Select
    
End Sub
' 最大値を抽出し強調表示する
Sub MakeMaxValueTable()

    ' =====================
    ' 開始処理
    ' =====================
    ' 結果表示時のちらつきが気持ち悪いので、非表示
    Application.ScreenUpdating = False
    
    '画面初期化
    ' 条件付き書式をクリア
    Dim ws As Worksheet
    For Each ws In Worksheets
        ws.Cells.FormatConditions.Delete
    Next ws
    
    ' グラフをクリア
    With Sheet1
    
        ' 実行結果出力範囲をクリア
        .Rows("2:3").Clear
    
        Dim r As Long
        For r = .ChartObjects.Count To 1 Step -1
            .ChartObjects(r).Delete
        Next r
    End With
    
    
    ' =====================
    ' 入力チェック
    ' =====================
    'リストの値を取得して、配列に格納
    Dim arr_ori As Variant: arr_ori = Sheet1.Range("B9:C18").Value
    
    ' 閾値を取得
    Dim line_val As Long: line_val = Sheet1.Range("E9").Value
    
    ' 最大値を取得
    Dim max_val As Long: max_val = Application.WorksheetFunction.Max(arr_ori)
    
    ' 最大値 <= 閾値は処理終了
    If (max_val <= line_val) Then
    
        Dim rc As Integer
            
        rc = MsgBox("閾値は最大値より小さい値を設定してください。" & vbCrLf & "最大値:" & max_val, vbOKOnly + vbExclamation)
        
        If rc = vbOK Then
            Exit Sub
        End If
    
    End If
    
    
    ' =====================
    ' コネクションOPEN
    ' =====================
    ' 処理対象ファイルのフルパス
    Dim fileFullName As String
    fileFullName = ThisWorkbook.FullName

    Dim cn As ADODB.Connection
    Set cn = New ADODB.Connection
    cn.PROVIDER = "MSDASQL"
    cn.ConnectionString = "Driver={Microsoft Excel Driver (*.xls, *.xlsx, *.xlsm, *.xlsb)}; " & "DBQ=" & fileFullName & "; ReadOnly=False;"
    cn.Open


    ' =====================
    ' SQL実行→結果取得
    ' =====================
    Dim rs As ADODB.Recordset:
    Set rs = New ADODB.Recordset

    Dim sqlStr As String
    ' シート名のパラメータ化が必要
    sqlStr = ""
    sqlStr = sqlStr & " select"
    sqlStr = sqlStr & "     *"
    sqlStr = sqlStr & " from"
    sqlStr = sqlStr & "     [Sheet1$]"
    sqlStr = sqlStr & " where"
    sqlStr = sqlStr & "         Value > " & line_val
    
    ' adOpenStatic・・・よくわからないが、静的カーソル(他ユーザーによる追加・更新・削除は反映されない)と説明があり、テーブルロックしている?ってこと?
    rs.Open sqlStr, cn, adOpenStatic
   
   
    ' =====================
    ' SQL実行結果出力
    ' =====================
    ' 抽出結果を出力・・・縦
'    Sheet1.Range("G2").CopyFromRecordset rs

    ' 抽出結果を出力・・・横
    Dim fieldCnt As Integer: fieldCnt = rs.Fields.Count
    Dim recordCnt As Long: recordCnt = rs.RecordCount
    
    ' これで、横に格納してるみたいです
    'ReDim recordArray(fieldCnt, recordCnt) As Variant: recordArray = rs.GetRows
    
    'シートに出力
    ' 横に出力する場合は、起点のセルだけ指定だとうまくいかないみたいなので、出力するRangeを指定する。
    'Sheet1.Range(Cells(2, 2), Cells(fieldCnt, recordCnt + 1)) = recordArray
    ' 抽出結果で特に何もしないなら、直接設定する方が良い
    Sheet1.Range(Cells(2, 2), Cells(fieldCnt, recordCnt + 1)) = rs.GetRows
    
    
    ' =====================
    ' コネクションCLOSE
    ' =====================
    rs.Close
    cn.Close
    Set rs = Nothing
    Set cn = Nothing
    
' 配列バージョン
'    Dim i As Long
'
'    Dim arrayCount As Long: arrayCount = UBound(arr_ori, 1)
'    ReDim arr_ans(LBound(arr_ori, 2) To UBound(arr_ori, 2), 1 To UBound(arr_ori, 1))
'
'    ' 閾値以上の値を抽出して、配列に再格納(行と列を入れ替えて格納する)
'    ' j = 1で、IDを処理:IDに紐づく値が、閾値より大きい場合、IDを格納
'    ' j = 2で、値を処理:閾値より大きい場合、値を格納
'    For j = LBound(arr_ori, 2) To UBound(arr_ori, 2)
'
'        Dim c As Long: c = 1
'        For i = LBound(arr_ori, 1) To UBound(arr_ori, 1)
'
'            If arr_ori(i, 2) > line_val Then
'
'                arr_ans(j, c) = arr_ori(i, j)
'                c = c + 1
'            End If
'
'        Next i
'    Next j
'
'    ' 閾値より大きい値の個数を、最大列数としてRangeに設定
'    Dim clm As Long: clm = c
'
'    '再格納した配列をエクセルに出力
'    Sheet1.Range(Cells(2, 2), Cells(3, clm)).Value = arr_ans
    
    
    ' =====================
    ' 最大値の設定
    ' =====================
    Dim fc As FormatCondition
    
    ' range・・・入れ子だと見辛いので、変数に格納
    Dim resultDataRange As Range: Set resultDataRange = Sheet1.Range(Cells(3, 2), Cells(fieldCnt, recordCnt + 1))

    ' 条件付き書式を生成・・・入れ子だと見辛いので、変数に格納(.Addressの絶対参照を忘れないこと)
    ' MAX関数は、最大値が複数ある場合は、存在するだけ最大値と判定します
    Dim strFormula As String: strFormula = "=B3=MAX(" & resultDataRange.Address & ")"
    
    Set fc = resultDataRange.FormatConditions.Add( _
        Type:=xlExpression, _
        Formula1:=strFormula)
        
    ' 最大値の見た目を設定
    fc.Interior.Color = RGB(255, 0, 0)
    fc.Font.Color = RGB(255, 255, 255)
    fc.Font.Bold = True
        
        
    ' =====================
    ' グラフを生成
    ' =====================
    ' 配列に入れなおさないとうまくいかないので、入れなおす。(この辺は、要検証)
    Dim arr_ans() As Variant: arr_ans = Sheet1.Range(Cells(2, 2), Cells(fieldCnt, recordCnt + 1))
       
    ' グラフ生成用に、ヘッダーと値の配列を作成
    Dim arr_ans_id As Variant: arr_ans_id = WorksheetFunction.Transpose(WorksheetFunction.Index(arr_ans, 1, 0))
    Dim arr_ans_val As Variant: arr_ans_val = WorksheetFunction.Transpose(WorksheetFunction.Index(arr_ans, 2, 0))
    
    Call MakeChart(Sheet1, arr_ans_id, arr_ans_val, line_val)


    ' =====================
    ' 全体的な見た目の設定
    ' =====================
    Call SetFormat(Sheet1, Sheet1.Range(Cells(2, 2), Cells(2, recordCnt + 1)), "id")
    Call SetFormat(Sheet1, Sheet1.Range(Cells(3, 2), Cells(3, recordCnt + 1)), "val")


    ' =====================
    ' 終了処理
    ' =====================
    ' フォーカスを所定の位置へ
    Sheet1.Range("A1").Select
    Application.ScreenUpdating = True

End Sub
' 書式を設定
' 項目:id 、値:val
Sub SetFormat(ByVal sh As Worksheet, ByVal rng As Range, ByVal item As String)

    sh.Select

    rng.Select
    
    With Selection
        ' 外枠の設定・・・ちなみにこれは、中太
        '.BorderAround Weight:=xlMedium
        .Borders.LineStyle = True
        
        Select Case item
            
            Case "id"
                .HorizontalAlignment = xlCenter
                .Interior.Color = rgbTurquoise
                
            Case "val"
                .NumberFormatLocal = "#,###"
                
        End Select
        
    End With

End Sub
' グラフを生成
Sub MakeChart(ByVal sh As Worksheet, ByVal arr_header As Variant, ByVal arr_val As Variant, ByVal line_val As Long)

    sh.Select

    Dim co As ChartObject
   
    ' グラフの外枠?を設置
    Set co = ActiveSheet.ChartObjects.Add(Left:=Range("G8").Left, Top:=Range("G8").Top, Width:=324, Height:=206)
  
    ' グラフの中身を生成
    With co
        With .Chart.SeriesCollection.NewSeries
            .Name = "Value"
            .Values = arr_val
            .XValues = arr_header
        End With
    End With
   
    With co.Chart
    
        'グラフ形式を設定
        co.Chart.ChartType = xlColumnClustered
    
        '凡例非表示
        co.Chart.HasLegend = True
        
        ' グラフの左線を強調(線の太さと色・・・結構見やすいかも)
        .Axes(xlValue).Format.Line.Weight = 2
        .Axes(xlValue).Format.Line.ForeColor.RGB = RGB(0, 0, 0)
        
        ' グラフの下線を強調
        .Axes(xlCategory).Format.Line.Weight = 2
        .Axes(xlCategory).Format.Line.ForeColor.RGB = RGB(0, 0, 0)
        
         
        '基準線を新規系列として追加
        With .SeriesCollection.NewSeries
            .Name = "=""閾値""" '特に必要はない
            .Values = "={" & Join(Array(line_val, line_val), ",") & "}"
            .ChartType = xlLine '折れ線
            .AxisGroup = 2 '第2軸
            .Format.Line.ForeColor.RGB = vbRed 'RGB(255, 0, 0)
        End With
    
        '第2縦軸のScaleを第1縦軸に合わせる
        .Axes(xlValue, 2).MinimumScale = .Axes(xlValue, 1).MinimumScale
        .Axes(xlValue, 2).MaximumScale = .Axes(xlValue, 1).MaximumScale
        .Axes(xlValue, 2).Delete '第2軸を消す
    
        'グラフ内左右余白を消す:基準線を第2横軸に設定
        .HasAxis(xlCategory, 2) = True '第2横軸表示
        .Axes(xlCategory, 2).AxisBetweenCategories = False '軸位置を目盛
        .Axes(xlCategory, 2).TickLabelPosition = xlNone '目盛ラベルなし
    
    End With
    
End Sub

vba:EXCELSQL

内容

  • シートのテーブル(テーブル化は不要みたい)に対して、SQLで処理を行う。
' Excelシートのデータに対して、SQLで対象を取得する
Sub ExeSql()

    ' 処理対象ファイルのフルパス
    Dim fileFullName As String
    fileFullName = ThisWorkbook.FullName

    Dim cn As ADODB.Connection
    Set cn = New ADODB.Connection
    
    cn.PROVIDER = "MSDASQL"
    cn.ConnectionString = "Driver={Microsoft Excel Driver (*.xls, *.xlsx, *.xlsm, *.xlsb)}; " & "DBQ=" & fileFullName & "; ReadOnly=False;"
    cn.Open

    Dim rs As ADODB.recordset
    Set rs = New ADODB.recordset

    Dim lineLimit As String: lineLimit = 4000
    Dim sqlStr As String
    sqlStr = ""
    
    ' シンプルSQL
    sqlStr = sqlStr & " select"
    sqlStr = sqlStr & "     *"
    sqlStr = sqlStr & " from"
    sqlStr = sqlStr & "     [Sheet1$]"
    sqlStr = sqlStr & " where"
    sqlStr = sqlStr & "         LINE_LIMIT > " & lineLimit

' joinしてみる
'    sqlStr = sqlStr & " select a.* from [Sheet1$] a left join [Sheet5$] b on a.ITEM_CD = b.ITEM_CD"
'    sqlStr = sqlStr & " where b.LINE_NO = 2"
    
' case(IIF)してみる・・・IIFを使ったときは、どうやらカラムを個別で指定する必要がある。・・・*とかを使った場合は、先頭にIIFの検索結果が出力されるみたいです
'    sqlStr = sqlStr & " select LINE_CD, LINE_LIMIT, ITEM_CD, IIF(LINE_LIMIT = 500,1,IIF(LINE_LIMIT = 1000,2,3)) as ll  from [Sheet1$] "
    
    Debug.Print sqlStr
    
    ' adOpenStatic・・・よくわからないが、静的カーソル(他ユーザーによる追加・更新・削除は反映されない)と説明があり、テーブルロックしている?ってこと?
    rs.Open sqlStr, cn, adOpenStatic
   
    ' 抽出結果を出力・・・縦
'    Sheet1.Range("G2").CopyFromRecordset rs

    ' 抽出結果を出力・・・横
    Dim fieldCnt As Integer: fieldCnt = rs.Fields.Count
    Dim recordCnt As Long: recordCnt = rs.RecordCount
    ' これで、横に格納してるみたいです
    ReDim recordArray(fieldCnt, recordCnt) As Variant: recordArray = rs.GetRows
    
    'シートに出力
    ' 横に出力する場合は、起点のセルだけ指定だとうまくいかないみたいなので、出力するRangeを指定する。
    Sheet1.Range(Cells(1, 7), Cells(fieldCnt, recordCnt + 6)) = recordArray
    
    rs.Close
    cn.Close
    Set rs = Nothing
    Set cn = Nothing

End Sub

vba:ListObjects(今思っていること)

基本的な考え方

  • DBから、データを取得する時に、できる計算処理はなるべくSQLで行う。
  • VBAでは、できる限り分析用に成形する処理を行う。
  • DBからデータを取得、シートに出力、テーブル化、成形処理を実施。

ListObjectsについて(本題)

  • ListObjectsをできる限り利用するようにしたいと考えている。
  • 理由としては、最終行などの取得などの処理がないこと。
  • 多分、関数を使う時よりも、速い気がする。
  • どちらかというと、多少の処理速度よりもソースの見やすさや改修のしやすさを重視したいので、たぶん、ListObjectsの方がシンプルなソースになると思う。
Sub CalcKyakutanka()

    If Sheet1.ListObjects.Count > 0 Then
        Sheet1.ListObjects(1).Unlist
    End If
    
    'Sheet1.Cells.Clear
    
    Stop

    With Sheet1
    
        .Range("A1").Select
        
        With .ListObjects.Add
        
            ' テーブル名・・・省略可能
'            .Name = "tbl_Kyakutanka"

            ' 行からセルを特定・・・2行目の4列目
'            .ListRows(1).Range.Item(4).Formula = "=[@売上]/[@客数]"

            ' 列からセルを特定・・・4列目の2行目
'            .ListColumns(4).Range(2).Formula = "=INT([@売上]/[@客数])" '・・・整数
            .ListColumns(4).Range(2).Formula = "=ROUND([@売上]/[@客数],2)" '・・・小数点第2で、丸目
            
            ' 列(列名)から、セルを特定・・・客単価列の2行目
'            .ListColumns("客単価").Range(2).Formula = "=[@売上]/[@客数]"

            ' 小数点を設定・・・注意:四捨五入で繰り上げされます
            '.ListColumns(4).Range.NumberFormatLocal = "##.##"
                
            Dim arr_ans_uriage As Variant: arr_ans_uriage = .ListColumns(2).DataBodyRange
            Dim arr_ans_kyakusu As Variant: arr_ans_kyakusu = .ListColumns(3).DataBodyRange
            Dim arr_ans_kyakutanka As Variant: arr_ans_kyakutanka = .ListColumns(4).DataBodyRange
    
        End With

    End With

    With Sheet1.ListObjects(1)
    
        Call MakeChart(Sheet1, arr_ans_uriage, 34500, .ListColumns(2).Range(1).Value, Range("F2"), Range("K2"))
        Call MakeChart(Sheet1, arr_ans_kyakusu, 50, .ListColumns(3).Range(1).Value, Range("F14"), Range("K14"))
        Call MakeChart(Sheet1, arr_ans_kyakutanka, 1000, .ListColumns(4).Range(1).Value, Range("F26"), Range("K26"))
'        Call MakeChart(Sheet1, arr_ans_kyakutanka, arr_ans_uriage, 50, .ListColumns(4).Range(1).Value, Range("F2"), Range("K2"))
    
    End With

End Sub


' グラフを生成
'Sub MakeChart(ByVal sh As Worksheet, ByVal arr_header As Variant, ByVal arr_val As Variant, ByVal line_val As Long)
Sub MakeChart(ByVal sh As Worksheet, ByVal arr_val As Variant, ByVal line_val As Long, ByVal chartNames As String, ByVal lRng As Range, ByVal tRnge As Range)
'Sub MakeChart(ByVal sh As Worksheet, ByVal arr_val As Variant, ByVal arr_val_line As Variant, ByVal line_val As Long, ByVal chartNames As String, ByVal lRng As Range, ByVal tRnge As Range)

    sh.Select

    Dim co As ChartObject
   
    ' グラフの外枠?を設置
    Set co = ActiveSheet.ChartObjects.Add(Left:=lRng.Left, Top:=tRnge.Top, Width:=300, Height:=200)
  
    ' グラフの中身を生成
    With co
        With .Chart.SeriesCollection.NewSeries
            .Name = chartNames
            .Values = arr_val
            '.XValues = arr_header
        End With
    End With
   
    With co.Chart
    
        ' グラフのスタイルを設定
        .ChartStyle = 209
    
        'グラフ形式を設定
        co.Chart.ChartType = xlColumnClustered
    
        '凡例非表示
        co.Chart.HasLegend = True
        
'        ' グラフの左線を強調(線の太さと色・・・結構見やすいかも)
'        .Axes(xlValue).Format.Line.Weight = 2
'        .Axes(xlValue).Format.Line.ForeColor.RGB = RGB(0, 0, 0)
'
'        ' グラフの下線を強調
'        .Axes(xlCategory).Format.Line.Weight = 2
'        .Axes(xlCategory).Format.Line.ForeColor.RGB = RGB(0, 0, 0)
        
         
'        '折れ線グラフを追加
'        With .SeriesCollection.NewSeries
'            .Name = "客数"
'            .Values = arr_val_line
'            .ChartType = xlLineMarkers '折れ線
'            .AxisGroup = 2 '第2軸
'            .Format.Line.ForeColor.RGB = RGB(255, 192, 0)
'        End With
        
        '基準線を新規系列として追加
        With .SeriesCollection.NewSeries
            .Name = "閾値" '特に必要はない
            .Values = "={" & Join(Array(line_val, line_val), ",") & "}"
            .ChartType = xlLine '折れ線
            .AxisGroup = 2 '第2軸
            .Format.Line.ForeColor.RGB = RGB(255, 192, 0)
        End With
    
        '第2縦軸のScaleを第1縦軸に合わせる
        .Axes(xlValue, 2).MinimumScale = .Axes(xlValue, 1).MinimumScale
        .Axes(xlValue, 2).MaximumScale = .Axes(xlValue, 1).MaximumScale
        .Axes(xlValue, 2).Delete '第2軸を消す
    
        'グラフ内左右余白を消す:基準線を第2横軸に設定
        .HasAxis(xlCategory, 2) = True '第2横軸表示
        .Axes(xlCategory, 2).AxisBetweenCategories = False '軸位置を目盛
        .Axes(xlCategory, 2).TickLabelPosition = xlNone '目盛ラベルなし
    
    End With
    
End Sub