Office365やExcel2019で新しく搭載された「スピル」と言う機能があります。
スピル機能を使うと複数の答えを返す関数を従来よりも圧倒的に簡単にセルに展開することが出来ます。
この機能と相性が良いのでは思っていた、ExcelのデータをSQL文で集計した結果をセルに表示する関数を作ってみた。
※スピル機能自体はこちらをご参考ください。
スピルについて|エクセル入門
関数の動作
完成した関数を入力してみるとこのようになる。
C1セルに関数を入力すると関数を入力したセルだけではなく、周りのセルにも答えが入力される。
関数を確定した際に青線で囲われている部分がスピル機能で答えが入力された範囲。
今回入力した関数は以下の通り。
=SQLSearch("select キャリア,sum(年齢) as 合計 from [sheet1$] group by キャリア order by sum(年齢) desc")
SQLSearchが今回作成した関数の名前で、カッコの中が引数になる。
引数は全てデータベースを操作するための言語、SQLの文章。
SQLの詳細な内容は割愛させていただくがこのSQL文をExcelの機能に置き換えて説明してみると、
・ピボットテーブルでキャリアを行項目にして、年齢の合計を求める。
・SUMIFS関数でキャリアごとの年齢の合計を求める。
と言う意味になる。
※ちなみに元データみんな大好き「なんちゃって個人情報」を利用させて頂きました。
SQLSearch関数のコード
Option Explicit Public Function SQLSearch(SQL文 As String) As Variant '###Microsoft ActiveX Data Objects XX Libraryの参照設定を行う '###Excel2007以降に接続出来るように設定。設定を変更すれば別のデータベースに接続も可能。 Application.Volatile True Dim myCON As ADODB.Connection Set myCON = New ADODB.Connection myCON.Provider = "Microsoft.ACE.OLEDB.12.0" myCON.Properties("Extended Properties") = "Excel 12.0;IMEX=1" Dim DBFullPath As String DBFullPath = ActiveWorkbook.FullName myCON.Open DBFullPath Dim myRS As ADODB.Recordset Set myRS = New ADODB.Recordset myRS.Open SQL文, myCON, adOpenStatic '###RecordSetの行列並び替え→配列化 Dim tmp As Variant If myRS.RecordCount = 0 Then tmp = "解無し" GoTo Ans End If Dim vvレコードセット件数 As Long Dim vvレコードセット項目数 As Long vvレコードセット件数 = myRS.RecordCount vvレコードセット項目数 = myRS.Fields.Count 'Recordsetから配列に置き換え Dim vv行列並替前配列 As Variant ReDim vv行列並替前配列(vvレコードセット項目数 - 1, vvレコードセット件数 - 1) vv行列並替前配列 = myRS.GetRows '配列内での行・列置き換え Dim vv行列並替後配列 As Variant Dim i As Long Dim c As Long '行列並替前より配列件数を1つ増やすことでヘッダー情報を受けられるようにしている ReDim vv行列並替後配列(vvレコードセット件数, vvレコードセット項目数 - 1) For i = 0 To myRS.Fields.Count - 1 'Recordsetのヘッダー情報を配列に代入 vv行列並替後配列(0, i) = myRS.Fields(i).Name Next i For i = 0 To UBound(vv行列並替前配列) For c = 0 To UBound(vv行列並替前配列, 2) vv行列並替後配列(c + 1, i) = vv行列並替前配列(i, c) Next c Next i tmp = vv行列並替後配列 Ans: SQLSearch = tmp End Function
From句の指定方法
この関数はExcel2007以降のバージョンのデータ集計を想定している。
SQLのFrom句にシート名や名前定義範囲を指定するときは以下のようにすれば良い。
シート名の場合
"select * from [sheet1$]"
シート名を[]で囲って、シート名の後ろに$をつける。
シートの特定のセル範囲の場合
"select * from [sheet1$A1:B10]"
シート名の指定の後に相対参照でセル範囲を記入。
絶対参照・複合参照($付)ではエラーになる。
名前定義の場合
"select * from 名前定義"
名前定義の場合はそのまま名前定義を書けばOK。
Excel2007以降ではもう1つセル範囲を表現する方法がある。
テーブル機能である。
テーブル名を名前定義範囲と同じ様に指定してもエラーになる。
その対応として別の関数を1つ作ることにしました。
※VBAのコード内で記載する場合はこちらの記事を参照ください。
テーブル用関数
上記で紹介したVBAコード内で利用する方法の関数版です。
Public Function Table範囲アドレス取得(テーブル名 As String) As String Dim tmp As Variant tmp = Range(テーブル名).ListObject.Range.Address(False, False) Table範囲アドレス取得 = tmp End Function
この関数を最初に紹介した関数のFrom句に使用することでテーブルをFrom句に指定できるようになります。
"select * from [" & Table範囲アドレス取得("テーブル名") & "]"
まとめ
取り敢えず、思ったようにスピルで動作するSQL検索関数を作成することができました。
実際に使ってみたところ、ブラッシュアップしたくなるような要素が既に何点か発生していますので、それについてはまた後日。