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

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

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

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

Q&A

解決済

3回答

4877閲覧

データ量の多い重複値に連番付与するやり方について(処理時間がかなり要しており苦戦しています)

quark87139

総合スコア6

VBA

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

0グッド

1クリップ

投稿2021/11/06 03:49

どなたか有識者の方ご教示頂けないでしょうか。

現在、重複レコードに連番(通し番号)を付与といった処理を行うために作成しています。

コード自体は問題はなく動作できているのですが、
データが20万を超えて尚且つ重複リストが1万以上超過していると
中々処理時間に要し、おおよそ10分ぐらい時間掛かっている状況です。

▼処理の流れ
①リストを配列へ格納
②①で格納した配列から重複リストを連想配列へ格納
③重複リストの分だけループ処理
④重複リストと一致判別し、レコードと連番を結合分を配列へ格納
⑤配列データをセル上へ反映

▼処理前後のイメージ
処理前   処理後
A   |  A1
B   |  B1
A   |  A2
D   |  D1
A   |  A3
※上記ではアルファベットで表記していますが、
実際はコード+日付+ステータスの結合した文字列となります。

▼処理に時間掛かっている要因
恐らく重複リストの要素が1万以上あることが一番の要因で
③~⑤の処理に負担が掛かっているのではと推測しています。

このような処理を行う際は重複となる要素や条件を付け加えて重複リストの数を減らすことが
必然となってくるのでしょうか....。

現在のコードを記載しますので、
どなたか他に処を早める最善のやり方や他の処理方法などあればご教示頂けると幸いです。

VBA

1Sub 連番付与() 2 3Dim dcdname As Variant, dccount As Variant 4Dim i, j 5Dim Cnt As Long 6Dim mRow As Long 7Dim dname As String 8Dim myKey As Variant 9Dim list As Variant 10 11 Dim ws1 As Worksheet 12 Set ws1 = ThisWorkbook.Worksheets(3) 13 14'▼画面更新を無効化 15Application.ScreenUpdating = False 16 17With ws1 18 19 '連想配列 20 Set dcdname = CreateObject("Scripting.Dictionary") 21 Set dccount = CreateObject("Scripting.Dictionary") 22 23 '最終行取得 24 mRow = .Cells(Rows.Count, 1).End(xlUp).Row 25 26 'O列を配列格納 27 list = .Range("O1:O" & mRow) 28 29 'ループ 30 For i = 2 To mRow 31 32 '値を変数へ 33 dname = list(i, 1) 34 35 '重複しないリストを連想配列へ格納 36 If Not dcdname.Exists(dname) Then 37 dcdname.Add dname, dname 38 Else 39 '重複しているリストを連想配列へ格納 40 If Not dccount.Exists(dname) Then 41 dccount.Add dname, dname 42 End If 43 End If 44 Next i 45 46 myKey = dccount.Keys 47 Cnt = 1 48 49 '重複しているリスト分ループして連番 50 For i = 0 To UBound(dccount.Items) 51 52 For j = 1 To UBound(list) 53 54 If list(j, 1) = myKey(i) Then 55 56 'レコードと連番の結合分を配列へ格納 57 list(j, 1) = list(j, 1) & Cnt 58 Cnt = Cnt + 1 59 60 End If 61 Next j 62 63 Cnt = 1 64 65 Next i 66 67 '配列をO列に反映 68 .Range("O1:O" & mRow) = list 69 70 '配列解放 71 Set dcname = Nothing 72 Set dccount = Nothing 73 74End With 75 76'▼画面更新を有効化 77Application.ScreenUpdating = True 78 79End Sub 80

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

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

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

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

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

guest

回答3

0

Dictionaryの Item に連番を格納するようにすれば、Dictionaryは一つですみますし、ループも1回ですみます。ループが1回で済む分、速度アップすると思います。

vba

1Sub 連番付与Sample() 2 3 Dim dcdname As Object 4 Dim i 5 Dim mRow As Long 6 Dim dname As String 7 Dim list As Variant 8 9 Dim ws1 As Worksheet 10 Set ws1 = ThisWorkbook.Worksheets(1) 11 12 '▼画面更新を無効化 13 Application.ScreenUpdating = False 14 15 With ws1 16 17 '連想配列 18 Set dcdname = CreateObject("Scripting.Dictionary") 19 20 '最終行取得 21 mRow = .Cells(Rows.Count, 1).End(xlUp).Row 22 23 'A列を配列格納 24 list = .Range("O1:O" & mRow).Value 25 26 'ループ 27 For i = 1 To mRow 28 29 '値を変数へ 30 dname = list(i, 1) 31 32 If Not dcdname.Exists(dname) Then 33 '初めてのリストは連番1 34 dcdname.Add dname, 1 35 list(i, 1) = dname & 1 36 Else 37 '既出のリストは 連番 + 1 38 dcdname(dname) = dcdname(dname) + 1 39 list(i, 1) = dname & dcdname(dname) 40 End If 41 Next i 42 43 44 '配列をO列に反映 45 .Range("O1:O" & mRow) = list 46 47 '配列解放 48 Set dcdname = Nothing 49 50 End With 51 52 '▼画面更新を有効化 53 Application.ScreenUpdating = True 54 55End Sub

投稿2021/11/06 05:03

編集2021/11/06 05:07
hatena19

総合スコア34075

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

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

quark87139

2021/11/06 05:29

ご回答ありがとうございます。 また分かりやすいように僕のコードから修正頂きありがとうございます。 ポイントとしては 処理時間の遅延要因 ・ループは1回で済ませるようにする ↓ ・ループを1回で済ませるために連番をItemへ格納 ※Itemへ格納することでDictionaryも1つで回せる ということですよね。 他の方のコードでもそうなんですが、 おっしゃるように極力ループは1回で済ませるように逆算して組み立てる力も大事ということを改めて痛感しました。 ありがとうございました。
guest

0

セルにアクセスするのではなく、配列化して操作するというのが、定番です。
後は書き込みなどで、関係する自動更新をOFFにしておくなど。

以下参考
シートへの高速なアクセス
第114回.セル範囲⇔配列(マクロVBA高速化必須テクニック)

Excel VBA 高速化アプローチ

投稿2021/11/06 04:46

sazi

総合スコア25327

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

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

0

ベストアンサー

これでどうでしょうか。

VBA

1With ws1 2 Set dcdname = CreateObject("Scripting.Dictionary") 3 mRow = .Cells(Rows.Count, 1).End(xlUp).Row 4 list = .Range("O1:O" & mRow) 5 For i = 2 To mRow 6 dname = list(i, 1) 7 dcdname(dname) = dcdname(dname) + 1 8 list(i, 1) = list(i, 1) & dcdname(dname) 9 Next i 10 .Range("O1:O" & mRow) = list 11 Set dcdname = Nothing 12End With 13

投稿2021/11/06 04:39

jinoji

総合スコア4592

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

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

quark87139

2021/11/06 05:23

早速のご回答ありがとうございます。 頂いたコードで処理時間も1秒もかからず僕が考えていたものと同様の結果となりました。 (コードもコンパクトで驚愕しています...) 改めて僕のコードと比較するとあまりにも無駄な処理が付け加えているのが分かる上に 処理速度が遅くても当然な結果ですね..。 Dictionaryは1個で回し、ループは1回で済ますというのがポイントといったところでしょうか。 これで終わりとせずに頂いたコードから自分なりに解釈して他のところで活用できないか模索していきます。ありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.35%

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

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

質問する

関連した質問