Chuyên đề giải đáp những thắc mắc về code VBA

Liên hệ QC

maytinhvp01

Thành viên thường trực
Tham gia
27/7/13
Bài viết
390
Được thích
179
Mình muốn nhờ giải thich câu lệnh " If Ran.Cells(d, c) > max Then max = Ran.Cells(d, c) "
trong ví du:
Public Function LonNhat(Ran As Range)
Dim max As Double, v As Integer, d As Integer, c As Integer
max = Ran.Cells(1, 1)
For d = 1 To Ran.Rows.Count
For c = 1 To Ran.Columns.Count
If Ran.Cells(d, c) > max Then max = Ran.Cells(d, c)
Next c
Next d
v = Tim(max, Ran)
LonNhat = max
End Function
-------------------------------------------------------
[INFO1]Thông báo:
Vì topic này:
http://www.giaiphapexcel.com/forum/...ải-thích-các-code-đề-nghị-các-bạn-gửi-vào-đây
đã quá dài nên BQT đóng lại.
Nay tôi mở topic mới với cùng chủ đề: GIẢI THÍCH NHỮNG THẮC MẮC VỀ CODE
Các bạn nếu có nhu cầu giải thích code, vui lòng post tại đây nhé
NDU96081631

[/INFO1]
 
Chỉnh sửa lần cuối bởi điều hành viên:
Nếu dữ liệu của cột 1 & dữ liệu dòng 1 là 2 loại khác nhau thì sau khi nạp các key là source(i,1), bạn tiếp tục nạp thêm các key là source(1,j) với item là chỉ số cột --> quá trình tra cứu sẽ không phải dùng vòng lặp
nạp thêm các key là source(1,j)? source(1,j) này là số liệu có thể giống nhau như vậy mình đâu add nó thành key được nhỉ. Hay mình chưa hiểu đúng ý bạn?

Em đính kèm file mọi người xem giúp em nhé.
 

File đính kèm

  • Check working hour Ver1.xlsb
    958.7 KB · Đọc: 5
Lần chỉnh sửa cuối:
Upvote 0
Đúng rồi bạn. Mình nhầm, cái này luôn luôn khác nhau. Nhưng sang dòng tiếp theo thì vẫn xét chung cái dòng này mà.
source(i,1) luôn luôn khác nhau, nhưng cái source(1,j) thì là dùng chung cho toàn bộ source(i,1)
Bạn thử code dưới xem sao
Mã:
Option Compare Text
Public Sub SosanhbangDic()
    Dim Dic As Object, ID As String, Tem As String
    Dim R As Long, i As Long, j As Long, k As Long
    Dim Source1(), Result(1 To 10000, 1 To 8)
    
    Dim Source0()
    Dim x, z, t
    
    Set Dic = CreateObject("Scripting.Dictionary")
    With Sheets("BP")
        Source0 = .Range("B4", .Range("B65000").End(xlUp)).Resize(, 134).Value
        For i = 5 To UBound(Source0, 1)
            ID = Source0(i, 1)
            If Len(ID) > 0 Then
                For j = 10 To 133
                    'Tem = Source0(i, 1) & "-" & Source0(1, j)
                    'Dic.Add Tem, Source0(i, j) 'Gan key la ID-Ngay_Dulieucong, Item la Dulieucong
                    
                    Dic.Add Source0(i, 1), i
                    Dic.Add Source0(1, j), j
                Next j
            End If
        Next i
    End With
    With Sheets("HR")
        Source1 = .Range("B4", .Range("B65000").End(xlUp)).Resize(, 134).Value
        For i = 5 To UBound(Source1, 1)
            For j = 10 To 133
                'Tem = Source1(i, 1) & "-" & Source1(1, j)
                'If Dic.exists(Tem) Then  'Neu khong ton tai key la ID-Ngay_Dulieucong
                If Dic.exists(Source1(i, 1)) And Dic.exists(Source1(1, j)) Then
                    x = Dic.Item(Source1(i, 1))
                    z = Dic.Item(Source1(1, j))
                    
                    'If Source1(i, j) <> Dic.Item(Tem) Then
                    If Source1(i, j) <> Source0(x, z) Then
                        k = k + 1
                        Result(k, 1) = Source1(i, 1) 'ID
                        Result(k, 2) = Source1(1, j) 'Cot
                        Result(k, 3) = Source1(2, j) 'Ngay check
                        Result(k, 4) = Day(Source1(2, j)) 'Ngay
                        Result(k, 5) = Source1(4, j) 'Noi dung check
                        Result(k, 6) = Source1(i, j)
                        
                        'Result(k, 7) = Dic.Item(Tem)
                        Result(k, 7) = Source0(x, z)
                        
                        Result(k, 8) = Source1(4, j)
                    End If
                    'Dic.Remove Tem
                End If
            Next j
        Next i
    End With
    Sheets("Report").Range("B5").CurrentRegion.Offset(2, 0).ClearContents
    Sheets("Report").Range("B5").Resize(UBound(Result, 1), 8) = Result
    If UBound(Dic.Keys, 1) > 0 Then Sheets("Report").Range("J2").Resize(1, UBound(Dic.Keys)) = Dic.Keys
    Set Dic = Nothing
