【问题标题】:Compare the previous value of a cell with the current one将单元格的先前值与当前值进行比较
【发布时间】:2020-06-12 13:40:57
【问题描述】:

“O2:O20”范围内的每个单元格都填充有数值。在这些单元格中的每一个旁边都有一个单元格,该单元格也填充了数值,具体取决于“O2:020”中存在的值。例如:如果“O2” = 10.2,则其一侧的单元格“P2” = 1000,然后“P2” = 500,然后“P2” = 600,然后“P2” = 50;简而言之,“P2”可以取任何正的自然值。我想计算“P2”采用的先前值与只要“O2”保持相同值即可采用的当前值之间的差异。如果“O2”的值发生变化,那么差异对我来说并不重要:例如:如果“O2” = 10.2 和“P2” = 50 然后“O2” = 10 和“P2” = 3000,在这种情况下,不,我想知道区别,因为两个单元格的“O2”不一样。

【问题讨论】:

    标签: excel vba record


    【解决方案1】:

    希望我能理解您的问题。请参阅此解决方案。 它正在使用 Option Base 1。 更新了将差异写入 Q 列的程序。 如果不需要该消息,请删除或 Rem 最后一个 MsgBox 的行。

    Option Base 1
    Private Sub Worksheet_Change(ByVal Target As Range)
        'Static declaration to safe the previous values and change status
        Static vO As Variant
        Static vP As Variant
        Static bolOChanged() As Boolean
        
        'Set up the ranges
        Dim rngO As Range, rngP As Range, rngQ As Range
        Set rngO = ThisWorkbook.ActiveSheet.Range("O2:O20")
        Set rngP = ThisWorkbook.ActiveSheet.Range("P2:P20")
        Set rngQ = ThisWorkbook.ActiveSheet.Range("Q2:Q20") 'Range for results
        
        'If the change is not in the range then ignore
        If Intersect(Union(rngO, rngP), Target) Is Nothing Then Exit Sub
        
        'Prevent unhandelt multiply changes. If multiply changes required than the
        'Target range shall be loop through
        If Target.Cells.Count > 1 Then
            Application.EnableEvents = False
            rngO.Value = vO
            rngP.Value = vP
            Application.EnableEvents = True
            MsgBox "You cannot change more the one cell in the range of: " & Union(rngO, rngP).Address
            Exit Sub
        End If
        
        'At the firs occasion the current status has to be saved
        If VarType(vO) < vbArray Then
            vO = rngO.Value
            vP = rngP.Value
            ReDim bolOChanged(1 To UBound(vO))
        End If
        
        Dim iIndex As Long 'Adjust the index of the array to the Row of Target cell
        iIndex = Target.Row - rngO(1).Row + 1
        
        If Not Intersect(rngO, Target) Is Nothing Then
            'Change was in O range, so next P change shall be ignored
            bolOChanged(iIndex) = True
        Else
            'rngP changed
            If bolOChanged(iIndex) Then
                'There was a previous O range change, ignore
                bolOChanged(iIndex) = False 'Delete the recent change flag
                vP(iIndex, 1) = Target.Value 'Store the value
            Else
                rngQ(iIndex).Value = Target.Value - vP(iIndex, 1)
                MsgBox "Value change from: " & vP(iIndex, 1) & ", to: " & Target.Value & ". Difference is: " & Target.Value - vP(iIndex, 1)
                vP(iIndex, 1) = Target.Value 'Store the value
            End If
        End If
    End Sub
    

    更新:此版本适用于多个条目。

    Option Base 1
    Private Sub Worksheet_Change(ByVal Target As Range)
        'Static declaration to safe the previous values and change status
        Static vO As Variant
        Static vP As Variant
        Static bolOChanged() As Boolean
        
        'Set up the ranges
        Dim rngO As Range, rngP As Range, rngQ As Range
        Set rngO = ThisWorkbook.ActiveSheet.Range("O2:O20")
        Set rngP = ThisWorkbook.ActiveSheet.Range("P2:P20")
        Set rngQ = ThisWorkbook.ActiveSheet.Range("Q2:Q20") 'Range for results
        
        'If the change is not in the range then ignore
        If Intersect(Union(rngO, rngP), Target) Is Nothing Then Exit Sub
            
        'At the firs occasion the current status has to be saved
        If VarType(vO) < vbArray Then
            vO = rngO.Value
            vP = rngP.Value
            ReDim bolOChanged(1 To UBound(vO))
        End If
        
        Dim iIndex As Long 'Adjust the index of the array to the Row of Target cell
        Dim item As Variant
        For Each item In Target
            iIndex = item.Row - rngO(1).Row + 1
            If Not Intersect(rngO, item) Is Nothing Then
                'Change was in O range, so next P change shall be ignored
                bolOChanged(iIndex) = True
            Else
                'rngP changed
                If bolOChanged(iIndex) Then
                    'There was a previous O range change, ignore
                    bolOChanged(iIndex) = False 'Delete the recent change flag
                    vP(iIndex, 1) = item.Value 'Store the value
                Else
                    rngQ(iIndex).Value = item.Value - vP(iIndex, 1)
                    MsgBox "Value changed in cell " & item.Address & " from: " & vP(iIndex, 1) & ", to: " & item.Value & ". Difference is: " & item.Value - vP(iIndex, 1)
                    vP(iIndex, 1) = item.Value 'Store the value
                End If
            End If
        Next item
    End Sub
    

    【讨论】:

    • 嗨@Viktor,我对你附加的代码有几个问题:我应该把代码放在哪里?在一个模块中?在本工作簿中?在发生操作的工作表中?练习中提到的所有单元格都通过类似这样的函数获取它们的值:='Sheet10'! L21,也就是说,它们采用其他工作表上其他单元格中的现有值,不会发生手动数据输入,所以我认为 (Private Sub Worksheet_Change ...) 如果通过函数发生更改,我认为 (Private Sub Worksheet_Change ...) 对我不起作用。提前致谢。
    • 我在 ThisWorkbook 中使用了另一个代码。我将在下面附加的这段代码允许我检测单元格包含函数时的更改,那么我应该添加什么?
    • '''Private Sub Workbook_SheetCalculate(ByVal Sh As Object)
    • 放入您拥有数据的工作表对象。 (例如,双击 sheet1 打开)。如果找不到,请用图片搜索。
    • Hola @Viktor,我已经在工作表中正确输入了代码,它对我对每个单元格所做的手动更改反应非常好,但我提到过 O2 的每个单元格:P20 范围包含一个函数(例如:O2 = 'Sheet5'!M30),因此: 更改事件不会检测到何时进行更改。不允许对 O2 进行多次更改:P20,我需要允许它。
    【解决方案2】:

    此解决方案使用工作表的更多列来存储要与实际值进行比较的先前值。在我的示例中,单元格 O2 和 O3 中的值将始终相同。

    Sub Populate_OandP()
    
    'Store previous values
    Call PreviousValues
    
    'This code just simulates the data population in columns O and P
                    Dim intRndNumber As Integer
    
                    Range("O2").Value = 10.2
                    Range("O3").Value = 10
    
                    intRndNumber = Int((10 - 1 + 1) * Rnd + 1)
    
                    For i = 4 To 20
                        intRndNumber = Int((10 - 1 + 1) * Rnd + 1)
                        Cells(i, 15).Value = intRndNumber * 10
                    Next i
    
                    For i = 2 To 20
                        intRndNumber = Int((10 - 1 + 1) * Rnd + 1)
                        Cells(i, 16).Value = intRndNumber * 10
                    Next i
    
    'Check differences
    Call CheckDifferenceIfOChanges    
    
    End Sub
    
    Sub PreviousValues()
    
    For i = 2 To 20
        Cells(i, 18).Value = Cells(i, 15).Value
        Cells(i, 19).Value = Cells(i, 16).Value
    Next i
    
    End Sub
    
    Sub CheckDifferenceIfOChanges()
    
    For i = 2 To 20
        If Cells(i, 18).Value = Cells(i, 15).Value Then
            Cells(i, 20).Value = Cells(i, 19).Value - Cells(i, 16).Value
        Else: Cells(i, 20).Value = "O columns value changed"
        End If
    Next i
    
    End Sub
    

    【讨论】:

    • Hola @Danilo,在测试您附加给我的代码后,我得到以下信息:当在“O2:O20”范围内的任何单元格中进行更改时,它不会被维护,即: 如果 "O10" = 61,这个数字不会被维持而是改变。代码运行缓慢,并且对每个单元格进行了一些烦人的审查。也许是我把代码错了?我把它放在一个模块里,那里可以吗?正如我对 Viktor 评论的那样,每个单元格中的数据输入是通过函数输入的,输入不是手动的。提前致谢,
    • iPerry,请注意,我编写的部分代码只是为了模拟 O 和 P 列中的数据。这部分代码应该被删除。 “'此代码仅模拟 O 和 P 列中的数据填充”和“'检查差异”上方的所有内容都只是为了模拟数据。尝试删除我提到的部分(甚至不是我制作了不同的列表)。总之,您只需要“Call PreviousValues”和“Call CheckDifferenceIfOChanges”。
    • iPerry,在你的问题中你没有提到你想如何运行代码。我提出的解决方案不会自动运行。
    猜你喜欢
    • 2022-08-23
    • 1970-01-01
    • 1970-01-01
    • 2016-05-27
    • 2019-12-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多