繁体   English   中英

如何编写Excel-VBA代码以使用从程序导出的数据更新Excel工作表?

[英]How write an Excel-VBA code to update an excel sheet with data that is exported from a program?

这里是业余编码员,我正在尝试编写一个Excel-VBA代码,该代码可以使用来自数据转储的较新信息自动更新行(原始数据来自先前的转储,只是想保持excel工作表的最新状态)数据转储,并使用户输入数据与数据转储数据对齐)。 该代码应在每一行中在同一列全片重复数据的前4列比较值,如果发现重复行具有相同的数据,将用新的数据替换原始行只是一个例子,我捏造的。 因此,在此示例中,代码将第8行的EFG列替换为第1行的EFG列,因为它们均已更改,但将所有用户输入数据保留为较新的数据。 第5行FG列也是如此,由第11行FG取代。 等等。 然后根据列AD中的重复数据删除所有重复的行。 我已经在Excel-VBA中编写了一些非常基本的代码,但是这一代码远远超出了我的能力范围,因此我什至不知道从哪里开始。 任何建议或简单的起点都会有所帮助!

我最近对在Excel VBA中查找重复项进行了一些测试。 这是StackOverflow上的一个常见问题,使用各种方法,答案范围从笨拙到精致。

恐怕OP无法提供足够的信息来回答他/她的特定问题,但是显然他/她将需要编写例程来管理重复项。 因此,我希望下面的代码能有所帮助。

该测试涉及获取500,000个项目的数据集,将唯一值写入新表并将这些值存储在arrayCollection 我记录了使用5种不同方法处理任务的时间: Range.AdvancedFilterCollection ,Array比较, Range.RemoveDuplicatesApplication.Match 这些项目仅存储在一列中,并且是具有500个唯一值的字符串(因此没有日期可以使事情变得更尴尬)。 速度顺序的结果是:

  1. AdvancedFilter,0.19秒
  2. 收集1.83秒
  3. RemoveDuplicates,2.41秒
  4. 阵列比较,37.28秒
  5. 比赛38.75秒

高级过滤器

优点:

  • 惊人的快速,无疑是删除重复项的最佳方法。

缺点:

  • 需要输出到Range (因此可能需要隐藏的工作表)
  • 包含标题(因此需要对结果进行一些管理)
  • 如果没有进一步的编码,就无法确定一个项目与另一个项目的匹配位置(因此,如果您要查找匹配的值(如此操作)将无法使用)。

采集