End Sub
 
Upvote 0
Bạn thử code dưới xem sao
Mã:
Option Compare Text
Public Sub SosanhbangDic()
    Dim Dic As Object, ID As String, Tem As String
    Dim R As Long, i As Long, j As Long, k As Long
    Dim Source1(), Result(1 To 10000, 1 To 8)
  
    Dim Source0()
    Dim x, z, t
  
    Set Dic = CreateObject("Scripting.Dictionary")
    With Sheets("BP")
        Source0 = .Range("B4", .Range("B65000").End(xlUp)).Resize(, 134).Value
        For i = 5 To UBound(Source0, 1)
            ID = Source0(i, 1)
            If Len(ID) > 0 Then
                For j = 10 To 133
                    'Tem = Source0(i, 1) & "-" & Source0(1, j)
                    'Dic.Add Tem, Source0(i, j) 'Gan key la ID-Ngay_Dulieucong, Item la Dulieucong
                  
                    Dic.Add Source0(i, 1), i
                    Dic.Add Source0(1, j), j
                Next j
            End If
        Next i
    End With
    With Sheets("HR")
        Source1 = .Range("B4", .Range("B65000").End(xlUp)).Resize(, 134).Value
        For i = 5 To UBound(Source1, 1)
            For j = 10 To 133
                'Tem = Source1(i, 1) & "-" & Source1(1, j)
                'If Dic.exists(Tem) Then  'Neu khong ton tai key la ID-Ngay_Dulieucong
                If Dic.exists(Source1(i, 1)) And Dic.exists(Source1(1, j)) Then
                    x = Dic.Item(Source1(i, 1))
                    z = Dic.Item(Source1(1, j))
                  
                    'If Source1(i, j) <> Dic.Item(Tem) Then
                    If Source1(i, j) <> Source0(x, z) Then
                        k = k + 1
                        Result(k, 1) = Source1(i, 1) 'ID
                        Result(k, 2) = Source1(1, j) 'Cot
                        Result(k, 3) = Source1(2, j) 'Ngay check
                        Result(k, 4) = Day(Source1(2, j)) 'Ngay
                        Result(k, 5) = Source1(4, j) 'Noi dung check
                        Result(k, 6) = Source1(i, j)
                      
                        'Result(k, 7) = Dic.Item(Tem)
                        Result(k, 7) = Source0(x, z)
                      
                        Result(k, 8) = Source1(4, j)
                    End If
                    'Dic.Remove Tem
                End If
            Next j
        Next i
    End With
    Sheets("Report").Range("B5").CurrentRegion.Offset(2, 0).ClearContents
    Sheets("Report").Range("B5").Resize(UBound(Result, 1), 8) = Result
    If UBound(Dic.Keys, 1) > 0 Then Sheets("Report").Range("J2").Resize(1, UBound(Dic.Keys)) = Dic.Keys
    Set Dic = Nothing
End Sub
Cột theo thứ tự, không cần add cột
Có thể thứ tự dòng giống nhau, không cần Dic
 
