Cộng giá trị vào cell khác khi cell chính thay đổi

Liên hệ QC

wtsrvd11

Thành viên mới
Tham gia
17/7/19
Bài viết
5
Được thích
2
Hi mọi người,
Em có bài toán nan giải lên đây hỏi ạ.
Chuyện là em có 1 cột cần theo dõi sự thay đổi. Nếu 1 cell trong cột đó thay đổi thì thì cell cùng hàng ở cột khác cộng giá trị ở cột vừa thay đổi. như mô tả hình dưới.screenshot_1594348628.png
Em cảm ơn mọi người giúp đỡ.
 
Nếu 1 cell trong cột đó thay đổi thì thì cell cùng hàng ở cột khác cộng giá trị ở cột vừa thay đổi như mô tả.
Sao mình xem như mô tả thì nó đâu có như vầy đâu bạn: "Nếu 1 cell trong cột đó thay đổi thì thì cell cùng hàng ở cột khác cộng giá trị ở cột vừa thay đổi". Mình thấy đâu có cùng hàng đâu.
Có phải bạn muốn ô giá trị ngày tháng bên cột "Mốc" thay đổi thì giá trị thay đổi đó được cập nhật vào dòng cuối cùng của cột "Mốc cũ" hay không?

Bạn up file đó lên đi để mọi người xem giúp cho nhé !
Thân.
 
Upvote 0
Sao mình xem như mô tả thì nó đâu có như vầy đâu bạn: "Nếu 1 cell trong cột đó thay đổi thì thì cell cùng hàng ở cột khác cộng giá trị ở cột vừa thay đổi". Mình thấy đâu có cùng hàng đâu.
Có phải bạn muốn ô giá trị ngày tháng bên cột "Mốc" thay đổi thì giá trị thay đổi đó được cập nhật vào dòng cuối cùng của cột "Mốc cũ" hay không?

Bạn up file đó lên đi để mọi người xem giúp cho nhé !
Thân.
Không ạ. nếu ô ở cột mốc thay đổi thì giá trị trước khi thay đổi được cập nhật vào dòng cuối cùng của cột "Mốc cũ".
 

File đính kèm

  • New Microsoft Excel Worksheet.xlsx
    10.4 KB · Đọc: 16
Upvote 0
Không ạ. nếu ô ở cột mốc thay đổi thì giá trị trước khi thay đổi được cập nhật vào dòng cuối cùng của cột "Mốc cũ".
1. Bài của bạn mình thấy nó hơi "có vấn đề" ở dữ liệu cột C á (Mốc). Vì khi nhập liệu mới nó ghi đè lên luôn dữ liệu cũ, nghĩa là mất luôn "tính lịch sử", hi. Bạn có nghĩ đến trường hợp bạn lỡ tay nhập vào ô đó 1 ngày tháng sai (nhập 1 ngày trong quá khứ hoặc nhập 1 ngày nào đó sai với hạn định kỳ,...), bạn Enter xong bạn phải nhập lại không? Lúc đó theo yêu cầu của bạn thì bên cột B- cột Mốc cũ- sẽ xử lý như thế nào với dữ liệu bạn đã nhập sai??
2. Ở cột B dữ liệu bạn nhập hơn chục ngày tháng nhưng nằm trong 1 ô thì nó cũng có vấn đề á. Vì 1 ô này nó sẽ ở dạng text, bạn sẽ rất khó tính toán với từng mốc thời gian trong đó (ví dụ: bạn sẽ không tính được khoảng thời gian giữa 2 mốc với nhau, nhỉ?!). Và, bây giờ xử lý theo yêu cầu của bạn thì phải "đối xử" với cái cột B đó như là text.
1594351827968.png
Bạn giải đáp ý 1 của mình đi rồi tính tiếp ! :)
 
