
同じ Excel ブックに 3 つのテーブルがあり、すべて同じ列ヘッダーを使用しています。
4番目のテーブルに列を追加したいのですが動的に3 つのテーブル全体で同じ列にあるすべての一意の値で更新します (例: Table01[MyCol]、Table02[MyCol]、Table03[MyCol])。
VBA ソリューションは問題ありませんが、どこから始めればよいのかわかりません。
答え1
VBA ソリューションは問題ありませんが、どこから始めればよいのかわかりません。
何か欲しいものがあれば「動的に更新できる」おそらく、「イベント」に基づいたソリューションを探しているのでしょう。他のソースからテーブルへの更新や、ワークシートへの変更によってトリガーされるイベントがあります。
テーブルがすべて同じワークシート上にある場合は、ワークシート イベント トリガーで済みますが、異なるワークシート上にある場合は、ワークブック イベント トリガーが必要です。
毎回この言葉を聞くたびに「明確な価値観」そして「VBA」同じ段落で、私はすぐにVBAスクリプト辞書について考え始めます。辞書のキー一意です(オプションで大文字と小文字を区別するかどうかを選択できます)。
問題のコラムは「3つのテーブルすべてで同じ列」、名前を決定するには、パブリックまたはプライベートの定数変数を使用することをお勧めします。列の名前を変更する場合は、1 か所を変更するだけで済みます。
VBA で構造化されたテーブルを扱うのは面倒な作業です。方法によって長所と短所が異なります。短所が最も少ない方法は、Range("Table01").ListObject
ListObject テーブルを使用することです。これは、単一のワークシートのテーブルだけでなく、ワークブックのテーブルを扱う場合に特に当てはまります。
各テーブルの列をセルごとにループしないでください。各テーブルの列をバリアント配列に読み込み、その配列をループします。これにより、処理が高速化され、計算量が少なくなります。
これは、2つのワークシートの3つのテーブルから3番目のワークシートの4番目のテーブルに一意の値のリストを取得するコードです。このワークブックコードシート。あなたの利益のために多くのコメントが付けられています。
Option Explicit
'declare the common column's name available to all sub procedures within this code sheet
Private Const col As String = "col2"
Private Sub Workbook_SheetChange(ByVal Sh As Object, ByVal Target As Range)
'the Intersect function sees if you have changed anything within the tables
'you cannot Intersect across worksheets so you need to know what worksheet contains which table
'in this case, Table01 and Table02 are on Sheet1, Table03 is on Sheet2
'note the use of the LCase function
Select Case LCase(Sh.Name)
Case "sheet1"
'did the change event (add/update/remove) occur on Sheet1.Table01 or Sheet1.Table02
If Not Intersect(Target, Range("Table01").ListObject.ListColumns(col).DataBodyRange, _
Range("Table02").ListObject.ListColumns(col).DataBodyRange) Is Nothing Then
'set error control
On Error GoTo byebye
'disable events so this doesn't run on top of itself when Table04 is updated
Application.EnableEvents = False
'run the Table04 update procedure
UpDate_Table04
End If
Case "sheet2"
'did the change event (add/update/remove) occur on Sheet2.Table03
If Not Intersect(Target, Range("Table03").ListObject.ListColumns(col).DataBodyRange) Is Nothing Then
'see above
On Error GoTo byebye
'see above
Application.EnableEvents = False
'see above
UpDate_Table04
End If
Case Else
'do nothing (placeholder for other considerations)
End Select
'error control 'catcher'
byebye:
'reenable event handling for future operations
Application.EnableEvents = True
End Sub
Private Sub UpDate_Table04()
'declare variables
Dim t As Long, v As Long, tbls As Variant, vals As Variant
'declare a reusable variable for the dictionary
Static d As Object
'assign the source table names to a variant array
tbls = Array("Table01", "Table02", "Table03")
'determine if the dictionary has been created
'if so remove all previous entries; if not create one and make it non-case-sensitive
If d Is Nothing Then
'dictionary does not exist; create one and make it non-case-sensitive
Set d = CreateObject("scripting.dictionary")
d.CompareMode = vbTextCompare
Else
'dictionary exists; remove all previous entries
d.RemoveAll
End If
'loop through the three source tables
For t = LBound(tbls) To UBound(tbls)
'retrieve the values from this table's common column
vals = Application.Transpose(Range(tbls(t)).ListObject.ListColumns(col).DataBodyRange.Value2)
'loop through the values and create create unique dictionary keys using the faster Add/Overwrite method
'this method does not require checking for identical previous additions
For v = LBound(vals) To UBound(vals)
'Add/Overwrite method
d(vals(v)) = vbNullString
Next v
Next t
'work with the destination table
'this reference method does not require worksheet reference within ThisWorkbook
With Range("Table04").ListObject
'make room/remove rows on the destination table
.Resize .HeaderRowRange.Cells(1).Resize(d.Count + 1, .ListColumns.Count)
'put the dictionary keys into the destination table
.ListColumns(col).DataBodyRange = Application.Transpose(d.keys)
End With
End Sub
すでに調査済みで答えが見つからない質問には喜んでお答えします。
答え2
VBA コードではなく、別の列に一意のリストを作成する配列 (CSE) 数式をお勧めします。
使い方:
- 3 つのテーブルを作成し、
NameTBL
、NameTBL1
& という名前を付けますNameTBL2
。 セル内の数式
J36
:{=IFERROR(IFERROR (IFERROR(INDEX(NameTBL[City1],MATCH(0, COUNTIF($J$35:J35, NameTBL[City1])+(NameTBL[City1]=""), 0)), INDEX(NameTBL1[City2], MATCH(0, COUNTIF($J$35:J35, NameTBL1[City2])+(NameTBL1[City2]=""), 0))), INDEX(NameTBL2[City3], MATCH(0, COUNTIF($J$35:J35, NameTBL2[City3])+(NameTBL2[City3]=""), 0))), "")}
フォーミュラを仕上げるCtrl+Shift+Enter&それを埋めます(数行追加されるまで)。
- 表1に新しいデータが追加されました
- 表3に新しいデータが追加されました。
注意
Excel は新しいデータを取得するとすぐに関連テーブルを自動的に更新するため、数式には
Column J
動的に新しい値が含まれます。column J
一意の値をテーブルに変換できます。
答え3
私が見つけた良い出発点はhttps://www.ablebits.com/office-addins-blog/2016/04/21/get-list-unique-values-excel/これには、大文字と小文字を区別したり、数字と空白を無視したりするバリエーションが含まれます。
VBA や配列/CSE 数式ではなく、この通常の数式を使用することをお勧めします。この数式には、すぐ上のセルへの相対参照が含まれていることに注意してください。たとえば、セル D5 の数式は、以下に示すように、D1 ではなく、TableHeader から D4 までの範囲を参照します。
=IFERROR( INDEX( Table1[MyCol], MATCH(0, INDEX( COUNTIF(TableDistinct[[#Headers],[DistinctVals]]:D1,Table1[MyCol]),0,0),0)),
IFERROR( INDEX( Table2[MyCol], MATCH(0, INDEX( COUNTIF(TableDistinct[[#Headers],[DistinctVals]]:D1,Table2[MyCol]),0,0),0)),
IFERROR( INDEX( Table3[MyCol], MATCH(0,INDEX(COUNTIF(TableDistinct[[#Headers],[DistinctVals]]:D1,Table3[MyCol]),0,0),0)),
"")
)
)
この数式は、まず、COUNTIF
ソース値のリスト {A、B、B、C} を、個別のリストにすでに存在する値に基づいて 1 と 0 のリストに変換します。「A」と「B」の値が個別のリストにすでに存在するが、「C」の値は存在しない場合、この例では、{A、B、B、C} が {1、1、1、0} に変換されます。
次に、MATCH
バイナリ配列を使用して最初の「0」値を検索します。これは、個別のリストにまだ含まれていない値を持つソース リスト セルの位置です。つまり、上記の「C」値の位置は 4 です。
3 番目に、 を使用して、INDEX
によって見つかった位置に関連付けられた値を取得しますMATCH
。この例では、「C」を返します。
最後に、一致するものが見つからなくなったときに (空白のセル値)IFERROR
を返すために使用します。"""
答え4
お使いの Excel のバージョンに動的配列機能がある場合は、UNIQUE
関数を使用して以下を使用できます。
=UNIQUE(FILTERXML("<t><s>" & SUBSTITUTE(TEXTJOIN(",",TRUE,Table01[MyCol],Table02[MyCol],Table03[MyCol]),",","</s><s>")& "</s></t>","//s"))
アルゴリズム
関数を使用してすべてのテキストを結合し、
TEXTJOIN
コンマ区切りのリストを作成します。(文字列にカンマが含まれている場合は、他の区切り文字を使用してください)TEXTJOIN(",",TRUE,Table01[MyCol],Table02[MyCol],Table03[MyCol])`
- 各ノードがカンマ区切りの項目の1つであるXMLを作成します。
FILTERXML
ノードを配列として返すために使用します。- 関数を使用して
UNIQUE
、一意のエントリのみを返します。