Board logo

標題: [發問] vlookup速度慢,使用vba取代的程式碼 [打印本頁]

作者: chiang0320    時間: 2018-6-15 14:50     標題: vlookup速度慢,使用vba取代的程式碼

當遇到資料量十幾萬筆的情況下,使用vlookup函數速度會很慢
詢問google大師有這麼一段程式碼
但是試著套,會出現溢位的錯誤,請問是否能幫忙修改程式碼,謝謝!

[attach]28846[/attach]
[attach]28845[/attach]
[attach]28844[/attach]
作者: ikboy    時間: 2018-6-15 15:20

try
dim i as Variant, r as Variant
作者: chiang0320    時間: 2018-6-15 15:45

回復 2# ikboy

ikboy 你好:

請問是直接加入這句程式碼嗎?
作者: ikboy    時間: 2018-6-16 10:13

更改 dim i %, r% 為 dim i as Variant, r as Variant
作者: GBKEE    時間: 2018-6-19 06:02

回復 1# chiang0320

試試看
  1. Option Explicit
  2. 'Option Explicit 為 在模組層次中強迫每個在模組�堛瘍僂くㄔ眸楨�確的宣告。
  3. '這是編寫程式易於偵錯的好習慣
  4. Sub Ex()
  5.     Dim d As Object, E As Range, Ar(), T As Date
  6.     T = Time
  7.     Debug.Print "程式開始時間 : " & T   '指令->檢視->即時運算視窗 :  查看程式起始時間
  8.     'Dim i%= i As Integer
  9.     'Integer 資料型態 Integer 變數係以範圍為 -32,768 到 32,767 之 16 位元 (2 個位元組) 數字的形式儲存。Integer 的型態宣告字元是百分比符號(%
  10.     '********** 不會溢位  ***********
  11.     Dim i As Long  '= i&
  12.     'Long 資料型態
  13.     'Long (長整數)變數係以範圍從 -2,147,483,648 到 2,147,483,647 之 32 位元 (4 個位元組) 有號數字形式儲存。Long 的型態宣告字元為 &。 '

  14.     Set d = CreateObject("scripting.dictionary")  '字典物件
  15.     With Sheets("p10")
  16.         For Each E In .Range(.[a1], .[a1].End(xlDown))
  17.             d(E.Value) = Array(E.Offset(, 2), E.Offset(, 3))
  18.             'e.Value > 字典物件的關鍵字(key) 導入 Array(e.Offset(, 2), e.Offset(, 3))
  19.         Next
  20.     End With
  21.     With Sheets("q72").Range(Sheets("q72").[B2], Sheets("q72").[B2].End(xlDown)).Resize(, 4)
  22.         Ar = .Value
  23.         For i = 1 To UBound(Ar)
  24.             If d.exists(Ar(i, 1)) Then
  25.             'Exists 方法 如果在 Dictionary 物件中指定的關鍵字存在,傳回 True,若不存在,傳回 False。
  26.                 Ar(i, 3) = d(Ar(i, 1))(0)
  27.                 Ar(i, 4) = d(Ar(i, 1))(1)
  28.             Else
  29.                 Ar(i, 3) = "無資料"
  30.                 Ar(i, 4) = "無資料"
  31.             End If
  32.         Next
  33.         .Value = Ar
  34.     End With
  35.     Debug.Print "程式結束時間 : " & Time, Application.Text(Time - T, "共計[S]秒")
  36.     '指令->檢視->即時運算視窗 :  查看程式運行速度
  37. End Sub
複製代碼

作者: Qin    時間: 2018-11-4 00:26

回復 5# GBKEE

套用了你的程式碼, 但是速度滿慢的, 不知道問題出在那里?
可否請G大幫我看看..
謝謝!!

[attach]29631[/attach]
作者: n7822123    時間: 2018-11-4 03:53

本帖最後由 n7822123 於 2018-11-4 03:54 編輯

回復 6# Qin


字典物件的Key 如果輸入的是 "字串",會加快速度
這招準大已經用過不少次,多爬文就知道了
看的有點痛苦,幫你縮排了

Option Explicit
Sub Ex()
Dim d As Object, E As Range, Ar(), T As Date
T = Time
Debug.Print "最宒羲宎奀潔 : " & T
Dim i As Long
Set d = CreateObject("scripting.dictionary")
With Sheets("Data")
  For Each E In .Range(.[a1], .[a1].End(xlDown))
    d(E.Value & "") = Array(E.Offset(, 1), E.Offset(, 2), E.Offset(, 3))
  Next