Upvote 0
1. Bài của bạn mình thấy nó hơi "có vấn đề" ở dữ liệu cột C á (Mốc). Vì khi nhập liệu mới nó ghi đè lên luôn dữ liệu cũ, nghĩa là mất luôn "tính lịch sử", hi. Bạn có nghĩ đến trường hợp bạn lỡ tay nhập vào ô đó 1 ngày tháng sai (nhập 1 ngày trong quá khứ hoặc nhập 1 ngày nào đó sai với hạn định kỳ,...), bạn Enter xong bạn phải nhập lại không? Lúc đó theo yêu cầu của bạn thì bên cột B- cột Mốc cũ- sẽ xử lý như thế nào với dữ liệu bạn đã nhập sai??
2. Ở cột B dữ liệu bạn nhập hơn chục ngày tháng nhưng nằm trong 1 ô thì nó cũng có vấn đề á. Vì 1 ô này nó sẽ ở dạng text, bạn sẽ rất khó tính toán với từng mốc thời gian trong đó (ví dụ: bạn sẽ không tính được khoảng thời gian giữa 2 mốc với nhau, nhỉ?!). Và, bây giờ xử lý theo yêu cầu của bạn thì phải "đối xử" với cái cột B đó như là text.
View attachment 240869
Bạn giải đáp ý 1 của mình đi rồi tính tiếp ! :)
Cột mốc cũ có thể chỉnh sửa được mà. Tạm thời mình chỉ có giải pháp ghi text vào ô mốc cũ thôi. Bảng tính có nhiều con quá chia ra nữa thì có rất nhiều hàng. nếu bạn có giải pháp hay có thể tư vấn giúp ạ.
 
Upvote 0
Cột mốc cũ có thể chỉnh sửa được mà. Tạm thời mình chỉ có giải pháp ghi text vào ô mốc cũ thôi. Bảng tính có nhiều con quá chia ra nữa thì có rất nhiều hàng. nếu bạn có giải pháp hay có thể tư vấn giúp ạ.
Cột mốc cũ có thể chỉnh sửa được mà => này là bạn chỉnh sửa bằng tay. Vậy thì sao có thể can thiệp bằng công thức để xử lý yêu cầu "nếu ô ở cột mốc thay đổi thì giá trị trước khi thay đổi được cập nhật vào dòng cuối cùng của cột Mốc cũ" được?! 1 trong 2 thôi, nhập tay sẽ không xài công thức, xài công thức thì không nhập tay.

Bài của bạn mình đang suy nghĩ để giải quyết nhưng chưa ra, nó trên tầm của mình, híc, sorry bạn. Bạn chờ các anh chị khác vào giúp nhé !

ps: dữ liệu nhiều hàng thì không thành vấn đề nếu làm như vậy sẽ chuẩn hóa được dữ liệu. Bạn sẽ xử lý chúng dễ dàng hơn. Bạn hiểu ý mình chứ. Bạn xử lý, tính toán ngày tháng thì dữ liệu bạn xử lý nó ở dạng Date sẽ dễ dàng hơn nhiều so với dạng Text đúng không?! :)
 
Upvote 0
Cột mốc cũ có thể chỉnh sửa được mà => này là bạn chỉnh sửa bằng tay. Vậy thì sao có thể can thiệp bằng công thức để xử lý yêu cầu "nếu ô ở cột mốc thay đổi thì giá trị trước khi thay đổi được cập nhật vào dòng cuối cùng của cột Mốc cũ" được?! 1 trong 2 thôi, nhập tay sẽ không xài công thức, xài công thức thì không nhập tay.

Bài của bạn mình đang suy nghĩ để giải quyết nhưng chưa ra, nó trên tầm của mình, híc, sorry bạn. Bạn chờ các anh chị khác vào giúp nhé !

ps: dữ liệu nhiều hàng thì không thành vấn đề nếu làm như vậy sẽ chuẩn hóa được dữ liệu. Bạn sẽ xử lý chúng dễ dàng hơn. Bạn hiểu ý mình chứ. Bạn xử lý, tính toán ngày tháng thì dữ liệu bạn xử lý nó ở dạng Date sẽ dễ dàng hơn nhiều so với dạng Text đúng không?! :)
Đúng là chia ra thì dễ tính toán hơn. Mình sẽ tham khảo ý kiến của bạn.
Edit trong trường hợp nhập sai thôi.
Nếu chi ra thì bài toán sẽ khó hơn là cộng dồn vào 1 cell.
Mình có tham khảo sự kiện worksheet-change. dữ liệu bị thay đổi sẽ lưu vào biến target.
 
