0

この質問が最初から意味があるかどうかはわかりません...

例 与えられた例: 次の値が1 つのセルに与えられます (これを と呼びますA1): Sub-value #1|Here's another sub-value #2|Yet again, last but not least, sub-value #3. ここでデータベースを使用する必要があると誰かが言うことはすでに知っています (信じてください。私の専攻は DB 管理です。わかっていますが、この方法でデータが必要です)。私の区切り文字は|. LEN()ここで、各サブ値を取り、すべてのサブ値を返す関数を作成したいとしAVERAGE()ます。これを行う単一の関数を作成したい場合は、 を使用しsplit()て各値を取得し、 を実行してLEN()を返すことができますAVERAGE()

与えられた例では、 cell を利用しましょうB1。過去に、次の方法で機能する同様の関数を作成しましたが (これとまったく同じではありません)、毎回配列/セル値を分割して結合する必要があります: =ARRAY_AVERAGE(ARRAY_LEN(A1,"|","|"),"|","|").

  • ARRAY_LEN(cell,delimiter[,Optional new_delimiter])
  • ARRAY_AVERAGE(cell,delimiter[,Optional new_delimiter])

ただし、これに対するより動的なアプローチがあるのではないかと考えています。基本的に、split()いくつかのカスタム VBA 関数を含む配列を作成し、それを親セル関数に渡し、配列をマージして元に戻す関数で配列をラップします。

セル関数の実行方法は次のとおり =ARRAY_AVERAGE(ARRAY_LEN(ARRAY_SPLIT(A1,"|")))です。

  • ARRAY_SPLIT(cell,delimiter)配列を分割します。
  • ARRAY_LEN(array)配列の各サブ値の長さを返します。
  • ARRAY_AVERAGE(array)配列の各サブ値の平均を返します。この関数は複数の値の単一の値を返すため、これはARRAY_JOIN(array,delimiter)配列を再度マージする虚数の形式になります。

これには、セルに 1 つまたは 2 つの関数を追加する必要がありますが、セルが単一のセル値と VBA 配列との間で変換する反復回数も減少します。

どう思いますか?可能?実現可能?コード効率は多かれ少なかれ?

4

2 に答える 2

2

これは非常に大雑把な例ですが、開始方法と、このメソッドをニーズに合わせてカスタマイズする方法についてのアイデアが得られるはずです。example.txtというテキスト ファイルに次のデータがあるとします。

Name|Age|DoB|Data1|Data2|Data3
David|25|1987-04-08|100|200|300
John|42|1960-06-21|400|500|600
Sarah|15|1997-02-01|700|800|900

このファイルはC:\Downloadsフォルダーにあります。ADO を使用して VBA でこれを照会するには、 Microsoft ActiveX Data Objects 2.X ライブラリを参照する必要があります。ここで、X はインストールした最新バージョンです。また、Microsoft Scripting Libraryを参照してSchema.iniを作成しています。実行時にファイルを変更して、データが正しく読み取られるようにします。Schema.ini ファイルがないと、データがドライバーによって期待どおりに読み取られないリスクがあります。テキストとしての数値は、理由もなく null として読み取られることがあり、日付もしばしば null が返されます。Schema.ini ファイルは、テキスト ドライバにデータの正確な定義とその処理方法を提供します。私が行ったようにすべての列を明示的に定義する必要はありませんが、少なくともFormat、ColNameHeader、および DateTimeFormat の値を設定する必要があります。

使用される Schema.ini ファイルの例:

[example.txt]
Format=Delimited(|)
ColNameHeader=True
DateTimeFormat=yyyy-mm-dd
Col1=Name Char
Col2=Age Integer
Col3=DoB Date
Col4=Data1 Integer
Col5=Data2 Integer
Col6=Data3 Integer

最初の行で、ファイル名が角かっこで囲まれていることがわかります。これはオプションではなく、ファイルごとに異なるスキーマを定義することもできます。前述のように、実行時に VBA で Schema.ini ファイルを次のように作成します。

Sub CreateSchema()
    Dim fso As New FileSystemObject
    Dim ts As TextStream

    Set ts = fso.CreateTextFile(FILE_DIR & "Schema.ini", True)

    ts.WriteLine "[example.txt]"
    ts.WriteLine "Format=Delimited(|)"
    ts.WriteLine "ColNameHeader=True"
    ts.WriteLine "DateTimeFormat=yyyy-mm-dd"
    ts.WriteLine "Col1=Name Char"
    ts.WriteLine "Col2=Age Integer"
    ts.WriteLine "Col3=DoB Date"
    ts.WriteLine "Col4=Data1 Integer"
    ts.WriteLine "Col5=Data2 Integer"
    ts.WriteLine "Col6=Data3 Integer"

    Set fso = Nothing
    Set ts = Nothing