Lần chỉnh sửa cuối:
Upvote 0
Bạn thử code dưới xem sao
Mã:
Option Compare Text
Public Sub SosanhbangDic()
    Dim Dic As Object, ID As String, Tem As String
    Dim R As Long, i As Long, j As Long, k As Long
    Dim Source1(), Result(1 To 10000, 1 To 8)
  
    Dim Source0()
    Dim x, z, t
  
    Set Dic = CreateObject("Scripting.Dictionary")
    With Sheets("BP")
        Source0 = .Range("B4", .Range("B65000").End(xlUp)).Resize(, 134).Value
        For i = 5 To UBound(Source0, 1)
            ID = Source0(i, 1)
            If Len(ID) > 0 Then
                For j = 10 To 133
                    'Tem = Source0(i, 1) & "-" & Source0(1, j)
                    'Dic.Add Tem, Source0(i, j) 'Gan key la ID-Ngay_Dulieucong, Item la Dulieucong
                  
                    Dic.Add Source0(i, 1), i
                    Dic.Add Source0(1, j), j
                Next j
            End If
        Next i
    End With
    With Sheets("HR")
        Source1 = .Range("B4", .Range("B65000").End(xlUp)).Resize(, 134).Value
        For i = 5 To UBound(Source1, 1)
            For j = 10 To 133
                'Tem = Source1(i, 1) & "-" & Source1(1, j)
                'If Dic.exists(Tem) Then  'Neu khong ton tai key la ID-Ngay_Dulieucong
                If Dic.exists(Source1(i, 1)) And Dic.exists(Source1(1, j)) Then
                    x = Dic.Item(Source1(i, 1))
                    z = Dic.Item(Source1(1, j))
                  
                    'If Source1(i, j) <> Dic.Item(Tem) Then
                    If Source1(i, j) <> Source0(x, z) Then
                        k = k + 1
                        Result(k, 1) = Source1(i, 1) 'ID
                        Result(k, 2) = Source1(1, j) 'Cot
                        Result(k, 3) = Source1(2, j) 'Ngay check
                        Result(k, 4) = Day(Source1(2, j)) 'Ngay
                        Result(k, 5) = Source1(4, j) 'Noi dung check
                        Result(k, 6) = Source1(i, j)
                      
                        'Result(k, 7) = Dic.Item(Tem)
                        Result(k, 7) = Source0(x, z)
                      
                        Result(k, 8) = Source1(4, j)
                    End If
                    'Dic.Remove Tem
                End If
            Next j
        Next i
    End With
    Sheets("Report").Range("B5").CurrentRegion.Offset(2, 0).ClearContents
    Sheets("Report").Range("B5").Resize(UBound(Result, 1), 8) = Result
    If UBound(Dic.Keys, 1) > 0 Then Sheets("Report").Range("J2").Resize(1, UBound(Dic.Keys)) = Dic.Keys
    Set Dic = Nothing
End Sub
Bình bổ sung thêm đoạn này có đúng không nhỉ

Mã:
         If Not Dic.exists(Source0(i, 1)) Then
                        Dic.Add Source0(i, 1), i
                    End If
                    If Not Dic.exists(Source0(1, j)) Then
                        Dic.Add Source0(1, j), j
                    End If

Vì mình thấy nó báo lỗi.
Cảm ơn bạn, code mới xử lý rất nhanh :)
 
Upvote 0
Các bạn cho mình hỏi chút Hàm Application.Transpose(Cells) bị giới hạn khi chuyển 255 ký tự trên 1 Cells
vậy có cách nào khác viết Hàm tự tạo chuyển mãng trên 300 ký tự/1 Cells ko nhỉ
 
Upvote 0
Các bạn cho mình hỏi chút Hàm Application.Transpose(Cells) bị giới hạn khi chuyển 255 ký tự trên 1 Cells
vậy có cách nào khác viết Hàm tự tạo chuyển mãng trên 300 ký tự/1 Cells ko nhỉ
Viết một hàm chuyển mảng cột thành dòng, dòng thành cột.
Hàm nhận một Variant. Xét type của Variant đó, nếu là Range thì chuyển thành mảng.
Hàm trả về một mảng là transpose của mảng đầu vào.

Đại khái code chuyển dòng thành cột, từ mảng a sang mảng b:
rowLo = LBound(a)
rowHi = UBound(a)
colLo = LBound(a, 2)
colHi = UBound(a, 2)
For i = rowILo To rowHi
For j = colLo To colHi
b(j, i) = a(i, j)
Next j
Next i
Nếu mảng rất lớn thì có thể cải tiến tốc đọ bằng cách duyệt cột vòng ngài, dòng vòng trong.
(tôi nhớ có người từng chứng minh rằng VBA dùng cấu trúc mảng xoay theo cột, tức là xoay theo chiều cuối cùng. Nếu đúng vậy thì duyệt cột nhanh hơn dòng. Nhưng nếu tôi nhớ lầm thì xin lỗi mọi người, lầm thôi. Bi giờ lười lục lại bài ấy để xem quá)
 
