質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.35%
VBA

VBAはオブジェクト指向プログラミング言語のひとつで、マクロを作成によりExcelなどのOffice業務を自動化することができます。

マクロ

定義された処理手続きに応じて、どのような一連の処理を行うのかを特定させるルールをマクロと呼びます。

配列

配列は、各データの要素(値または変数)が連続的に並べられたデータ構造です。各配列は添え字(INDEX)で識別されています。

Q&A

4回答

4596閲覧

Excel VBA で3万行と3万行を付きあわせる処理を短時間でやりたい。配列?

access

総合スコア9

VBA

VBAはオブジェクト指向プログラミング言語のひとつで、マクロを作成によりExcelなどのOffice業務を自動化することができます。

マクロ

定義された処理手続きに応じて、どのような一連の処理を行うのかを特定させるルールをマクロと呼びます。

配列

配列は、各データの要素(値または変数)が連続的に並べられたデータ構造です。各配列は添え字(INDEX)で識別されています。

0グッド

2クリップ

投稿2021/04/25 03:56

Excel VBA で3万行と3万行を付きあわせる処理を短時間でやりたい。

sheet1のA列に3万行の会員番号があります。
sheet2のA列にも同じく3万行の会員番号があります。

[やりたいこと]
sheet1のA1から順番にsheet2のA1:A30000までを突き合わせる処理をしたいのです。
つまり、30000回*30000回の突き合わせが発生します。
こういう処理は配列でやると一瞬だ、という話を聞いて初心者なりにコードを書いてみたのですが
なぜか途方もない時間がかかります。
ちなみに、"突き合わせて何をするか"は様々なパターンがありまして、とりあえず突き合わせの部分を高速にしないことには実務で使えない状況です。

[試したこと]
'変数Range1 にはsheet1のA1:A30000がRange型として入ってます。
'変数Range2 にはsheet2のA1:A30000がRange型として入ってます。

'データレンジ取得し配列へ代入
Dim arr1 As Variant '
Dim arr2 As Variant '仮納品データレンジ用配列
Set arr1 = Range1
Set arr2 = Range2
'カウント
Dim rep() As Integer 'カウント結果代入用の配列変数を用意
ReDim rep(arr1.Count)
'カウント
Dim tmp As Variant
Dim i As Long
Dim j As Long
Dim cnt As Integer 'カウント
For i = 1 To arr1.Count
For j = 1 To arr2.Count
If arr1(i) = arr2(j) Then
cnt = cnt + 1
End If
Next j
rep(i) = cnt
cnt = 0
Next i
'カウント結果をセルへ貼付
Range1.Offset(0, 1) = rep

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答4

0

某有名なエクセルの先生が、「エクセルはワークシートがあるのでいちいち小難しい連想配列なんて使わないよ。」とおっしゃっていましたが、違うようですね。

どの先生かは知りませんが、たぶん、ワークシート関数を使えば簡単かつ高速にできることがある、という意味でいっているだと推測。

今回の案件なら、
Sheet1 の B1 に下記の式を設定して

=COUNTIF(Sheet2!$A$1:$A$30000,Sheet1!A1)

これをフィルダウンすれば結果が表示されます。

これをVBA化すると下記のようになります。

vba

1Sub Sumple() 2 With Sheet1.Range("A1:A30000").Offset(0, 1) 3 .Formula = "=COUNTIF(Sheet2!$A$1:$A$30000,Sheet1!A1)" 4 .Value = .Value '式を値に変換 5 End With 6End Sub

速度はどの程度になるかは分かりませんが、セルを一つずつ参照して計算するよりはるかに高速なはずです。

投稿2021/05/06 08:09

hatena19

総合スコア34075

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

標準機能を使用して行う場合、Power Queryの利用が考えられます。
Power Queryで複数シートの結合

面倒だなと感じるようであれば、シートをDBのテーブルとして扱い、SQL操作を可能にする方法で行ってみて下さい。
Excelの集計をSQLでおこなう方法。VLOOKUP()やSUBTOTAL()の限界を超えろ!!

応用を考えた時は後者が断然お薦めです。

投稿2021/04/25 04:42

sazi

総合スコア25327

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

Rangeを配列に移しているつもりで、移した先もRange型になってしまっています。

VBA

1Sub test() 2 '変数Range1 にはsheet1のA1:A30000がRange型として入ってます。 3 '変数Range2 にはsheet2のA1:A30000がRange型として入ってます。 4 Dim Range1 As Range, Range2 As Range 5 Set Range1 = Sheet1.Range("A1:A30000") 6 Set Range2 = Sheet2.Range("A1:A30000") 7 8 'データレンジ取得し配列へ代入 9 Dim arr1 As Variant ' 10 Dim arr2 As Variant '仮納品データレンジ用配列 11 arr1 = Range1 12 arr2 = Range2 13 14 'カウント 15 Dim rep() As Integer 'カウント結果代入用の配列変数を用意 16 ReDim rep(1 To Range1.Count, 1 To 1) 17 18 19 'カウント 20 Dim tmp As Variant 21 Dim i As Long 22 Dim j As Long 23 Dim cnt As Integer 'カウント 24 For i = 1 To Ubound(arr1, 1) 25 For j = 1 To Ubound(arr2, 1) 26 If arr1(i, 1) = arr2(j, 1) Then 27 cnt = cnt + 1 28 End If 29 DoEvents 30 Next j 31 rep(i) = cnt 32 cnt = 0 33 Next i 34 'カウント結果をセルへ貼付 35 Range1.Offset(0, 1) = rep 36 37End Sub 38

