Power Query With Excel VBA

#0.Intro
仕事で必要になったので色々調べながらコーディングしてました。日本語情報がまだまだ少ないので調べたことをメモがてらシェアさせて頂きます。
とはいえそれほどまだ精通できているわけではないので細かいことはわかってません。また僕自身VBAから使いたかったのでPower Query Editorの方は大して調べてませんのにで書けません。主にDBの変わりとしてVBAから使う基本的な使い方のみ記載させて頂きます。
_(2020/09/11　現在、仮公開の段階です)_

#1.基本的な使い方。
Power Queryを使うにはまず何より、その名の通りデータ(つまりシート)をクエリに変換するところから始まります。

流れとしては、

```
Excelシートのデータ
↓
デーブル化
↓
クエリ作成
```
と進みます。

Power QueryにおいてSQLに相当する物はM言語です。僕自身まだ色々試してみないといけませんが、それほどにややこしいものではなさそうなのと、基本的に文字列で命令を出すので変数等も柔軟に織り込めそうです。

#2.目的別に纏めた関数
1.ExcelシートからPower Queryのクエリを作成

```
'------------------------------------------------
'  引  数：arg_sheet_name    対象シート
'  戻り値：生成されたクエリ名
'------------------------------------------------
Function ConvertTableToQuery(arg_sheet_name As String) As String
On Error GoTo HandleErr
    Dim Table_Name As String: Table_Name = arg_sheet_name & "_Table"
    Dim SheetName As String: SheetName = arg_sheet_name
    
    If IsQuerrExist(Table_Name) = True Then
        MsgBox Table_Name & ":Already Exist"
        ConvertTableToQuery = vbNullString
        Exit Function
    End If

    ThisWorkbook.Sheets(SheetName).ListObjects.Add(xlSrcRange, ThisWorkbook.Sheets(SheetName).Range("$A$1:$" & ConvNumToAlphabet(CountColumn(ThisWorkbook.Sheets(SheetName))) & "$" & CountRow(ThisWorkbook.Sheets(SheetName))), , xlYes).Name = Table_Name
    ThisWorkbook.Queries.Add Name:=Table_Name, Formula:="let Source = Excel.CurrentWorkbook(){[Name=""" & Table_Name & """]}[Content],  MODIED_TYPE = Table.TransformColumnTypes(Source,{" & GetShemaString(ThisWorkbook.Sheets(SheetName)) & "}) , AddIndex = Table.AddIndexColumn(MODIED_TYPE, ""Index_a"", 0, 1, Int64.Type) in AddIndex"
    
    ConvertTableToQuery = Table_Name
    Exit Function
HandleErr:
    MsgBox "Error ConvertTableToQuery:" & Err.Number & vbCrLf & "Description:" & Err.Description
    ConvertTableToQuery = vbNullString
End Function
```
2.クエリを作成する際に必要なカラム一覧1

```
'------------------------------------------------
'  引  数：対象シート
'  戻り値：カラム一覧
'------------------------------------------------
Function GetShemaString(SheetName As Worksheet)
    Dim ColumnArr() As String
    Dim i As Long
    Dim rec As String: rec = vbNullString
    Dim t_maxcol As Long
    t_maxcol = CountColumn(SheetName)
    
    
    For i = 1 To t_maxcol
        ReDim Preserve ColumnArr(i - 1)
        ColumnArr(i - 1) = "{""" & SheetName.Cells(1, i).value & """, type text}"
    Next i
    
    For i = 0 To UBound(ColumnArr)
        rec = rec & ColumnArr(i) & ","
    Next i
    
    rec = CutRight(rec, 1)
    
    GetShemaString = rec

End Function
```
3.クエリを外部結合でマージする(要はLEFT OUTER JOIN)

```
'------------------------------------------------
'  引  数：
'  arg_LeftQuery    結合の基準となるテーブル名
'  argRightQuery    他方のテーブル
'  Key_col    結合条件のカラム
'  MergeQueryName    生成するクエリ名
'  戻り値：True 成功 / False 失敗 
'------------------------------------------------
Function DistMergeQuery(arg_LeftQuery As String, argRightQuery As String, Key_col As String, MergeQueryName As String) As Boolean
On Error GoTo HandleErr
    
    ActiveWorkbook.Queries.Add Name:=MergeQueryName, Formula:="let Source = Table.NestedJoin(" & arg_LeftQuery & ", {""" & Key_col & """}, " & argRightQuery & ", {""" & Key_col & """}, """ & argRightQuery & """, JoinKind.LeftOuter),  Merged_Table = Table.ExpandTableColumn(Source, """ & argRightQuery & """, {" & GetShemaStringForMerge(ThisWorkbook.Sheets(Left(argRightQuery, Len(argRightQuery) - 6))) & "}, {" & GetShemaStringForMerge2(ThisWorkbook.Sheets(Left(argRightQuery, Len(argRightQuery) - 6))) & "}), AddIndex = Table.AddIndexColumn(Merged_Table , """ & MergeQueryName & "_Index" & """, 0, 1, Int64.Type) in AddIndex"
    DistMergeQuery = MergeQueryName
    
    If DistQueryToSheet(ThisWorkbook.Sheets("WorkQueryDist"), MergeQueryName, "$A$1") = False Then
        DistMergeQuery = False
        Exit Function
    End If
    DistMergeQuery = True
    Exit Function
HandleErr:
    MsgBox "Error DistMergeQuery:" & Err.Number & vbCrLf & "Description:" & Err.Description
    DistMergeQuery = False
End Function
```
4.マージする際に必要なカラム一覧1