Upvote 0
Bạn copy code dưới đây để thử:
Viết công thức ở cột I hoặc một ô bất kì:
=S_CompareDate(C2,B2,True)
Và Fil xuống.

Giá trị True/False là Tự động giãn dòng
-------------------------------
JavaScript:
Option Explicit

#If VBA7 Then
  Private Declare PtrSafe Function SetTimer Lib "USER32" (ByVal hwnd As LongPtr, ByVal nIDEvent As LongPtr, ByVal uElapse As LongPtr, ByVal lpTimerFunc As LongPtr) As Long
  Private Declare PtrSafe Function KillTimer Lib "USER32" (ByVal hwnd As LongPtr, ByVal nIDEvent As LongPtr) As Long
#Else
  Private Declare Function SetTimer Lib "user32" (ByVal HWnd As Long, ByVal nIDEvent As Long, ByVal uElapse As Long, ByVal lpTimerFunc As Long) As Long
  Private Declare Function KillTimer Lib "user32" (ByVal HWnd As Long, ByVal nIDEvent As Long) As Long
#End If

'///////////////////////////////////////////////////////
#If Win64 Then
  Private gTimerID As LongPtr
#Else
  Private gTimerID As Long
#End If
'///////////////////////////////////////////////////////
Private CompareDateAgrs(), CompareDateIndex As Integer

Function S_CompareDate(Compare As Variant, _
                   ByVal WithCell As Excel.Range, _
          Optional ByVal AutoFitRow As Boolean = False) As String
  On Error Resume Next
  KillTimer 0&, gTimerID: gTimerID = 0
  '-----------------------------------------------
  S_CompareDate = VBA.Replace(VBA.Mid(Application.Caller.Formula, 2), "S_CompareDate", "S_CompareDate", , , 1)
  '-----------------------------------------------
  Dim UB As Integer, i As Integer, f As Integer, K As Integer, Fit, Arg
  Set WithCell = WithCell.Parent.Range(WithCell.Address)
  '-----------------------------------------------
  UB = UBound(CompareDateAgrs, 2): K = UB
  If K > 0 Then GoSub CheckIn
  If f = 0 Then K = K + 1:
  ReDim Preserve CompareDateAgrs(1 To 2, 1 To K)
  CompareDateAgrs(1, K) = Compare
  Set CompareDateAgrs(2, K) = WithCell
  CompareDateAgrs(3, K) = AutoFitRow
  gTimerID = SetTimer(0&, 0&, 0, AddressOf S_CompareDate_callback)
Exit Function
CheckIn:
  i = VBA.IIf(CompareDateIndex > 0 And CompareDateIndex <= K, CompareDateIndex, 1)
  For f = i To K
    If CompareDateAgrs(2, f).Worksheet Is WithCell.Worksheet Then
      If CompareDateAgrs(2, f).Address = WithCell.Address Then Return
    End If
  Next
  f = 0
Return
End Function
'///////////////////////////////////////////////////////
Private Sub S_CompareDate_callback()
  On Error Resume Next
  Static EarliestTime As Date, Procedure As String
  Procedure = "'" & ThisWorkbook.Name & "'!S_CompareDate_callback"
  Call KillTimer(0&, gTimerID): gTimerID = 0
  Call Application.OnTime(EarliestTime, Procedure, , False)
  '----------------------------------
  Dim UB As Integer
  UB = UBound(CompareDateAgrs, 2)
  If UB > 0 Then
    CompareDateIndex = CompareDateIndex + 1
    Dim a As Variant, b As String, SP() As String, i As Integer
    a = CompareDateAgrs(1, CompareDateIndex)
    If Not IsDate(a) Or a = "" Then Exit Sub
    a = Format(a, "dd/mm/yyyy")
    Debug.Print CDate(a)
    b = CompareDateAgrs(2, CompareDateIndex).Value
    SP = Split(b, Chr(10))
    For i = LBound(SP) To UBound(SP)
      If IsDate(SP(i)) Then
        If CDate(SP(i)) = CDate(a) Then : GoTo N
        If CDate(SP(i)) > CDate(a) Then
          SP(i) = CStr(a) & Chr(10) & SP(i): GoTo N
        End If
      End If
    Next
    SP(UBound(SP)) = SP(UBound(SP)) & Chr(10) & CStr(a)