Upvote 0
Viết một hàm chuyển mảng cột thành dòng, dòng thành cột.
Hàm nhận một Variant. Xét type của Variant đó, nếu là Range thì chuyển thành mảng.
Hàm trả về một mảng là transpose của mảng đầu vào.

Đại khái code chuyển dòng thành cột, từ mảng a sang mảng b:
rowLo = LBound(a)
rowHi = UBound(a)
colLo = LBound(a, 2)
colHi = UBound(a, 2)
For i = rowILo To rowHi
For j = colLo To colHi
b(j, i) = a(i, j)
Next j
Next i
Nếu mảng rất lớn thì có thể cải tiến tốc đọ bằng cách duyệt cột vòng ngài, dòng vòng trong.
(tôi nhớ có người từng chứng minh rằng VBA dùng cấu trúc mảng xoay theo cột, tức là xoay theo chiều cuối cùng. Nếu đúng vậy thì duyệt cột nhanh hơn dòng. Nhưng nếu tôi nhớ lầm thì xin lỗi mọi người, lầm thôi. Bi giờ lười lục lại bài ấy để xem quá)
Mới hỏi xong thì mò Google 1 chút cũng ra cách này ...
Cho Mạnh mở rộng hỏi thêm chút là có cách nào nhanh nhất tăng tốc chuyển một Mảng ADO rất lớn không ... ý hỏi Tốc độ nhanh nhất có thể ấy
VD: cùng dữ liệu như nhau mà cái nào về trước thì nó ok ... còn lại không quan tâm cách gì !
Mã:
Sub Transpose_Data()
    Set cn = CreateObject("ADODB.Connection")
    Set rs = CreateObject("ADODB.Recordset")
    '.....
    Set rs = cn.Execute(srtQry)
    tmpArray = rs.GetRows
    cn.Close
    tmpArray2 = transposeArray(tmpArray)
End Sub
Viết cái Hàm transposeArray chạy nhanh nhất có thể
 
Lần chỉnh sửa cuối:
Upvote 0
...Viết cái Hàm transposeArray chạy nhanh nhất có thể
Nếu bạn không cần gọi hàm trên bảng tính thì viết cái sub nhanh hơn
Sub XoayMang(a(), b())
' sub xoay mảng (transpose) a qua b
code bài #2449 ở đây (tuy nhiên, tôi không bảo đảm nó "nhanh nhất")
quan trọng: code bài #2449 tự cho rằng hai mảng a và b có LBound giống nhau
End Sub
Code gọi sub tự mình dựng cái mảng b và chuyển cho sub để nhận kết quả.
Lưu ý: với sub này, b có thể lớn hơn a. Nếu b lớn hơn a thì nó sẽ giữ lại các trị bên ngoài a.
Nếu muốn b có thể nhỏ hơn a thì viết thêm code giới hạn: Max cột là Min của dòng a và cột b; Min dòng là Min của cột a và dòng b

Nếu phải gọi hàm trên bảng tính thì viết cái hàm gọi sub này. Tuy nhiên, hàm phải qua mọt giai đoạn copy mảng cho nên chậm hơn (một vài phần ngàn giây).
Function TransposeArray(a())
Dim b()
Redim b(LBound(a, 2) To UBound(a, 2), LBound(a, 1) To UBound(a, 1))
XoayMang(a, b)
TransposeArray = b
End Function
 
Upvote 0
Nếu bạn không cần gọi hàm trên bảng tính thì viết cái sub nhanh hơn
Sub XoayMang(a(), b())
' sub xoay mảng (transpose) a qua b
code bài #2449 ở đây (tuy nhiên, tôi không bảo đảm nó "nhanh nhất")
quan trọng: code bài #2449 tự cho rằng hai mảng a và b có LBound giống nhau
End Sub
Code gọi sub tự mình dựng cái mảng b và chuyển cho sub để nhận kết quả.
Lưu ý: với sub này, b có thể lớn hơn a. Nếu b lớn hơn a thì nó sẽ giữ lại các trị bên ngoài a.
Nếu muốn b có thể nhỏ hơn a thì viết thêm code giới hạn: Max cột là Min của dòng a và cột b; Min dòng là Min của cột a và dòng b