```
'------------------------------------------------
'  引  数：対象シート
'  戻り値：カラム一覧
'------------------------------------------------
Function GetShemaStringForMerge(SheetName As Worksheet) As String
    Dim ColumnArr() As String
    Dim i As Long
    Dim rec As String: rec = vbNullString
    Dim t_maxcol As Long
    t_maxcol = CountColumn(SheetName)
    
    For i = 1 To t_maxcol
        ReDim Preserve ColumnArr(i - 1)
        ColumnArr(i - 1) = """" & SheetName.Cells(1, i).value & """"
    Next i
    
    For i = 0 To UBound(ColumnArr)
        rec = rec & ColumnArr(i) & ","
    Next i
    
    rec = CutRight(rec, 1)
    
    GetShemaStringForMerge = rec

End Function
```
5.マージする際に必要なカラム一覧2

```
'------------------------------------------------
'  引  数：対象シート
'  戻り値：カラム一覧
'------------------------------------------------
Function GetShemaStringForMerge2(SheetName As Worksheet) As String
    Dim ColumnArr() As String
    Dim i As Long
    Dim rec As String: rec = vbNullString
    Dim t_maxcol As Long
    t_maxcol = CountColumn(SheetName)
    
    For i = 1 To t_maxcol
        ReDim Preserve ColumnArr(i - 1)
        ColumnArr(i - 1) = """" & SheetName.Name & "_Table." & SheetName.Cells(1, i).value & """"
    Next i
    
    For i = 0 To UBound(ColumnArr)
        rec = rec & ColumnArr(i) & ","
    Next i
    
    rec = CutRight(rec, 1)
    
    GetShemaStringForMerge2 = rec

End Function
```

6.クエリの内容をExcelシートに出力する

```
'------------------------------------------------
'  引  数：対象シート
'  戻り値：True 成功 / False 失敗 
'------------------------------------------------
Function DistQueryToSheet(sheet As Worksheet, queryname As String, rangestr As String) As Boolean
On Error GoTo HandleErr
    With sheet.ListObjects.Add(SourceType:=0, Source:= _
        "OLEDB;Provider=Microsoft.Mashup.OleDb.1;Data Source=$Workbook$;Location=" & queryname & ";Extended Properties=""""" _
        , Destination:=sheet.Range(rangestr)).QueryTable
        .CommandType = xlCmdSql
        .CommandText = Array("SELECT * FROM [" & queryname & "]")
        .RowNumbers = False
        .FillAdjacentFormulas = False
        .PreserveFormatting = True
        .RefreshOnFileOpen = False
        .BackgroundQuery = True
        .RefreshStyle = xlInsertDeleteCells
        .SavePassword = False
        .SaveData = True
        .AdjustColumnWidth = True
        .RefreshPeriod = 0
        .PreserveColumnInfo = True
        .ListObject.DisplayName = queryname
        .Refresh BackgroundQuery:=False
    End With
    DistQueryToSheet = True
    Exit Function
HandleErr:
    MsgBox "Error DistQueryToSheet:" & Err.Number & vbCrLf & "Description:" & Err.Description
    DistQueryToSheet = False
End Function
```

#3.Power Queryを使う際にあると便利な関数
1.テーブル化されたデータを解除し通常のシートに戻す

```
'------------------------------------------------
'  引  数：対象シート
'------------------------------------------------
Sub TableUnlist(sheet As Worksheet)
    On Error Resume Next
    Dim ls As ListObject
    
    For Each ls In sheet.ListObjects
        ls.TableStyle = ""
        ls.Unlist
    Next ls
End Sub
```

2.ブック中に同じ名前のクエリが存在しているかどうか確認

```
'------------------------------------------------
'  引  数：検査対象クエリ名
'  戻り値：True 存在する / False 存在しない
'------------------------------------------------
Function IsQuerrExist(qname As String) As Boolean
    Dim rec As Boolean: rec = False
    Dim wq As WorkbookQuery
    For Each wq In ThisWorkbook.Queries
          If InStr(1, wq.Name, qname) > 0 Then
            rec = True
          End If
    Next wq
    IsQuerrExist = rec
End Function
```