End Sub

モジュールの先頭で定義した定数である変数 FILE_DIR を使用していることに気付くでしょう。Schema.ini ファイルは、データ ファイルと同じ場所に存在する必要があります。クエリの接続文字列もこのディレクトリを使用するため、同じ場所を参照するように定数を定義します。これは、接続文字列と SQL クエリとともに FILE_DIR 定数を使用したモジュールの上部です。

Option Explicit

Const FILE_DIR = "C:\Downloads\"
Const TXT_CONN = "Driver={Microsoft Text Driver (*.txt; *.csv)};Dbq=" & FILE_DIR & ";Extensions=asc,csv,tab,txt;"
Const SQL = "SELECT Name, DoB, ((Data1 + Data2 + Data3)/3) AS [Avg_of_Data]" & _
            "FROM example.txt "

Dbqと呼ばれるTXT_CONNの部分に注意してください。これは、データ ファイルが保存されるディレクトリです。SQL 文字列の WHERE 句で使用する特定のファイルを実際に定義します。SQL定数には、クエリ文字列が含まれています。この場合、Name、DoB、および 3 つのデータ値の平均を選択しているだけです。以上で、実際にクエリを実行する準備が整いました。

Sub QueryText()
    Dim cn As New ADODB.Connection
    Dim rs As New ADODB.Recordset
    Dim i As Integer

    'Define/open connection
    With cn
        .ConnectionString = TXT_CONN
        .Open

        'Query text file
        With rs
            .Open SQL, cn
            .MoveFirst

            'Loop through/print column names to Immediate Window
            For i = 0 To .Fields.Count - 1
                Debug.Print .Fields(i).Name
            Next i

            'Loop through recordset
            While Not (.EOF Or .BOF)

                'Loop through/print each column value to Immediate Window
                For i = 0 To .Fields.Count - 1
                    Debug.Print .Fields(i)
                Next i

                .MoveNext
            Wend

            .Close 'Close recordset
        End With

        .Close 'Close connection to file
    End With

    Set rs = Nothing
    Set cn = Nothing
End Sub

上記のコメントで、これを行うのは非常に簡単であり、これは大変な作業のように見えますが、そうではないことを保証します. QueryText() メソッドのみを使用して、同様の結果を得ることができます。ただし、他のすべてを含めて、プロジェクトでこれを使用できる場所のアイデアを提供し、期待した結果が得られない場合に発生する可能性のある問題を解決する方法を示します.

これは、私が最初に学んだガイドです: http://msdn.microsoft.com/en-us/library/ms974559.aspx

実際の Excel ファイルに対して同じことを行うためのガイドを次に示します: http://support.microsoft.com/kb/257819

最後に、Schema.ini ファイルの詳細については、http://msdn.microsoft.com/en-us/library/windows/desktop/ms709353(v=vs.85).aspx を ご覧ください。

うまくいけば、このすべての情報をあなたの仕事で利用する方法を見つけることができます! これらすべてを学習する副次的な利点は、ADO を使用して、Access、SQL Server、および Oracle などの実際のデータベースに対してクエリを実行できることです。コードは、ここに印刷されているものとほぼ同じです。接続文字列、SQL 文字列を交換するだけで、Schema.ini ファイルに関するすべてのビットを無視できます。

于 2012-09-19T23:08:18.133 に答える
1

1 つのセルで機能する VBA UDF の例を 2 つ示します。式を次のように入力します。

=AVERAGE(len_text(SPLIT_TEXT(A1,"|")))

この特定のケースでは、実際には len_text 関数は必要ないことに注意してください。代わりに Excel の LEN() を使用できますが、配列数式として AVERAGE(..) を入力する必要があります。

Option Explicit

Public Function Split_Text(theText As Variant, Delimiter As Variant) As Variant
    Dim var As Variant
    var = Split(theText, Delimiter)
    Split_Text = Application.WorksheetFunction.Transpose(var)
End Function
Public Function Len_Text(something As Variant) As Variant
    Dim j As Long
    Dim k As Long
    Dim var() As Variant

    If IsObject(something) Then
        something = something.Value2
    End If

    ReDim var(LBound(something) To UBound(something), LBound(something, 2) To UBound(something, 2))
    For j = LBound(something) To UBound(something)
        For k = LBound(something, 2) To UBound(something, 2)
            var(j, k) = Len(something(j, k))
        Next k
    Next j
    Len_Text = var

End Function
于 2012-09-20T10:16:47.220 に答える