End With
   
With Sheets("Search").Range(Sheets("Search").[a2], Sheets("Search").[a2].End(xlDown)).Resize(, 4)
  Ar = .Value
  For i = 1 To UBound(Ar)
     If d.exists(Ar(i, 1)) Then
      Ar(i, 2) = d(Ar(i, 1) & "")(0)
      Ar(i, 3) = d(Ar(i, 1) & "")(1)
      Ar(i, 4) = d(Ar(i, 1) & "")(2)
    Else
      Ar(i, 2) = "No Data"
      Ar(i, 3) = "No Data"
      Ar(i, 4) = "No Data"
    End If
  Next
  .Value = Ar
End With

Debug.Print "最宒賦旰奀潔 : " & Time, Application.Text(Time - T, "僕數[S]鏃")
End Sub

[attach]29633[/attach]
作者: 准提部林    時間: 2018-11-4 09:59

E As Range
For Each E In .Range(.[a1], .[a1].End(xlDown))

next

這段改成Array會再加快速度~~
作者: 准提部林    時間: 2018-11-4 10:34

Sub Ex_01()
Dim xD, Arr, Brr, i&, j%, R&, Tm
Tm = Time
Set xD = CreateObject("scripting.dictionary")
Arr = Range([Data!D1], [Data!A1].Cells(Rows.Count, 1).End(3))
For i = 2 To UBound(Arr): xD(Arr(i, 1) & "") = i: Next
   
Brr = Range([Search!D1], [Search!A1].Cells(Rows.Count, 1).End(3))
For i = 2 To UBound(Brr)
    R = Val(xD(Brr(i, 1) & ""))
    For j = 1 To 3
        Brr(i - 1, j) = "No Data"
        If R > 0 Then Brr(i - 1, j) = Arr(R, j + 1)
    Next j
Next i

[Search!B2:D2].Resize(UBound(Brr) - 1) = Brr
MsgBox Time - Tm
End Sub
作者: n7822123    時間: 2018-11-5 01:23

本帖最後由 n7822123 於 2018-11-5 01:32 編輯

回復 9# 准提部林


原來把儲存格資料放到數組陣列後,再裝到字典裡面
會比直接拿儲存格的值放入字典裡面來的快!!
這有點違反直覺...........

以下是我拿準大的程式碼做一些修改來做比較
明顯test_1 比較快

Sub test_1()
Dim T1, d, R%, Arr
T1 = Timer
Set d = CreateObject("scripting.dictionary")
Arr = Range([data!A1], [data!D1].End(4))
For R = 2 To UBound(Arr)
  d(Arr(R, 1) & "") = R
Next R
MsgBox "共耗時" & Round(Timer - T1, 2) & "秒"
End Sub

Sub test_2()
Dim T1, d, R%
T1 = Timer
Set d = CreateObject("scripting.dictionary")
Sheets("data").Activate
For R = 2 To [A1].End(4).Row
  d(Cells(R, 1) & "") = R
Next R
MsgBox "共耗時" & Round(Timer - T1, 2) & "秒"
End Sub
作者: n7822123    時間: 2018-11-5 01:51

本帖最後由 n7822123 於 2018-11-5 01:57 編輯

回復 10# n7822123

提供用Find來查找的方法,不過沒有字典物件來的快(單純用來測試)

Sub test_3()
Dim R%, T1
T1 = Timer
Application.ScreenUpdating = False
Sheets("Search").Activate  '若按鈕在"Search"頁面可省略此列程式
For R = 2 To [A1].End(4).Row
    On Error Resume Next
      Cells(R, 2).Resize(, 3) = [data!A:A].Find(Cells(R, 1), lookat:=xlWhole).Offset(, 1).Resize(, 3).Value
      If Err = 91 Then Cells(R, 2).Resize(, 3) = "No Data"   '找不到會產生錯誤碼:91
    On Error GoTo 0
Next R
MsgBox "共耗時" & Round(Timer - T1, 2) & "秒"
End Sub
作者: Qin    時間: 2018-11-6 08:09

回復 9# 准提部林

准大
因為不懂得套用, 所以用公式做了一個範例
可以請你把它寫成VBA嗎?

資料共有6頁, 每一頁最少的資料有5萬筆, 最多的有7萬筆

[attach]29646[/attach]
作者: 准提部林    時間: 2018-11-6 14:05

回復 12# Qin