N:  b = Join(SP, Chr(10))
    CompareDateAgrs(2, CompareDateIndex).Value = b
    If CompareDateAgrs(3, CompareDateIndex) Then
      CompareDateAgrs(2, CompareDateIndex).WrapText = True
      CompareDateAgrs(2, CompareDateIndex).EntireRow.AutoFit
    End If
    If CompareDateIndex >= UB Then
      Erase CompareDateAgrs: CompareDateIndex = 0
    Else
      EarliestTime = VBA.Now()
      Call Application.OnTime(EarliestTime, Procedure)
    End If
  End If
End Sub
 
Upvote 0
Bạn copy code dưới đây để thử:
Viết công thức ở cột I hoặc một ô bất kì:
=S_CompareDate(C2,B2,True)
Và Fil xuống.

Giá trị True/False là Tự động giãn dòng
-------------------------------
JavaScript:
Option Explicit

#If VBA7 Then
  Private Declare PtrSafe Function SetTimer Lib "USER32" (ByVal hwnd As LongPtr, ByVal nIDEvent As LongPtr, ByVal uElapse As LongPtr, ByVal lpTimerFunc As LongPtr) As Long
  Private Declare PtrSafe Function KillTimer Lib "USER32" (ByVal hwnd As LongPtr, ByVal nIDEvent As LongPtr) As Long
#Else
  Private Declare Function SetTimer Lib "user32" (ByVal HWnd As Long, ByVal nIDEvent As Long, ByVal uElapse As Long, ByVal lpTimerFunc As Long) As Long
  Private Declare Function KillTimer Lib "user32" (ByVal HWnd As Long, ByVal nIDEvent As Long) As Long
#End If

'///////////////////////////////////////////////////////
#If Win64 Then
  Private gTimerID As LongPtr
#Else
  Private gTimerID As Long
#End If
'///////////////////////////////////////////////////////
Private CompareDateAgrs(), CompareDateIndex As Integer

Function S_CompareDate(Compare As Variant, _
                   ByVal WithCell As Excel.Range, _
          Optional ByVal AutoFitRow As Boolean = False) As String
  On Error Resume Next
  KillTimer 0&, gTimerID: gTimerID = 0
  '-----------------------------------------------
  S_CompareDate = VBA.Replace(VBA.Mid(Application.Caller.Formula, 2), "S_CompareDate", "S_CompareDate", , , 1)
  '-----------------------------------------------
  Dim UB As Integer, i As Integer, f As Integer, K As Integer, Fit, Arg
  Set WithCell = WithCell.Parent.Range(WithCell.Address)
  '-----------------------------------------------
  UB = UBound(CompareDateAgrs, 2): K = UB
  If K > 0 Then GoSub CheckIn
  If f = 0 Then K = K + 1:
  ReDim Preserve CompareDateAgrs(1 To 2, 1 To K)
  CompareDateAgrs(1, K) = Compare
  Set CompareDateAgrs(2, K) = WithCell
  CompareDateAgrs(3, K) = AutoFitRow
  gTimerID = SetTimer(0&, 0&, 0, AddressOf S_CompareDate_callback)
Exit Function
CheckIn:
  i = VBA.IIf(CompareDateIndex > 0 And CompareDateIndex <= K, CompareDateIndex, 1)
  For f = i To K
    If CompareDateAgrs(2, f).Worksheet Is WithCell.Worksheet Then
      If CompareDateAgrs(2, f).Address = WithCell.Address Then Return
    End If
  Next
  f = 0
Return
End Function
'///////////////////////////////////////////////////////
Private Sub S_CompareDate_callback()
  On Error Resume Next
  Static EarliestTime As Date, Procedure As String
  Procedure = "'" & ThisWorkbook.Name & "'!S_CompareDate_callback"
  Call KillTimer(0&, gTimerID): gTimerID = 0
  Call Application.OnTime(EarliestTime, Procedure, , False)
  '----------------------------------
  Dim UB As Integer
  UB = UBound(CompareDateAgrs, 2)
  If UB > 0 Then
    CompareDateIndex = CompareDateIndex + 1
    Dim a As Variant, b As String, SP() As String, i As Integer
    a = CompareDateAgrs(1, CompareDateIndex)
    If Not IsDate(a) Or a = "" Then Exit Sub
    a = Format(a, "dd/mm/yyyy")
    Debug.Print CDate(a)
    b = CompareDateAgrs(2, CompareDateIndex).Value
    SP = Split(b, Chr(10))
    For i = LBound(SP) To UBound(SP)
      If IsDate(SP(i)) Then
        If CDate(SP(i)) = CDate(a) Then : GoTo N
        If CDate(SP(i)) > CDate(a) Then
          SP(i) = CStr(a) & Chr(10) & SP(i): GoTo N
        End If
      End If
    Next
    SP(UBound(SP)) = SP(UBound(SP)) & Chr(10) & CStr(a)
N:  b = Join(SP, Chr(10))
    CompareDateAgrs(2, CompareDateIndex).Value = b
    If CompareDateAgrs(3, CompareDateIndex) Then
      CompareDateAgrs(2, CompareDateIndex).WrapText = True
      CompareDateAgrs(2, CompareDateIndex).EntireRow.AutoFit
    End If
    If CompareDateIndex >= UB Then
      Erase CompareDateAgrs: CompareDateIndex = 0
    Else
      EarliestTime = VBA.Now()
      Call Application.OnTime(EarliestTime, Procedure)
    End If
  End If
End Sub
Cảm ơn bạn. code chạy ok. code phức tạp quá k biết gì để hỏi luôn
 
Upvote 0
Cảm ơn bạn. code chạy ok. code phức tạp quá k biết gì để hỏi luôn
Vì bạn không biết VBA nên không hiểu cơ chế của code trên.

Hiểu đơn giản:
1. Bắt sự kiện Cell thay đổi dữ liệu
2. Thêm công việc xét duyệt dữ liệu vào Mảng
3. Tách dữ liệu cần so sánh để đối chiếu với dữ liệu so sánh:
Sau khi duyệt mảng xong, kết thúc và xóa mảng, tiếp tục cho sự kiện cell thay đổi.

Cách trên là tôi viết theo lối tự động chạy.

Còn nếu bạn muốn click vào một Button mới thực hiện thì 15 đến 20 dòng code là đủ rồi.
 
Upvote 0
Bạn copy code dưới đây để thử:
Viết công thức ở cột I hoặc một ô bất kì:
=S_CompareDate(C2,B2,True)
Và Fil xuống.

Giá trị True/False là Tự động giãn dòng
-------------------------------
JavaScript:
Option Explicit

#If VBA7 Then
  Private Declare PtrSafe Function SetTimer Lib "USER32" (ByVal hwnd As LongPtr, ByVal nIDEvent As LongPtr, ByVal uElapse As LongPtr, ByVal lpTimerFunc As LongPtr) As Long
  Private Declare PtrSafe Function KillTimer Lib "USER32" (ByVal hwnd As LongPtr, ByVal nIDEvent As LongPtr) As Long
#Else
  Private Declare Function SetTimer Lib "user32" (ByVal HWnd As Long, ByVal nIDEvent As Long, ByVal uElapse As Long, ByVal lpTimerFunc As Long) As Long
  Private Declare Function KillTimer Lib "user32" (ByVal HWnd As Long, ByVal nIDEvent As Long) As Long
