![](http://oshiete.xgoo.jp/images/v2/pc/qa/question_title.png?08b1c8b)
35万件以上あるエクセルデータに対して、マクロを使って以下のような処理で重複業を削除したいと思っています。
Sub DeleteOldRow()
Dim lastRow As Integer
Dim i As Integer
Dim j As Integer
Dim strVal As String
'B列の最終行を求めます。
lastRow = ActiveSheet.Cells(ActiveSheet.Rows.Count, 2).End(xlUp).Row
'1行目から最終行の前まで繰り返します。
For i = 1 To lastRow - 1
'チェックする値を、strValに代入します。
strVal = ActiveSheet.Cells(i, 2).Value
'今見てる行から、下をチェックします。
For j = i + 1 To lastRow
'もし、値が同じであれば、
If strVal = ActiveSheet.Cells(j, 2).Value Then
'元の行を削除します
ActiveSheet.Rows(i).Delete
'最終行が1行減ったのでlastRowの値を減らします。
lastRow = lastRow - 1
'チェックしている行を1行前に戻します。
j = j - 1
End If
Next j
Next i
End Sub
上記処理を35万件あるファイル上でマクロの実行すると、オーバーフローしてしまいました。マクロ側で対象ファイルを読み込むなどして、処理を軽くするやり方はありますでしょうか。上記処理にどのような処理を加えればスムーズに処理されるでしょうか。
No.3ベストアンサー
- 回答日時:
2007 にも「重複の削除」はありますよ。
VBA で記述してみると以下の様な感じです。
Public Sub Samp1()
Cells.RemoveDuplicates Array(2)
End Sub
上記では行全部消えますけど、他の列に影響しない指定もできるようです。
以下は B, C 列を対象にして、Array 部分にはその対象の何番目を指定する様です。
Public Sub Samp2()
Columns("B:C").RemoveDuplicates Array(1)
End Sub
ただ、質問者さんがやりたそうな事は、
・重複したら前の方を消す・・・
みたいで、上記「重複の削除」では、前の方を残すものの様です。
残したいものを前にするようなソートしてからなら、一番速そうです。
前の方を消す場合は、後ろの方から重複を確認して、重複するものだったら消す。
以下の様に考えます。
重複のチェックには Dictionary のキーを利用します。
無条件でキーを登録し、
重複した場合、前後の Count 値は変化しない事を利用し、
消す対象のセルを覚えておいて、
重複のチェックが終わったら、覚えていた重複していたものに対して一気に行削除
30万件で15秒くらい(環境によって大幅変動あると思います)
Public Sub Samp3()
Dim dic As Object
Dim rng As Range
Dim iRow As Long, i As Long
Application.ScreenUpdating = False
Set dic = CreateObject("Scripting.Dictionary")
iRow = Cells(Rows.Count, 2).End(xlUp).Row
Set rng = Cells(iRow + 1, 2)
While (iRow > 0)
i = dic.Count
dic(Cells(iRow, 2).Value) = Null
If (i = dic.Count) Then
Set rng = Union(rng, Cells(iRow, 2))
End If
iRow = iRow - 1
Wend
rng.EntireRow.Delete
Set rng = Nothing
Set dic = Nothing
Application.ScreenUpdating = True
End Sub
※ ループの中では Union だけ使いたかったので、
消えても良い最終行+1のセルをダミー登録してからループに突入
※ Union で覚えて一気に削除していましたが、覚える事はしないで
If (i = dic.Count) Then
Rows(iRow).Delete
End If
と、1行毎に削除しても結果は同じになりますが・・・
気長に処理が終わるのを待ってください・・・ それだけ遅くなります
【余談】
> 上記処理にどのような処理を加えればスムーズに処理されるでしょうか。
提示あったVBA記述を見て、危ない所を何箇所か・・・・
★ ループ条件を考えてみる
Public Sub test1()
Dim i As Long, j As Long
j = 3
For i = 1 To j
Debug.Print i, j
If (i = 1) Then j = j - 1
Next
End Sub
上記のような記述がみられますが、出力された i, j はどうなっていたでしょうか?
For 文が解釈された時点で、ループ回数は決まってしまうので、
途中でループ条件を変更しても無意味
途中でループ条件を変えて動きたいのであれば、While 等で毎回判別する様に
Public Sub test1k()
Dim i As Long, j As Long
j = 3
i = 1
While (i <= j)
Debug.Print i, j
If (i = 1) Then j = j - 1
i = i + 1
Wend
End Sub
★ 行を削除した時をキッチリとイメージする
1行目:A
2行目:B
3行目:A
4行目:A
だった時、i=1, j=3 で、1行目を削除するみたいですが
1行目:B
2行目:A
3行目:A
になって、また i=1, j=3 で、さらに1行目を削除しようとしていませんか?
少なくとも1行目を削除した時には、j の For 文は抜ける???
また、i = i - 1 も必要???
For 文で指定した変数(i とか j)は、いじらない方が良いと思います。
いじるのなら For 文はやめて、While 等に書き換えるとか・・・
> マクロ側で対象ファイルを読み込むなどして
元は CSV ファイルとかでしょうか?
これについては、状況がわからないと・・・
30246kiku 様
ご丁寧に解説いただきありがとうございます。
またお返事が遅くなり大変申し訳ございませんでした。
余談の部分についても大変勉強になります!
サンプルソースもしっかりと読ませて頂きます。
No.8
- 回答日時:
#6です
#7さんの結果を参考に Samp3 を書き換えました
Public Sub Samp5()
Dim dic As Object
Dim iRow As Long, i As Long
Dim v As Variant
Application.ScreenUpdating = False
Set dic = CreateObject("Scripting.Dictionary")
With Range("B1", Cells(Rows.Count, "B").End(xlUp))
v = .Value
For iRow = UBound(v) To 1 Step -1
i = dic.Count
dic(v(iRow, 1)) = Null
If (i = dic.Count) Then v(iRow, 1) = Empty
Next
.Value = v
On Error Resume Next
.SpecialCells(xlCellTypeBlanks).EntireRow.Delete
End With
Set dic = Nothing
Application.ScreenUpdating = True
End Sub
同じデータではありませんが、812.5秒 → 113秒まで短縮されました。
ちなみに、私の方のサンプルデータでは、16秒 → 2.7秒に。
No.7
- 回答日時:
回答じゃありません II。
30246kiku さん、わたしメの投稿にお付き合い頂きありがとうございます。
35万件あってもExcelのソートは速いので、ダミーデータ作成ももうチョット・・・
と思って検索、
田中氏の
http://officetanaka.net/Excel/vba/speed/index.htm
を参考にしまして
Sub SampMakeData2()
Dim i As Long, j As Long, str As String
Dim v As Variant
Const rCount As Long = 350000 '実使用行数の代わりに決め打ちしてます
Application.ScreenUpdating = False
ReDim v(1 To rCount, 1 To 2)
For i = 1 To rCount
For j = 1 To 5
str = str & Chr(Int(Rnd() * 26) + 65)
Next j
v(i, 1) = i
v(i, 2) = str
str = ""
Next i
Range("A1:B350000") = v
Application.ScreenUpdating = True
Beep
End Sub
だと処理速度が1/4くらいに短縮されました。
ここまで大きな配列を扱ったことが無かったのですが
問題なさそうです。
限界はいずこに有りや???
引退PCのWinXP & Excel2002 & メモリ 1GB でも
配列の確保・参照『まで』は問題なく出来ました。
質問者さんの反応がないけど・・・?
以上ご参考まで。
No.6
- 回答日時:
#5です
#4さんのサンプルデータのA列の値を使えば前後関係がわかるようなので、
そのような列があったら・・・という事でもう1つ処理を
Excelファイルの拡張子が xlsm とした場合のものになりますが・・・
Public Sub Samp4()
Dim cn As Object, rs As Object
Dim sSql As String, sS As String
Application.ScreenUpdating = False
sS = ActiveSheet.Name
Set cn = CreateObject("ADODB.Connection")
cn.Open "Provider=Microsoft.ACE.OLEDB.12.0;" _
& "Data Source=" & ThisWorkbook.FullName & ";" _
& "Extended Properties='Excel 12.0 Xml;HDR=No'"
sSql = "SELECT Q1.* FROM [{%1}$] AS Q1 INNER JOIN " _
& "(SELECT Max(F1) AS AA FROM [{%1}$] GROUP BY F2) AS Q2 " _
& "ON Q1.F1=Q2.AA;"
Set rs = cn.Execute(Replace(sSql, "{%1}", sS))
Cells.ClearContents
If (Not rs.EOF) Then Range("A1").CopyFromRecordset rs
rs.Close: Set rs = Nothing
cn.Close: Set cn = Nothing
Application.ScreenUpdating = True
End Sub
これでやると 64秒でしたね
ただ、使用している列数は A,B の2列だけなので、列数が多くなったら???
5文字ではなく2文字にしてもう一度測定してみると
Samp1:ソート 4秒 その後の実行 1.5秒
Samp1:(未ソート)1.4秒
Samp3:266.5秒
Samp4:17.5秒
以上 何かの参考になればと・・・
PS.
#3にて
> > マクロ側で対象ファイルを読み込むなどして
>
> 元は CSV ファイルとかでしょうか?
> これについては、状況がわからないと・・・
と記述していたのは、
もし CSVファイルだったら Excel に読み込んでからではなく、
Samp4 の方法で直接 CSV 操作できるかな・・・
というのがあったので・・・
No.5
- 回答日時:
#3です
処理性能は環境によって大幅変動しましたね
秒数は記述しない方が良かったですね
#4さん提示のサンプルデータ作成を使用してやってみた結果をご報告します。
・サンプルデータの作成
2度やって平均 39秒
#3での Samp3 812.5秒
残った行数 343107
#3での Samp1 28.9秒
残った行数 343043
※ 同じデータでやっとけばよかったと後悔(再度・・・断念)
私が使っていたサンプル作成用を35万件に
Public Sub textData()
Dim r As Range
Dim st As Single
st = Timer()
Application.ScreenUpdating = False
For Each r In Range("A1:G350000")
Select Case r.Column
Case 2
r.Value = r.Row Mod 100
Case Else
r.Value = r.Address(False, False)
End Select
Next
Columns.AutoFit
Application.ScreenUpdating = True
MsgBox Timer() - st & " 秒"
End Sub
※ みてわかると思いますが、残る行数 100 且つ 比較する所は数値
このデータを作るのが 152秒
このデータでは、Samp3 16秒 Samp1 1.2秒
※ 何がどうで・・・考察は各自でお願い致します。
現状では同じ傾向のあるサンプルは作れないのかな・・・と思います。
No.4
- 回答日時:
回答じゃありません。
ショックだったもので。。。こちらは2~3年くらい前の中堅クラスのPCだと思います。
下記で35万件のダミーデータを作成し
30246kiku さんのを試したところ・・・4分半位でした orz
Sub SampMakeData()
Dim i As Long, j As Long, str As String
Application.ScreenUpdating = False
For i = 1 To 350000
For j = 1 To 5
str = str & Chr(Int(Rnd() * 26) + 65)
Next j
Cells(i, 1) = i
Cells(i, 2) = str
str = ""
Next i
Application.ScreenUpdating = True
Beep
End Sub
NotFound404様
ご連絡ありがとうございます。
ご返信が遅くなり大変申し訳ありませんでした。
ダミーデータで検証も頂いたようで大変参考になります。
No.2
- 回答日時:
35万件以上あるエクセルデータなので
Integer型の変数だと、32767 までしか格納できませんから
オーバーフローになります。
データ型をIntegerからLong型に修正して
日本郵便の郵便番号データ(12万件ちょっと)で
走らせると見事に「応答なし」になって1分経っても変化なし。
二重ループで行を総なめに近いことをやっている。
発見する都度、行を削除していますので直接関係ないセルのアドレスまで
移動しなければならずExcel中で変換作業が入って非効率。
なので
予めセルを並び替えてしまい、
セルの値が変わるまで、Do ~ Loop
重複発見の列は、Delete ではなくてClearContents で中身だけ削除。
中身の削除処理が終わったら
ジャンプ(Ctrl+G) → 空白セルを選択 → 削除
をマクロで行えば少しはマシになるかもしれません。
試してません。
が、Excel2010には、データタブ→「重複の削除」があります。
2007にもある・・?
これで行うとあっという間に終わりました。Excel恐るべし。
勉強のためではなく、仕事で必要なら
マクロよりも「重複の削除」が時短になりますね。
nicotinism 様
ご回答ありがとうございます。
お返事が遅くなり大変申し訳ございませんでした。
重複の削除で試してみたのですが、期待通りの結果にならなかったので、断念しました。
No.1
- 回答日時:
まず、オーバーフローの件について。
Integer型は最大で32767までの数字しか扱えません。
lastRow, i, jはLong型で宣言してください。
http://excelvba.pc-users.net/fol5/5_2.html
次に、アルゴリズムについてですが、
「今見ている行と、それより下の行とをすべて比較する」という方式だと、
比較回数が最大で
349999 + 349998 + 349997 + …… + 3 + 2 + 1 = 約612億 (回)
になり、かなりの時間がかかると思われます。
「今見ている行のデータが、今まで見たことのあるデータなら削除する」という方式にすると、
比較回数が35万回程度で済みます。
以下のサイトを参考に作ってみてください。
http://officetanaka.net/excel/vba/tips/tips80.htm
Picosoft様
ご回答ありがとうございます。
またご返信が遅くなり大変申し訳ありませんでした。
やはり35万の二乗ってのは無理がありますよね。。。
参考サイトのご提示ありがとうございます。
お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!
似たような質問が見つかりました
- Excel(エクセル) マクロで最終行から上に検索を逆にしたい 1 2022/05/17 18:27
- Visual Basic(VBA) 数字が「0」の列を削除するため、下記のコードを実行しましたが、コンパイルエラーSubまたはFunct 3 2022/12/04 00:00
- Visual Basic(VBA) Sub 分けてソートして貼り付ける() Dim srcSheet As Worksheet Dim 6 2023/08/04 19:57
- Visual Basic(VBA) vbaの計算 if elseと範囲について 6 2022/11/26 01:49
- Excel(エクセル) B列に文字がはいったらA列に数字が入るマクロードを完成させたい 4 2023/04/21 01:58
- Excel(エクセル) Excel VBAどこが間違ってますか? 4 2023/07/17 10:04
- Excel(エクセル) VBAについて 3 2022/06/19 18:19
- Visual Basic(VBA) VBA Userformで一部別シートに転記がしたいのですが 2 2023/05/24 13:08
- Excel(エクセル) なぜExit Subがあるのかわかりません 4 2023/02/19 12:34
- Visual Basic(VBA) ワークシート内を検索 1 2022/12/19 23:46
関連するカテゴリからQ&Aを探す
おすすめ情報
- ・漫画をレンタルでお得に読める!
- ・思い出すきっかけは 音楽?におい?景色?
- ・あなたなりのストレス発散方法を教えてください!
- ・もし10億円当たったら何に使いますか?
- ・何回やってもうまくいかないことは?
- ・今年はじめたいことは?
- ・あなたの人生で一番ピンチに陥った瞬間は?
- ・初めて見た映画を教えてください!
- ・今の日本に期待することはなんですか?
- ・【大喜利】【投稿~1/31】『寿司』がテーマの本のタイトル
- ・集中するためにやっていること
- ・テレビやラジオに出たことがある人、いますか?
- ・【お題】斜め上を行くスキー場にありがちなこと
- ・人生でいちばんスベッた瞬間
- ・コーピングについて教えてください
- ・あなたの「プチ贅沢」はなんですか?
- ・コンビニでおにぎりを買うときのスタメンはどの具?
- ・おすすめの美術館・博物館、教えてください!
- ・【お題】大変な警告
- ・【大喜利】【投稿~1/20】 追い込まれた犯人が咄嗟に言った一言とは?
- ・洋服何着持ってますか?
- ・みんなの【マイ・ベスト積読2024】を教えてください。
- ・「これいらなくない?」という慣習、教えてください
- ・今から楽しみな予定はありますか?
- ・AIツールの活用方法を教えて
- ・最強の防寒、あったか術を教えてください!
- ・【大喜利】【投稿~1/9】 忍者がやってるYouTubeが炎上してしまった理由
- ・歳とったな〜〜と思ったことは?
- ・モテ期を経験した方いらっしゃいますか?
- ・好きな人を振り向かせるためにしたこと
- ・スマホに会話を聞かれているな!?と思ったことありますか?
- ・それもChatGPT!?と驚いた使用方法を教えてください
- ・見学に行くとしたら【天国】と【地獄】どっち?
- ・これまでで一番「情けなかったとき」はいつですか?
- ・この人頭いいなと思ったエピソード
- ・あなたの「必」の書き順を教えてください
- ・14歳の自分に衝撃の事実を告げてください
- ・人生最悪の忘れ物
- ・あなたの習慣について教えてください!!
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
Excel VBAのユーザーフォームで...
-
csvデータ不要列の削除をbatフ...
-
エクセルで去年のデータを今年...
-
ListBoxにAddItemする際、重複...
-
ACCESS VBAでSeekメソッドの処...
-
pandasでsqlite3にテーブル作成...
-
VBAでの行数を揃える方法
-
【VB】セルが空になるまで処理...
-
EXCELで外部データの取り込みが...
-
【SQL】他テーブルに含まれる値...
-
フラグをたてるってどういうこ...
-
SELECT INTOで一度に複数の変数...
-
sqlに記述できない文字
-
PostgreSQLのtimestamp型で時間...
-
エラーを起こす方法
-
既存データをINSERT文にして出...
-
UPDATEで既存のレコードに文字...
-
timestampのデータはどのように...
-
truncate tableを使って複数の...
-
SELECTした結果に行番号を求めたい
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
追加クエリで重複データなしで...
-
csvデータ不要列の削除をbatフ...
-
Excel VBAのユーザーフォームで...
-
pandasでsqlite3にテーブル作成...
-
自作アプリからAPIで他のアプリ...
-
【VB】セルが空になるまで処理...
-
EXCELで外部データの取り込みが...
-
VBAでの行数を揃える方法
-
ACCESS VBAでSeekメソッドの処...
-
エクセル 2つの列にある値の完...
-
ListBoxにAddItemする際、重複...
-
PHPとMYSQL、DBのデータが空の...
-
Accessでパラメーター以外の削...
-
マクロでファイルを読み込み、...
-
JDBCを使ってdate型へのINSERT...
-
エクセルで去年のデータを今年...
-
【ExcelVBA】範囲選択の方法に...
-
Excel VBA でイベント"Workshee...
-
【SQL】他テーブルに含まれる値...
-
フラグをたてるってどういうこ...
おすすめ情報