Nếu phải gọi hàm trên bảng tính thì viết cái hàm gọi sub này. Tuy nhiên, hàm phải qua mọt giai đoạn copy mảng cho nên chậm hơn (một vài phần ngàn giây).
Function TransposeArray(a())
Dim b()
Redim b(LBound(a, 2) To UBound(a, 2), LBound(a, 1) To UBound(a, 1))
XoayMang(a, b)
TransposeArray = b
End Function
Mai rảnh thử viết kiểu Sub xem Sao ... chuyển từ mảng A sang B
Mã:
Public Sub TransposeArr(ByRef A() As Variant, ByRef B() As Variant)
''............
''............
End Sub
 
Upvote 0
Nếu mảng rất lớn thì có thể cải tiến tốc đọ bằng cách duyệt cột vòng ngài, dòng vòng trong.
(tôi nhớ có người từng chứng minh rằng VBA dùng cấu trúc mảng xoay theo cột, tức là xoay theo chiều cuối cùng. Nếu đúng vậy thì duyệt cột nhanh hơn dòng. Nhưng nếu tôi nhớ lầm thì xin lỗi mọi người, lầm thôi. Bi giờ lười lục lại bài ấy để xem quá)
Delphi ghi mảng theo từng dòng, VBA ghi theo từng cột. Vì thế có thể thay vì duyệt từng ô của cột thì dùng CopyMemory (hàm API) để copy trong 1 nốt nhạc. Nhưng nếu là mảng các Variant (String) thì để viết chuẩn không phải dễ. Vì rất dễ rò rỉ bộ nhớ, code không chuẩn.

Ngày xưa siwtom cũng từng tham gia chủ đề của Nguyễn Duy Tuân. Đại khái cứ tìm với từ khóa "modFastArray" là ra.
 
Lần chỉnh sửa cuối:
Upvote 0
Delphi ghi mảng theo từng dòng, VBA ghi theo từng cột. Vì thế có thể thay vì duyệt từng ô của cột thì dùng CopyMemory (hàm API) để copy trong 1 nốt nhạc. Nhưng nếu là mảng các Variant (String) thì để viết chuẩn không phải dễ. Vì rất dễ rò rỉ bộ nhớ, code không chuẩn.

Ngày xưa siwtom cũng từng tham gia chủ đề của Nguyễn Duy Tuân. Đại khái cứ tìm với từ khóa "modFastArray" là ra.
Bài đó em đọc tới nhiều nhiều rồi ... hôm thớt @thuyyeu99 có hỏi anh cũng có giải thích ... Em cũng đọc tới lui rồi mà nó cứ như gà mắc tắc vậy
Anh cho Em xin 1 code đơn giản nhất như 1+1=2 đi em hình dung ra xong mới học được
cảm ơn Anh
 
Upvote 0
Bài đó em đọc tới nhiều nhiều rồi ... hôm thớt @thuyyeu99 có hỏi anh cũng có giải thích ... Em cũng đọc tới lui rồi mà nó cứ như gà mắc tắc vậy
Anh cho Em xin 1 code đơn giản nhất như 1+1=2 đi em hình dung ra xong mới học được
cảm ơn Anh
Tôi chỉ đoán ý bác VetMini và nghĩ đang nói tới "cái này" thôi. Còn chuyện áp dụng vào vấn đề của bạn, và có áp dụng được không, thì tôi không có ý kiến. Tôi không tham gia chủ đề xoay mảng của bạn. Chỉ là bình luận bên lề bài của bác VetMini thôi.
 
Upvote 0
sử dụng Hàm API CopyMemory Tốc độ nhanh thật ... Nghiên cứu cái vụ chuyển mảng nếu thành công thì quá tốt :p
Nó ứng dụng nhiều thứ đấy chứ nhỉ
Mã:
Private Declare Sub CopyMemory Lib "kernel32.dll" Alias "RtlMoveMemory" (ByRef Destination As Any, ByRef Source As Any, ByVal Length As Long)

