Excel VBA質問箱 IV

当質問箱は、有志のボランティア精神のおかげで成り立っています。
問題が解決したら、必ずお礼をしましょうね。
本サイトの基本方針をまとめました。こちら をご一読ください。

投稿種別の選択が必要です。ご注意ください。
迷惑投稿防止のため、URLの入力を制限しています。ご了承ください。


1184 / 13645 ツリー ←次へ | 前へ→

【75769】大量データの比較:VLOOKUPの高速化 西瓜糖 14/7/4(金) 14:14 質問[未読]
【75771】Re:大量データの比較:VLOOKUPの高速化 γ 14/7/4(金) 22:58 回答[未読]
【75772】Re:大量データの比較:VLOOKUPの高速化 γ 14/7/4(金) 23:11 発言[未読]
【75773】Re:大量データの比較:VLOOKUPの高速化 γ 14/7/5(土) 4:50 発言[未読]
【75783】Re:大量データの比較:VLOOKUPの高速化 西瓜糖 14/7/7(月) 13:30 お礼[未読]

【75769】大量データの比較:VLOOKUPの高速化
質問  西瓜糖  - 14/7/4(金) 14:14 -

引用なし
パスワード
   はじめて質問させていただきます。お世話になります。
素人で申し訳ありませんが、どうぞよろしくお願いいたします。

前月と今月のシートを比較し、
新規データと削除データ、修正があったデータを洗い出すのが目的です。
それぞれ、A列からAN列までデータが入っています。M列からP列は空白です。

見よう見まねで以下の通り作成しましたところ、
データの行が100行くらいであれば問題なく動くのですが、
10万行近くなるとフリーズしてしまいます。

高速化のために"Application.Calculation = xlCalculationManual"を追加すると、
計算式が動かなくなってしまいました。

VLOOKUPの代わりにdictionaryオブジェクトを使うべきかとも思いましたが、
使い方がよくわかりません。

ご教示いただけますと幸甚です。
どうぞよろしくお願いいたします。


Sub Data_compare()
Application.ScreenUpdating = False

Dim LastMonth As Worksheet
Dim ThisMonth As Worksheet
Set LastMonth = Worksheets("前月")
Set ThisMonth = Worksheets("今月")

'データ照合

Dim TheRow1 As Long
Dim TheRow2 As Long

'新規データの明示
'今月のシートと前月のシートを比較。
'今月のシートのL列の数値(重複なし)をキーとし、前月のシートを検索。
'データがあれば、今月のシートのM列は空白のままとする。
'データがなければ、今月のシートのM列に「新規」と入力。

ThisMonth.Select
TheRow2 = Range("A1").CurrentRegion.Rows.Count
Range("M2").Formula = "=IF(ISNA(VLOOKUP(L2,前月!$L:$L,1,FALSE)),""新規"","""")"
Range("M2").Copy
Range("M3:M" & TheRow2).Select
ActiveSheet.Paste
Application.CutCopyMode = False

Range("M:M").Copy
Range("M1").PasteSpecial Paste:=xlPasteValues
Application.CutCopyMode = False

LastMonth.Select
 
'後に出てくる「範囲修正」でVLOOKUPを使うが、
'キーがL列で、ピックアップしたい列がG列(左側)のため、
'前月のデータのG列からP列にデータをコピー&ペースト

Columns(7).Copy
Columns(16).PasteSpecial

'削除データの明示
'前月のシートと前月のシートを比較。
'前月のシートのL列の数値(重複なし)をキーとし、今月のシートを検索。
'データがあれば、前月のシートのM列は空白のままとする。
'データがなければ、前月のシートのM列に「削除」と入力。

TheRow1 = Range("A1").CurrentRegion.Rows.Count
Range("M2").Formula = "=IF(ISNA(VLOOKUP(L2,今月!$L:$L,1,FALSE)),""削除"","""")"
Range("M2").Copy
Range("M3:M" & TheRow1).Select
ActiveSheet.Paste
Application.CutCopyMode = False

Range("M:M").Copy
Range("M1").PasteSpecial Paste:=xlPasteValues
Application.CutCopyMode = False

'範囲修正の明示
'今月のシートのG列と前月のシートのP列(G列のデータをコピーしたもの)を比較。
'M列が空白のデータ(今月と前月のシートの両方にデータが存在するもの)を対象とする。
'今月のシートのL列の数値(重複なし)をキーとし、前月のシートを検索。
'G列とP列のデータに相違がなければM列は空白のままとする。
'G列とP列のデータに相違があれば、M列に「範囲修正」と入力。

ThisMonth.Select

For i = 2 To TheRow2
  If Cells(i, 13).Value = "" Then
  Cells(i, 13).Formula = "=If(G" & i & "=VLOOKUP(L" & i & ",前月!$L:$P,5,FALSE),"""",""範囲修正"")"
    Cells(i, 13).Copy
    Cells(i, 13).PasteSpecial Paste:=xlPasteValues

  End If
Next

'--並び替え---------------------------------------
'F列を第1キー、A列を第2キーとして並べ替え

LastMonth.Select
Range("A:AN").Sort _
  Key1:=Range("F1"), Order1:=xlAscending, _
  Key2:=Range("A1"), Order1:=xlAscending, _
  Header:=xlYes, _
  OrderCustom:=1, _
  MatchCase:=False, _
  Orientation:=xlTopToBottom, _
  SortMethod:=xlPinYin

ThisMonth.Select
Range("A:AN").Sort _
  Key1:=Range("F1"), Order1:=xlAscending, _
  Key2:=Range("A1"), Order1:=xlAscending, _
  Header:=xlYes, _
  OrderCustom:=1, _
  MatchCase:=False, _
  Orientation:=xlTopToBottom, _
  SortMethod:=xlPinYin

Application.ScreenUpdating = True

End Sub

【75771】Re:大量データの比較:VLOOKUPの高速化
回答  γ  - 14/7/4(金) 22:58 -

引用なし
パスワード
   たたき台。
Sub test()
  Dim lastMonth As Worksheet
  Dim thisMonth As Worksheet
  Dim myRow1 As Long
  Dim myRow2 As Long

  Dim dic前月 As Object
  Dim dic当月 As Object
  Dim v As Variant
  Dim vv As Variant
  Dim k As Long
  Dim kk As Long

  Set dic前月 = CreateObject("Scripting.Dictionary")
  Set dic当月 = CreateObject("Scripting.Dictionary")

  Set lastMonth = Worksheets("前月")
  Set thisMonth = Worksheets("今月")

  myRow1 = lastMonth.Range("A1").CurrentRegion.Rows.Count
  myRow2 = thisMonth.Range("A1").CurrentRegion.Rows.Count

  '辞書の作成
  For k = 1 To myRow2
    dic前月(lastMonth.Cells(k, "L").Value) = k
  Next

  For k = 1 To myRow2
    dic当月(thisMonth.Cells(k, "L").Value) = k
  Next

  ' ---------削除データ
  For k = 1 To myRow1
    v = lastMonth.Cells(k, "L").Value
    If Not dic当月.exists(v) Then
      lastMonth.Cells(k, "M").Value = "削除"
    End If
  Next
  ' ---------新規データ
  For k = 1 To myRow2
    v = thisMonth.Cells(k, "L").Value
    If Not dic前月.exists(v) Then
      thisMonth.Cells(k, "M").Value = "新規"
    End If
  Next
  ' --------- 修正データ
  For k = 1 To myRow2
    v = thisMonth.Cells(k, "L").Value
    If dic前月.exists(v) Then
      kk = dic前月(v)
      vv = lastMonth.Cells(kk, "G").Value
      If vv <> thisMonth.Cells(k, "G").Value Then
        thisMonth.Cells(k, "M").Value = "修正"
      End If
    End If
  Next
End Sub

あとは、配列化によるスピードアップだけれど、
・新規、削除、修正箇所がさほど多くなければ、
 書き込み部分の測度上昇の余地は小さいだろう。
・読み込み部分のセル範囲を纏めて配列で読み込んでから、
 処理する方法もあるけれど、劇的に早くなる気は余りしない。
そこそこのスピードは出るのではないか。

【75772】Re:大量データの比較:VLOOKUPの高速化
発言  γ  - 14/7/4(金) 23:11 -

引用なし
パスワード
   新規データ判定部分と、修正データ部分は繰り返しをひとつにすることが
できます。それは、そちらでトライしてみてください。

【75773】Re:大量データの比較:VLOOKUPの高速化
発言  γ  - 14/7/5(土) 4:50 -

引用なし
パスワード
   dic前月を作成するところ、
>  '辞書の作成
>  For k = 1 To myRow2
は  For k = 1 To myRow1
の間違いです。

【75783】Re:大量データの比較:VLOOKUPの高速化
お礼  西瓜糖  - 14/7/7(月) 13:30 -

引用なし
パスワード
   ▼γ さん:

ご教示いただきましたプログラムで、
問題なく短時間で動作するようになりました。
本当に助かりました。どうもありがとうございました。

1184 / 13645 ツリー ←次へ | 前へ→
ページ:  ┃  記事番号:
2610219
(SS)C-BOARD v3.8 is Free