#End If

'///////////////////////////////////////////////////////
#If Win64 Then
  Private gTimerID As LongPtr
#Else
  Private gTimerID As Long
#End If
'///////////////////////////////////////////////////////
Private CompareDateAgrs(), CompareDateIndex As Integer

Function S_CompareDate(Compare As Variant, _
                   ByVal WithCell As Excel.Range, _
          Optional ByVal AutoFitRow As Boolean = False) As String
  On Error Resume Next
  KillTimer 0&, gTimerID: gTimerID = 0
  '-----------------------------------------------
  S_CompareDate = VBA.Replace(VBA.Mid(Application.Caller.Formula, 2), "S_CompareDate", "S_CompareDate", , , 1)
  '-----------------------------------------------
  Dim UB As Integer, i As Integer, f As Integer, K As Integer, Fit, Arg
  Set WithCell = WithCell.Parent.Range(WithCell.Address)
  '-----------------------------------------------
  UB = UBound(CompareDateAgrs, 2): K = UB
  If K > 0 Then GoSub CheckIn
  If f = 0 Then K = K + 1:
  ReDim Preserve CompareDateAgrs(1 To 2, 1 To K)
  CompareDateAgrs(1, K) = Compare
  Set CompareDateAgrs(2, K) = WithCell
  CompareDateAgrs(3, K) = AutoFitRow
  gTimerID = SetTimer(0&, 0&, 0, AddressOf S_CompareDate_callback)
Exit Function
CheckIn:
  i = VBA.IIf(CompareDateIndex > 0 And CompareDateIndex <= K, CompareDateIndex, 1)
  For f = i To K
    If CompareDateAgrs(2, f).Worksheet Is WithCell.Worksheet Then
      If CompareDateAgrs(2, f).Address = WithCell.Address Then Return
    End If
  Next
  f = 0
Return
End Function
'///////////////////////////////////////////////////////
Private Sub S_CompareDate_callback()
  On Error Resume Next
  Static EarliestTime As Date, Procedure As String
  Procedure = "'" & ThisWorkbook.Name & "'!S_CompareDate_callback"
  Call KillTimer(0&, gTimerID): gTimerID = 0
  Call Application.OnTime(EarliestTime, Procedure, , False)
  '----------------------------------
  Dim UB As Integer
  UB = UBound(CompareDateAgrs, 2)
  If UB > 0 Then
    CompareDateIndex = CompareDateIndex + 1
    Dim a As Variant, b As String, SP() As String, i As Integer
    a = CompareDateAgrs(1, CompareDateIndex)
    If Not IsDate(a) Or a = "" Then Exit Sub
    a = Format(a, "dd/mm/yyyy")
    Debug.Print CDate(a)
    b = CompareDateAgrs(2, CompareDateIndex).Value
    SP = Split(b, Chr(10))
    For i = LBound(SP) To UBound(SP)
      If IsDate(SP(i)) Then
        If CDate(SP(i)) = CDate(a) Then : GoTo N
        If CDate(SP(i)) > CDate(a) Then
          SP(i) = CStr(a) & Chr(10) & SP(i): GoTo N
        End If
      End If
    Next
    SP(UBound(SP)) = SP(UBound(SP)) & Chr(10) & CStr(a)
N:  b = Join(SP, Chr(10))
    CompareDateAgrs(2, CompareDateIndex).Value = b
    If CompareDateAgrs(3, CompareDateIndex) Then
      CompareDateAgrs(2, CompareDateIndex).WrapText = True
      CompareDateAgrs(2, CompareDateIndex).EntireRow.AutoFit
    End If
    If CompareDateIndex >= UB Then
      Erase CompareDateAgrs: CompareDateIndex = 0
    Else
      EarliestTime = VBA.Now()
      Call Application.OnTime(EarliestTime, Procedure)
    End If
  End If
End Sub
Nhìn code khủng quá, bạn làm nghề lập trình hả
 
Upvote 0
Web KT
Back
Top Bottom