<追記>
上記のように配列に代入しても、大量データ同志の二重ループだとどうしても遅くなります。
また、やりたいことによっては、必ずしも配列にするのが正解とも限らないと思います。
たとえば、以下はワークシート関数を利用してみたものです。

Sub test2() Dim Range1 As Range, Range2 As Range Set Range1 = Sheet1.Range("A1:A30000") Set Range2 = Sheet2.Range("A1:A30000") Dim rep() ReDim rep(1 To Range1.Count, 1 To 2) Dim i As Long For i = 1 To Range1.Count rep(i, 1) = WorksheetFunction.CountIf(Range2, Range1(i, 1)) 'Sheet2でのカウント結果 If rep(i, 1) > 0 Then rep(i, 2) = WorksheetFunction.Match(Range1(i, 1), Range2, False) 'Sheet2の(最初にマッチした)行番号 End If Next i Range1.Offset(0, 1).Resize(, 2) = rep End Sub

また、こちらは連想配列を使った例です。

VBA

1Sub test3() 2 '変数Range1 にはsheet1のA1:A30000がRange型として入ってます。 3 '変数Range2 にはsheet2のA1:A30000がRange型として入ってます。 4 Dim Range1 As Range, Range2 As Range 5 Set Range1 = Sheet1.Range("A1:A30000") 6 Set Range2 = Sheet2.Range("A1:A30000") 7 8 'データレンジ取得し配列へ代入 9 Dim arr1 As Variant ' 10 Dim arr2 As Variant '仮納品データレンジ用配列 11 arr1 = Range1 12 arr2 = Range2 13 14 'カウント 15 Dim rep() As Integer 'カウント結果代入用の配列変数を用意 16 ReDim rep(1 To Range1.Count, 1 To 1) 17 18 19 'カウント 20 Dim tmp As Variant 21 Dim i As Long 22 Dim j As Long 23 Dim d 24 Set d = CreateObject("Scripting.Dictionary") 25 For j = 1 To UBound(arr2, 1) 26 d(arr2(j, 1)) = d(arr2(j, 1)) + 1 27 Next 28 29 For i = 1 To UBound(arr1, 1) 30 If d.Exists(arr1(i, 1)) Then 31 rep(i, 1) = d(arr1(i, 1)) 32 End If 33 Next i 34 'カウント結果をセルへ貼付 35 Range1.Offset(0, 1) = rep 36 37End Sub

投稿2021/04/25 04:32

編集2021/04/25 05:36
jinoji

総合スコア4592

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

access

2021/04/25 04:38

セル範囲をVariant型変数へ代入すると配列になるとネットで調べたのですが違いましたか・・・。 arr1とarr2の宣言を以下のように書き換えて試してみます。ありがとうございます。 Dim arr1() as variant redim arr1(Range1.count)
jinoji

2021/04/25 04:41

セル範囲をVariant型変数へ代入すると配列になる、はその通りですが、 Set arr1 = Range1 とやると、arr1もRange型になってしまいます。 arr1 = Range1 (丁寧にやると arr1 = Range1.Value ですが)とやると、意図したものになります。
access

2021/04/25 04:57

そういうことだったのですが、添削して頂いていたコードを確認いたしました。 セル1列を配列に入れても二次元配列になるのですね。とにかくやってみます!有難うございます!
jinoji

2021/04/25 05:02

まあ、それでも今のコードのまま30000×30000だとかなり遅いと思います。 TanakaHiroakiさんがいうように連想配列にするのがよいと私も思います。
access

2021/04/25 05:15

ご回答いただいた方法でとりあえずできました! ただ、速さは1割短縮といったところでした。連想配列について勉強してみます。 ありがとうございます。
access

2021/04/25 07:19

連想配列のコード有難うございます。 1分40秒かかっていた処理がなんと1秒かからず終わりました。 今の業務でこういった件数の突き合わせ処理が頻繁に発生しますので必ず習得しようと思います。 某有名なエクセルの先生が、「エクセルはワークシートがあるのでいちいち小難しい連想配列なんて使わないよ。」とおっしゃっていましたが、違うようですね。
guest

0

更に高速化するために連想配列の使い方を覚えると良いです。
Dictionary オブジェクト(連想配列) とは ビッグデータ高速処理を可能にする技 (Excel VBA)

投稿2021/04/25 04:04

TanakaHiroaki

総合スコア1063

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.35%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問