Sub Ex_01()
Dim xD, Arr, Brr, xA As Range, xS As Worksheet, R&, C%, i&, j%
Set xD = CreateObject("scripting.dictionary")
R = [Search!A1].Cells(Rows.Count, 1).End(3).Row
Arr = [Search!A1:O1].Resize(R)
For i = 2 To UBound(Arr, 2):   xD(Arr(1, i) & "") = i - 1:   Next '標記[欄]位置
For i = 2 To UBound(Arr):   xD(Arr(i, 1) & "") = i - 1:   Next  '標記[列]位置
Set xA = [Search!B2:O2].Resize(R - 1)  '資料填入區(即原公式區)
xA = "No Data"  '預先填入[No Data], 待有符合再覆蓋
Arr = xA.Value  '帶入Array

For Each xS In Sheets(Array("AB", "CD", "EF", "GH", "KL", "MN"))
    Brr = xS.UsedRange
    For i = 2 To UBound(Brr)
        R = Val(xD(Brr(i, 1) & "")): If R = 0 Then GoTo 101
    For j = 2 To UBound(Brr, 2)
        C = Val(xD(xS.Name & "_" & Brr(1, j)))
        If C > 0 Then Arr(R, C) = Brr(i, j)
    Next j
101: Next i
Next
xA.Value = Arr
End Sub

[attach]29650[/attach]
作者: GBKEE    時間: 2018-11-6 16:11

回復 6# Qin

不慢ㄚ,win 10 ,2010 下 測試只需9-10秒.
作者: Qin    時間: 2018-11-11 00:35

回復 13# 准提部林

准大
我又遇到問題了...
2個程式碼, 想將它修改成:
1) 不論是輸入大寫或小寫都可以抓到資料
2)強制Part No. 一定要完整輸入, 才會抓到資料
3) 如果"A" 欄某個單元格輸入錯誤, 刪除後, B & C 欄單元格里的資料也要一起"清除"

[attach]29666[/attach]
作者: 准提部林    時間: 2018-11-11 10:10

回復 15# Qin
1)自動取對應值
Private Sub Worksheet_Change(ByVal Target As Range)
Dim xR As Range, xF As Range
With Target
     If .Columns.Count > 1 Or .Column <> 1 Then Exit Sub
     For Each xR In .Cells
         If .Row = 1 Then GoTo 101
         xR(1, 2).Resize(1, 2).ClearContents
         If xR = "" Then GoTo 101
         Set xF = [Sheet1!A:A].Find(xR, LookAt:=xlWhole, MatchCase:=False)
         If xF Is Nothing Then GoTo 101
         xR(1, 2).Resize(1, 2) = xF(1, 2).Resize(1, 2).Value
101: Next
End With
End Sub

可以只對A欄單一儲存格輸入取對應值, 或一次貼入多個查詢值取對應~~
-----------------------------------
2)字典法比對取值
只要改兩個(加Ucase, 或Lcase, 將英文字強制轉為大寫或小寫)
xD(UCase(Arr(i, 1))) = i
R = Val(xD(UCase(Brr(i, 1))))
 
 
 
作者: n7822123    時間: 2018-11-11 11:49

本帖最後由 n7822123 於 2018-11-11 11:53 編輯

回復 15# Qin


字典可設定是否區分大小寫
預設模式下,會區分大小寫
把字典的CompareMode屬性設為1,即不分大小寫
以下是Test範例

Sub ex()
Set D = CreateObject("scripting.dictionary")
D.CompareMode = 1      '字典不區分大小寫
D("abc") = 22
D("ABC") = 55
MsgBox D("abc") & "," & D("ABC")
End Sub

詳細VBA說明如下圖
[attach]29667[/attach]
作者: Qin    時間: 2018-11-17 23:17

回復 16# 准提部林

可以只對A欄單一儲存格輸入取對應值, 或一次貼入多個查詢值取對應~~

  
太糗了, 原來一篇程式碼就能解決的事 我卻儍儍的以為要用2篇才能實現
准大你太牛了啦…
這完全是我想要的效果.
高興後, 卻發現自己不懂得修改欄位.
因為有很多Excel 檔都要用到此程式碼
因此又再厚顏上來發問.

問題在附檔
bcca 檔 password :  1234    &   pass

[attach]29693[/attach]
作者: 准提部林    時間: 2018-11-19 16:34

本帖最後由 准提部林 於 2018-11-19 16:42 編輯

回復 18# Qin