Private Function JoinIt(stringArray() As String, sJoinChars As String) As String
    Dim lJoinLen As Long, lSize As Long
    Dim joinPtr As Long, newStrPtr As Long
    Dim Z As Long
    
    lJoinLen = LenB(sJoinChars)
    ' count first
    For Z = 0 To UBound(stringArray)
       lSize = lSize + LenB(stringArray(Z))
    Next
    JoinIt = String$((lSize + (UBound(stringArray) * lJoinLen)) \ 2, vbNullChar)
    If LenB(JoinIt) Then
         newStrPtr = StrPtr(JoinIt)
         joinPtr = StrPtr(sJoinChars)
         ' transfer
         For Z = 0 To UBound(stringArray) - 1
             lSize = LenB(stringArray(Z))
             If lSize Then
                 CopyMemory ByVal newStrPtr, ByVal StrPtr(stringArray(Z)), lSize
                 newStrPtr = newStrPtr + lSize
             End If
             If lJoinLen Then
                 CopyMemory ByVal newStrPtr, ByVal joinPtr, lJoinLen
                 newStrPtr = newStrPtr + lJoinLen
             End If
         Next
         ' handle last array item separately, no join char appended
         lSize = LenB(stringArray(Z))
         If lSize Then CopyMemory ByVal newStrPtr, ByVal StrPtr(stringArray(Z)), lSize
    End If
End Function

Private Sub Command1_Click()
    Dim s(0 To 25) As String
    Dim v As Long
    For v = 1 To 26
        s(v - 1) = Chr$(v + 64)
    Next
    Debug.Print JoinIt(s(), "(:)")
End Sub
 
Lần chỉnh sửa cuối:
Upvote 0
memmove, memcpy, memset, và memcmp là 4 hàm macro căn bản của C viết bằng mã máy và rất ưu việt. Bởi vì chúng hoạt động trên cả vùng nhớ xác định và liên tục.
Tôi không dùng COM và API nhiều nhưng không lấy làm lạ khi mấy hàm này được MS đưa vào API's

Sử dụng chúng thì có mấy điểm sau đây cần lưu ý:
1. mảng là một vùng nhớ liên tục, không chấp nhận chuyện đứt quãng.
2. C là ngôn ngữ hoạt động mạnh nhất khì sử dụng con trỏ. Vì vậy các hàm này được viết với người sử dụng con trỏ là mục đích.
3. hai tính chất trên cũng xác định rằng bước vào vùng nhớ của mảng thì không còn khái niệm dòng cột gì cả. Việc xác định phần tử dòng nào, cột nào thuộc về code gọi hàm.

Tính chất 2 ở trên là điểm mà các bạnn chưa quen với con trỏ cần để ý.
Mảng Variant là mảng chỉ có địa chỉ (tức các con trỏ) của các phần tử được chứa trong mảng. Trị của từng phần tử được chứa nơi khác. Khi đụng vào loại mảng này, quý vị phải coi chừng bị rò rỉ bộ nhớ. Bộ phận dọn rác (garbage collector) cảu VBA chỉ dọn theo tính chất biến mà nó biết. Bạn thay đổi con trỏ tầng thứ nhì (secondary reference) vào chỗ khác hoặc thay đổi kích thước vùng nhớ bằng cách đi vòng thì chưa chắc VBA biết. Và dẫn đến việc rò rỉ bộ nhớ (memory leak, chương trình hoặc hệ thống bị đứng) hoặc tệ hơn bị chồng vùng nhớ (memory overlap, dữ liệu bị sai).
Điều này bạn "bờ mờ số một" đã cảnh báo quý vị mỗi khi nhắc đến mấy cái hàm mem's này.
 
Upvote 0
Xin chào các Bạn,
Giả thiết OT tạo module đặt tên là "KhaiBao"

Trong modude khai Báo OT có nhiều dòng
Public Const ....

Nếu có 2 biến trùng nhau, ví dụ:
Public Const sName As String = "OT"
....
Public Const sName As String = "Oanh_Tho"

Vậy có cách nào để mình biết được việc khai báo trùng này không ạ?
 
Upvote 0
Xin chào các Bạn,
Giả thiết OT tạo module đặt tên là "KhaiBao"

Trong modude khai Báo OT có nhiều dòng
Public Const ....

Nếu có 2 biến trùng nhau, ví dụ:
Public Const sName As String = "OT"
....
Public Const sName As String = "Oanh_Tho"

Vậy có cách nào để mình biết được việc khai báo trùng này không ạ?
Bạn thử chạy chương trình xem nó có báo lỗi chỗ khai báo không? Làm gì có khai báo 2 lần trong một sub
 
Upvote 0
Web KT
Back
Top Bottom