【问题标题】:Excel "Do While" Loop Not functioning correctly after first iterationExcel“Do While”循环在第一次迭代后无法正常运行
【发布时间】:2016-05-28 16:46:06
【问题描述】:

我有一个 Sub 可以进行一些简单的格式化,然后我需要它来评估和计算一列是否包含“1”或不包含任何内容,以及该列是否有一个为数字的标题。

Do...Until 循环的第一次迭代完全按照它应该的方式运行。但是,如果我再次尝试运行它,它会将活动单元格一直抛出到工作表 (XFD) 中的最右边一列。我总共需要循环遍历大约 114,000 行。

请看下面的代码,只有第一个循环;这需要嵌套在另一个循环中以循环遍历所有行:

Sub TotalBookCountsProcess()

    Dim ws As Excel.Worksheet
    Dim numberedBooks As Integer 'Total Number of physical books
    Dim virtualBooks As Integer 'Total Number of virtual books
    Dim firstBookCol As Integer 'First Column with a book number
    Dim ispeecCol As Integer 'ISPEC Column
    Dim lastWorksheetCol As Integer 'Last Column in the worksheet after adding total book count columns
    Dim loopColOffset As Integer  'Offset column amounts for new row reset after loop
    Dim lastItem As String 'Last item number in last row of the worksheet

    ActiveCell.End(xlDown).Select
    lastItem = ActiveCell.Value
    ActiveCell.End(xlUp).End(xlToRight).Select
    ActiveCell.Offset(0, 1).Select
    ActiveCell.Value = "Total Numbered Books"
    ActiveCell.Offset(0, 1).Select
    ActiveCell.Value = "Total CS Books"
    lastWorksheetCol = ActiveCell.Column

    Columns.AutoFit

    numberedBooks = 0
    virtualBooks = 0

    Cells.Range("1:1").Find("ISPEC").Select

    ispecCol = ActiveCell.Column
    firstBookCol = ispecCol + 1
    ActiveCell.Offset(1, 1).Select

    loopColOffset = ((lastWorksheetCol - firstBookCol) * -1)

Do Until ActiveCell.End(xlUp).Value = "Total Numbered Books"
    If ActiveCell.Value = 1 And IsNumeric(ActiveCell.End(xlUp).Value) = True Then
        numberedBooks = numberedBooks + 1
        ActiveCell.Offset(0, 1).Select
    ElseIf ActiveCell.Value = 1 And IsNumeric(ActiveCell.End(xlUp).Value) = False Then
        virtualBooks = virtualBooks + 1
        ActiveCell.Offset(0, 1).Select
    Else
        ActiveCell.Offset(0, 1).Select
    End If
Loop

    ActiveCell.Value = numberedBooks
    ActiveCell.Offset(0, 1).Select
    ActiveCell.Value = virtualBooks
    ActiveCell.Offset(1, loopColOffset).Select



End Sub

非常感谢任何见解。

【问题讨论】:

  • 可以附上工作表的截图吗?有更好的选择来实现您正在尝试的内容,而不是遍历所有单元格。
  • 您不需要 VBA 来执行此操作。这可以通过单元格内公式来完成。
  • 假设数据从“A1”开始,尝试将ActiveCell.Worksheet.Range("A1").Select作为第一行。
  • Gary,此宏将在从访问宏导出文件后运行,因此它已经在单元格 A1 中打开。此外,无论启动时活动单元格是什么,当前代码此时都能正常运行。除非我错过了什么。

标签: vba excel iteration do-loops


【解决方案1】:

原因在于

ActiveCell.End(xlUp).Value = "Total Numbered Books"

作为循环的结束条件

您的真正目标是在 ActiveCell 列的第一行具有“总编号图书”值时立即结束行循环

但是

  • ActiveCell.End(xlUp).Value 将引用ActiveCell 上方的第一个非空单元格

  • 从第二次迭代开始,其第一行值实际上是“总编号书籍”的单元格的正上方的单元格也填充了numberedBooks

  • 所以它会一直跳到下一列直到列的末尾...

您的代码可能如下所示:

Option Explicit

Sub TotalBookCountsProcess()

    Dim ws As Excel.Worksheet
    Dim numberedBooks As Integer 'Total Number of physical books
    Dim virtualBooks As Integer 'Total Number of virtual books
    Dim firstBookCol As Integer 'First Column with a book number
    Dim ispeecCol As Integer 'ISPEC Column
    Dim lastWorksheetCol As Integer 'Last Column in the worksheet after adding total book count columns
    Dim loopColOffset As Integer  'Offset column amounts for new row reset after loop
    Dim lastItem As String 'Last item number in last row of the worksheet
    Dim ispecCol As Long

    ActiveCell.End(xlDown).Select
    lastItem = ActiveCell.Value
    ActiveCell.End(xlUp).End(xlToRight).Select
    ActiveCell.Offset(0, 1).Select
    ActiveCell.Value = "Total Numbered Books"
    ActiveCell.Offset(0, 1).Select
    ActiveCell.Value = "Total CS Books"
    lastWorksheetCol = ActiveCell.Column

    Columns.AutoFit

    numberedBooks = 0
    virtualBooks = 0

    Cells.Range("1:1").Find("ISPEC").Select

    ispecCol = ActiveCell.Column
    firstBookCol = ispecCol + 1
    ActiveCell.Offset(1, 1).Select

    loopColOffset = ((lastWorksheetCol - firstBookCol) * -1)

Do
    numberedBooks = 0
    virtualBooks = 0
    Do Until Cells(1, ActiveCell.Column) = "Total Numbered Books"
        If ActiveCell.Value = 1 Then
            If IsNumeric(Cells(1, ActiveCell.Column)) Then
                numberedBooks = numberedBooks + 1
            Else
                virtualBooks = virtualBooks + 1
            End If
        End If
        ActiveCell.Offset(0, 1).Select
    Loop

    ActiveCell.Value = numberedBooks
    ActiveCell.Offset(0, 1).Select
    ActiveCell.Value = virtualBooks
    ActiveCell.Offset(1, loopColOffset).Select
Loop Until Cells(ActiveCell.Row - 1, 1) = lastItem


End Sub

我还添加了行循环

但要确保真正的解决方案是避免所有选择/激活

【讨论】:

  • 做到了。非常感谢,我一定会从这段代码中学习的。
  • 嗯,这是我们都开始使用的那种编码,作为使用宏记录器的“副作用”。但是你必须很快学会摆脱它,放弃“Select/Selection”、“Activate/Active”之类的东西,拥抱完全合格的范围参考风格。最后,如果我的回答满足了您的问题,请将其标记为已接受。谢谢
【解决方案2】:

首先不要使用 select ...您不需要并且会变得代码缓慢并且将依赖于所选的单元格。

我没有详细查看您的代码,但是如果您告诉它第一次运行正确,但第二次没有...通常会发生这种情况,因为在第二次运行中,所选单元格不同。

如何避免问题:

  • 第一种解决方案:在函数中强加起始单元格

    Range("a1").Select ' 这是一个例子

  • 第二种解决方案:将您的代码独立于 activeCell 或 selectedCell。只是也许只是在开始时才启动算法。

    sheet1.range("A1") .....(代码中不使用select和activeCell)

第一个解决方案使您的系统始终处于相同的启动条件。第二种方案与起始条件无关(更好)。

【讨论】:

    最近更新 更多