Private Sub Worksheet_Change(ByVal Target As Range)
Dim xR As Range, xF As Range, xCr, xCf, j%
xCr = Array(3, 6, 7) '本表要貼入的欄位
xCf = Array(2, 4, 5) '來源表要複製的欄位
With Target
     If .Columns.Count > 1 Or .Column <> 1 Then Exit Sub
     For Each xR In .Cells
         If .Row = 1 Then GoTo 101
         xR(1, 2).Resize(1, 7).ClearContents
         If xR = "" Then GoTo 101
         Set xF = Sheet1.[A:A].Find(xR, LookAt:=xlWhole, MatchCase:=False)
         '_Sheet1為來源表的[屬性名稱], 工作表名稱可任意更改而不影響(見下圖)
         If xF Is Nothing Then GoTo 101
         For j = 0 To UBound(xCr)
             xR(1, xCr(j)) = xF(1, xCf(j)).Value
         Next j
101: Next
End With
End Sub

[attach]29699[/attach]
作者: Qin    時間: 2018-11-24 23:26

回復 19# 准提部林

准大
謝謝你又幫了我個大忙
1)讓我可以任意使用不同的欄位
2)50頁工作表不因工作表名稱變動的問題解決了, 免去了需要逐頁去修改的煩惱

想再請問, 有沒有這樣異想天開的寫法
就是當我把wPrg資料複製去bcca 檔時,是否也可以同時修改工作表名稱並加上當天日期. " w_PRG_20181124"

因為有太多像這樣的檔要處理, 如果以上的要求可以實現, 那實在是太完美了.

[attach]29726[/attach]
作者: 准提部林    時間: 2018-11-25 11:46

回復 20# Qin


Sub CopyPaste()
Dim xA As Range, xB As Workbook, xS As Worksheet, Chk%
Set xA = ActiveSheet.UsedRange
Application.ScreenUpdating = False
Set xB = Workbooks.Open(ThisWorkbook.Path & "\bcca.xls", Password:="1234")
For Each xS In xB.Sheets
    If Left(xS.Name, 6) = "w_PRG_" Then Chk = 1: Exit For
Next
If Chk = 0 Then MsgBox "工作表〔w_PRG〕不存在! ": Exit Sub
With xS
    .Unprotect "pass"
    .UsedRange.Clear
     xA.Copy .[A1]
     .UsedRange.Font.Color = vbWhite
     .Name = "w_PRG_" & Format(Date, "yyyymmdd")
     .Protect "pass"
End With
xB.Close 1
MsgBox "複製完成! "
End Sub
作者: Qin    時間: 2018-11-25 18:33

回復 21# 准提部林

准大
以上的問題解決了, 實在是太棒了,它簡化了我工作的流程. 感激!!

不好意思, 還有一點小問題
之前沒注意到...

1) 想將 L ,M 單元格的資料 copy 去 A & B 欄里,
為何C , F & G 的資料就抓不出來了??

2) 有時會因為手誤, 誤刪A欄的資料, 為何不能用" Ctrl Z" Undo 重新叫出來?

   [attach]29734[/attach]
作者: 准提部林    時間: 2018-11-25 19:37

回復 22# Qin

1)要去了解每一行程式碼的意思, 不然問一堆會沒完沒了∼∼∼∼
Private Sub Worksheet_Change(ByVal Target As Range)
Dim xR As Range, xF As Range, xCr, xCf, j%
xCr = Array(3, 6, 7)
xCf = Array(2, 4, 5)
With Target.Columns(1)  '貼入或輸入區的第一欄
     If .Column <> 1 Then Exit Sub
     For Each xR In .Cells
         If .Row = 1 Then GoTo 101
         xR(1, 3).Resize(1, 5).ClearContents
         If xR = "" Then GoTo 101
         Set xF = Sheet1.[A:A].Find(xR, LookAt:=xlWhole, MatchCase:=False)
         If xF Is Nothing Then GoTo 101
         For j = 0 To UBound(xCr)
             xR(1, xCr(j)) = xF(1, xCf(j)).Value
         Next j
101: Next
End With
End Sub
 
2)CHANGE觸發的程式,就無法再使用〔復原〕!
 
 
 
作者: Qin    時間: 2018-11-25 23:44

回復 23# 准提部林

好的, 在努力學習中....
作者: Qin    時間: 2018-12-2 10:19

回復 23# 准提部林


想起尚未回覆准大的問題

CHANGE觸發的程式,就無法再使用〔復原〕


不了, 還是讓它保留原狀

謝謝!!


   








終于想起步驟的"驟"字了..




歡迎光臨 麻辣家族討論版版 (http://forum.twbts.com/)