优点:

  • 所有内容都包含在VBA中(因此,如果您不将结果写入worksheet效果很好
  • 相当快
  • 识别匹配项(例如,值,匹配项的索引等)
  • 使您能够将其他数据存储在与唯一值关联的集合中(例如,重复发生多少次,其他行值等)。

缺点:

  • 需要一个String作为唯一键(因此可能需要强制转换,如果该键最初是IntegerLong Integer ,并且您忘记将其转换为String进行查找,则可能会发生错误)
  • 要求捕获错误以找到重复项,并且一些开发人员不喜欢将此作为一种哲学( Dictionary对象将绕过此问题)。

删除重复项

优点:

  • 按照锡罐上的指示进行操作-如果您只想从现有Range删除重复项,这是一项很棒的技术
  • 无需在其他地方输出结果
  • 标头没有问题
  • 仍然可观的速度

缺点:

  • AdvancedFilter ,没有进一步的编码就无法识别匹配项。

数组比较

优点:

  • 对于VBA的初学者来说非常好,因为代码易于理解和编写。
  • 识别匹配项并保持唯一项的连续计数
  • 与Collections一样,将所有内容保留在VBA中。

缺点:

  • 速度极慢(如果对数据进行排序,还有很大的提高速度的余地)
  • 写入Worksheet更加困难,因为结果数组是一维的,因此管理行可能会成为问题。 如果只有VBA让你ReDim第一维...

比赛

优点:

  • 如果您只想找到一个匹配项,就可以正常工作
  • 识别比赛

缺点:

  • 如果您有大量数据集,请放水壶
  • 低效的代码(但跳过已知重复项等改进将大有帮助)

因此,我想在管理重复项时很难超越AdvancedFiltersCollections ,但是没人能容忍丑陋的事物,所以请选择。

如果您有兴趣,测试代码如下:

Option Explicit
Private mTimer As clsTimer
Private mDataRanges As Collection
Private Const ADV_FILTER_KEY As String = "AdvancedFilter"
Private Const COLLECTION_KEY As String = "Collection"
Private Const ARRAY_COMP_KEY As String = "Array Comparison"
Private Const REMOVE_DUPES_KEY As String = "RemoveDuplicates"
Private Const MATCH_KEY As String = "Match"

Public Sub RunMe()
    Dim srcSht As Worksheet
    Dim outSht As Worksheet
    Dim lastCell As Range
    Dim loc As clsRanges

    'Initialise
    Set mTimer = New clsTimer

    'Idenfity the source data
    Set srcSht = ThisWorkbook.Worksheets("SourceData")
    Set outSht = ThisWorkbook.Worksheets("UniqueList")
    Set lastCell = srcSht.Cells(srcSht.Rows.count, "A").End(xlUp)

    'Prepare the output sheet
    outSht.Cells.Clear
    outSht.Cells(1, 1).Value = "Type"
    outSht.Cells(2, 1).Value = "Secs"


    'Define the source and output ranges
    Set mDataRanges = New Collection

    Set loc = New clsRanges
    loc.Create lastCell, outSht, 2, True
    mDataRanges.Add loc, ADV_FILTER_KEY

    Set loc = New clsRanges
    loc.Create lastCell, outSht, 3
    mDataRanges.Add loc, COLLECTION_KEY

    Set loc = New clsRanges
    loc.Create lastCell, outSht, 4
    mDataRanges.Add loc, ARRAY_COMP_KEY

    Set loc = New clsRanges
    loc.Create lastCell, outSht, 5
    mDataRanges.Add loc, REMOVE_DUPES_KEY

    Set loc = New clsRanges
    loc.Create lastCell, outSht, 6
    mDataRanges.Add loc, MATCH_KEY

    'Find the unique values using different methods
    UsingAdvFilter
    UsingCollection
    UsingArrayComparison
    UsingRemoveDuplicates
    UsingMatch

End Sub
Private Sub UsingAdvFilter()
    Dim loc As clsRanges
    Dim v As Variant
    Dim rng As Variant
    Dim srcRange As Range
    Dim outRange As Range

    'Start the clock
    mTimer.StartCounter

    'Run the filter to write unique values
    Set loc = mDataRanges(ADV_FILTER_KEY)
    loc.SourceRange.AdvancedFilter Action:=xlFilterCopy, CopyToRange:=loc.OutputRange, unique:=True

    'Read the unique values into an array
    v = loc.OutputRange.CurrentRegion.Resize(, 1).Value

    'Stop the clock
    loc.OutputRange.Offset(-1).Value = Round(mTimer.TimeElapsed / 1000, 2)

    'Write the heading
    loc.OutputRange.Offset(-2).Value = ADV_FILTER_KEY
    loc.OutputRange.EntireColumn.AutoFit
End Sub
Private Sub UsingCollection()
    Dim col As Collection
    Dim data As Variant
    Dim key As String
    Dim item As Variant
    Dim v() As Variant
    Dim i As Long
    Dim loc As clsRanges

    'Start the clock
    mTimer.StartCounter

    'Read the source data into an array
    Set loc = mDataRanges(COLLECTION_KEY)
    data = loc.SourceRange.Value2


    'Prepare error handler to trap duplicate keys
    On Error Resume Next

    'Loop through the data array to find unique values
    Set col = New Collection
    For i = 1 To UBound(data, 1)

        'Define the key (must be a String)
        key = CStr(data(i, 1))

        'Test if collection already contains the key
        'If it doesn't an error 5 will be thrown
        item = col(key)
        If Err.Number = 5 Then 'key doesn't exist
            col.Add data(i, 1), key
            Err.Clear
        ElseIf Err.Number <> 0 Then 'trap any unplanned errors
            MsgBox Err.Description
            End
        End If

    Next

    'Restore the error handler
    On Error GoTo 0

    'Read the unique values into an array
    ReDim v(1 To col.count, 1 To 1)
    i = 1
    For Each item In col
        v(i, 1) = item
        i = i + 1
    Next

    'Write the unique values
    loc.OutputRange.Resize(UBound(v, 1)).Value = v

    'Stop the clock
    loc.OutputRange.Offset(-1).Value = Round(mTimer.TimeElapsed / 1000, 2)

    'Write the heading
    loc.OutputRange.Offset(-2).Value = COLLECTION_KEY
    loc.OutputRange.EntireColumn.AutoFit
End Sub
Private Sub UsingArrayComparison()
    Dim loc As clsRanges
    Dim data As Variant
    Dim tmp() As Variant
    Dim v() As Variant
    Dim i As Long
    Dim c As Long
    Dim count As Long
    Dim isUnique As Boolean

    'Start the clock
    mTimer.StartCounter

    'Read the source data into an array
    Set loc = mDataRanges(ARRAY_COMP_KEY)
    data = loc.SourceRange.Value2

    'Dimension the array which will temporarily store unique values
    ReDim tmp(1 To UBound(data, 1))

    'Set the unique counter - use 0 to prevent the loop running on first item.
    count = 0

    'Loop through the data array
    For i = 1 To UBound(data, 1)

        'Test if value is already contained in unique list
        'by iterating through it until a match is found
        isUnique = True
        For c = 1 To count
            If data(i, 1) = tmp(c) Then
                isUnique = False
                Exit For
            End If
        Next

        'If no match is found then add it to the temporary array
        'and increment the count
        If isUnique Then
            count = count + 1
            tmp(count) = data(i, 1)
        End If

    Next

    'Trim the temporary array to the unique count size
    ReDim Preserve tmp(1 To count)

    'Unfortunately we can't write a one-dimensional array to
    'a Worksheet (without using some form of Transposition)
    'so we'll copy it to a two-dimensional one.
    'It would be easier if we could just Dim the tmp array
    'in two dimensions, but ReDim only allows us to adjust the
    'last dimension (ie column), so we can't deal with rows.
    ReDim v(1 To count, 1 To 1)
    For i = 1 To count
        v(i, 1) = tmp(i)
    Next

    'Write the unique values
    loc.OutputRange.Resize(count).Value = v

    'Stop the clock
    loc.OutputRange.Offset(-1).Value = Round(mTimer.TimeElapsed / 1000, 2)

    'Write the heading
    loc.OutputRange.Offset(-2).Value = ARRAY_COMP_KEY
    loc.OutputRange.EntireColumn.AutoFit
End Sub
Private Sub UsingRemoveDuplicates()
    Dim loc As clsRanges
    Dim rng As Range
    Dim v As Variant
    Dim count As Long

    'Start the clock
    mTimer.StartCounter

    'Resize the output range to match the source data range
    Set loc = mDataRanges(REMOVE_DUPES_KEY)
    Set rng = loc.OutputRange.Resize(loc.SourceRange.Rows.count)

    'Turn off screen updating to keep our test fair
    Application.ScreenUpdating = False

    'Write the full source data to the output sheet
    rng.Value = loc.SourceRange.Value2

    'Run the remove duplicates routine
    rng.RemoveDuplicates 1, xlNo

    'Restore screen updating
    Application.ScreenUpdating = True

    'Calculate size of range without the duplicates
    count = rng.Cells(rng.Rows.count, 1).End(xlUp).Row - loc.OutputRange.Row + 1

    'Read the values into an array
    v = loc.OutputRange.Resize(count).Value

    'Stop the clock
    loc.OutputRange.Offset(-1).Value = Round(mTimer.TimeElapsed / 1000, 2)

    'Write the heading
    loc.OutputRange.Offset(-2).Value = REMOVE_DUPES_KEY
    loc.OutputRange.EntireColumn.AutoFit
End Sub
Private Sub UsingMatch()
    Dim data As Variant
    Dim lastPos As Long
    Dim searchRange As Range
    Dim isUnique As Boolean
    Dim loc As clsRanges
    Dim count As Long
    Dim i As Long
    Dim tmp() As Variant
    Dim v() As Variant


    'Start the clock
    mTimer.StartCounter

    'Read the source data into an array
    Set loc = mDataRanges(MATCH_KEY)
    data = loc.SourceRange.Value2

    'Dimension the array which will temporarily store unique values
    ReDim tmp(1 To UBound(data, 1))

    'Prepare the loop parameters
    lastPos = UBound(data, 1)
    count = 0

    For i = 1 To lastPos

        If i = lastPos Then 'no need to look for a match as it's the last one
            isUnique = True
        Else
            'Define the search range to be one below the current item to the end.
            Set searchRange = loc.SourceRange.Cells(i + 1, 1).Resize(lastPos - i)
            isUnique = IsError(Application.Match(data(i, 1), searchRange, 0))
        End If

        'If there's no match, add the item to our uniques array
        If isUnique Then
            count = count + 1
            tmp(count) = data(i, 1)
        End If

    Next

    'Trim the temporary array to the unique count size
    ReDim Preserve tmp(1 To count)

    'Same one-dimensional array issue as array method so transpose.
    ReDim v(1 To count, 1 To 1)
    For i = 1 To count
        v(i, 1) = tmp(i)
    Next

    'Write the unique values
    loc.OutputRange.Resize(count).Value = v

    'Stop the clock
    loc.OutputRange.Offset(-1).Value = Round(mTimer.TimeElapsed / 1000, 2)

    'Write the heading
    loc.OutputRange.Offset(-2).Value = MATCH_KEY
    loc.OutputRange.EntireColumn.AutoFit
End Sub

...为了完整clsRanges ,这是clsRanges代码:

Private mSrcRange As Range
Private mOutRange As Range
Public Sub Create(srcLastCell As Range, outSht As Worksheet, outCol As Long, Optional incHeader As Boolean = False)
    Dim ws As Worksheet
    Dim r As Long
    Dim c As Long

    Set ws = srcLastCell.Worksheet
    r = IIf(incHeader, 1, 2)
    c = srcLastCell.Column
    Set mSrcRange = ws.Range(ws.Cells(r, c), srcLastCell)
    Set mOutRange = outSht.Cells(3, outCol)

End Sub
Public Property Get SourceRange() As Range
    Set SourceRange = mSrcRange
End Property
Public Property Get OutputRange() As Range
    Set OutputRange = mOutRange
End Property

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM