vba 将数据从一张纸比较和复制到另一张需要很长时间的宏

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/24998958/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-12 04:00:13  来源:igfitidea点击:

Macro to compare and copy data from one sheet to another taking a long time

excelvbaexcel-vbaoptimization

提问by Rahul Nair

I used this macro to copy contents from one Excel sheet to another, by comparing two columns and finding a matching cell. The problem is that this macro is taking a long time (close to three days) to complete. There are close to 4,00,000 records in both the sheets to compare against.

通过比较两列并找到匹配的单元格,我使用此宏将内容从一个 Excel 工作表复制到另一个 Excel 工作表。问题是这个宏需要很长时间(接近三天)才能完成。两张表中都有近 4,00,000 条记录可供比较。

Can someone please help me to make things faster?

有人可以帮我加快速度吗?

    Option Explicit
    Sub MatchAndCopy()

       Dim sheet01 As Worksheet, sheet02 As Worksheet
       Dim count As Range, matchingCell As Long
       Dim RangeInSheet1 As Variant
       Dim RangeInSheet2 As Variant

       Application.ScreenUpdating = False
       Application.DisplayStatusBar = True

       Set sheet01 = Worksheets("Sheet1")
       Set sheet02 = Worksheets("Sheet2")
       Set RangeInSheet1 = sheet01.Columns(1)
       Set RangeInSheet2 = sheet02.Range("A2", sheet02.Range("A" & Rows.count).End(xlUp))


       For Each count In RangeInSheet2
         matchingCell = 0
         On Error Resume Next
         matchingCell = Application.Match(count, RangeInSheet1, 0)
         On Error GoTo 0
         If matchingCell <> 0 Then
           Application.StatusBar = "Please wait while data is being copied, Processing count : " & count
           sheet01.Range("F" & matchingCell).Value = count.Offset(, 1)
           sheet01.Range("G" & matchingCell).Value = count.Offset(, 2)
           sheet01.Range("H" & matchingCell).Value = count.Offset(, 3)
           sheet01.Range("I" & matchingCell).Value = count.Offset(, 4)
           sheet01.Range("J" & matchingCell).Value = count.Offset(, 5)
         End If
       Next count

       Application.StatusBar = False
       Application.ScreenUpdating = True

    End Sub

回答by Tim Williams

Should be faster:

应该更快:

Sub MatchAndCopy()

    Dim sheet01 As Worksheet, sheet02 As Worksheet
    Dim c As Range, matchingCell As Long
    Dim RangeInSheet1 As Range
    Dim RangeInSheet2 As Range
    Dim dict As Object, tmp
    Set dict = CreateObject("scripting.dictionary")

    Application.ScreenUpdating = False
    Application.DisplayStatusBar = True

    Set sheet01 = Worksheets("Sheet1")
    Set sheet02 = Worksheets("Sheet2")

    Set RangeInSheet1 = sheet01.Range(sheet01.Range("A2"), _
              sheet01.Cells(Rows.count, 1).End(xlUp))
    Set RangeInSheet2 = sheet02.Range(sheet02.Range("A2"), _
              sheet02.Cells(Rows.count, 1).End(xlUp))

    'populate dictionary...
    For Each c In RangeInSheet1.Cells
        tmp = c.Value
        If Not dict.exists(tmp) Then
            dict.Add tmp, c.Row
        End If
    Next c

    For Each c In RangeInSheet2.Cells
      tmp = c.Value
      If dict.exists(tmp) Then
        Application.StatusBar = "Please wait while data is being copied," & _
                                " Processing count : " & c.Row
        sheet01.Cells(dict(tmp), "F").Resize(1, 5).Value = _
                c.Offset(0, 1).Resize(1, 5).Value
      End If
    Next c

    Application.StatusBar = False
    Application.ScreenUpdating = True

End Sub

回答by Zev Spitz

For 4 million records between the two sheets, use a database. Excel is not a database.

对于两张工作表之间的 400 万条记录,请使用数据库。Excel 不是数据库。

If you insist on treating Excel as a database, I would suggest using ADODB. See this answerfor a similar problem and solution.

如果您坚持将 Excel 视为数据库,我建议您使用 ADODB。有关类似问题和解决方案,请参阅此答案

Name each of the columns on Sheet1 that you want to write to, by putting the name in the first row of each column. For the example, let's call them F1,F2,F3,F4and F5. Also, name the column with the shared data between Sheet1 and Sheet2; for the example we'll call it F0.

通过将名称放在每列的第一行,命名 Sheet1 上要写入的每一列。对于这个例子,让我们给他们打电话 F1F2F3F4F5。另外,命名包含 Sheet1 和 Sheet2 之间共享数据的列;对于这个例子,我们称之为F0

Then, if your version of Office allows it, you can issue this statement:

然后,如果您的 Office 版本允许,您可以发出以下声明:

UPDATE [Sheet1$]
INNER JOIN [Sheet2$] ON [Sheet1$].F0 = [Sheet2$].F0
SET 
    [Sheet1$].F1 = [Sheet2$].F1,
    [Sheet1$].F2 = [Sheet2$].F2,
    [Sheet1$].F3 = [Sheet2$].F3,
    [Sheet1$].F4 = [Sheet2$].F4,
    [Sheet1$].F5 = [Sheet2$].F5

If not, you can use the CopyFromRecordset method with the recordset generated from the following SQL statement:

如果没有,您可以对从以下 SQL 语句生成的记录集使用 CopyFromRecordset 方法:

SELECT s1.F0, 
    Iif(s2.F0 Is Not Null, s2.F1, s1.F1),
    Iif(s2.F0 Is Not Null, s2.F2, s1.F2),
    Iif(s2.F0 Is Not Null, s2.F3, s1.F3),
    Iif(s2.F0 Is Not Null, s2.F4, s1.F4),
    Iif(s2.F0 Is Not Null, s2.F5, s1.F5)
FROM [Sheet1$] AS s1
LEFT JOIN [Sheet2$] AS s2 ON s1.F0 = s2.F0

回答by SoftwareTester

Get the whole sheet at once using

使用一次获取整个工作表

var values = sheet.getDataRange().getValues();

and compare values locally

并在本地比较值

EDIT-1
Google Apps script documentation https://developers.google.com/apps-script/reference/spreadsheet/spreadsheetprovides the following example for getDataRange()

EDIT-1
Google Apps 脚本文档https://developers.google.com/apps-script/reference/spreadsheet/spreadsheet为 getDataRange() 提供了以下示例

Returns a Range corresponding to the dimensions in which data is present. This is functionally equivalent to creating a Range bounded by A1 and (Range.getLastColumn(), Range.getLastRow()).


var ss = SpreadsheetApp.getActiveSpreadsheet();
 var sheet = ss.getSheets()[0];

 // This represents ALL the data
 var range = sheet.getDataRange();
 var values = range.getValues();

 // This logs the spreadsheet in CSV format with a trailing comma
 for (var i = 0; i < values.length; i++) {
   var row = "";
   for (var j = 0; j < values[i].length; j++) {
     if (values[i][j]) {
       row = row + values[i][j];
     }
     row = row + ",";
   }
   Logger.log(row);
 }

Instead of using a lot of ranges, data should be obtained in ONE call and processed locally

不应使用大量范围,而应在 ONE 调用中获取数据并在本地处理