Tổng hợp về phương thức tìm kiếm FIND (Find Method) (1 người xem)

Liên hệ QC

Người dùng đang xem chủ đề này

hoangdanh282vn

Nguyễn Cảnh Hoàng Danh
Thành viên danh dự
Tham gia
21/12/07
Bài viết
1,902
Được thích
5,306
Nghề nghiệp
Kinh doanh các mặt hàng văn phòng phẩm
Find Method

Phương thức Find tìm kiếm thông tin trong một vùng nào đó, kết quả trả về là ô đầu tiên chứa đựng thông tin được tìm thấy. Nếu không tìm thấy thông tin trong vùng tìm kiếm thì phương thức Find sẽ trả về Nothing.

Cú pháp phương thức như sau :

Expression.Find(What, After, LookIn, LookAt, SearchOrder, SearchDirection, MatchCase, MatchByte, SearchFormat)

Expression : Vùng tìm kiếm. Biểu thức Expression phải được khai báo và nó trả về một đối tượng Range.

What : Dữ liệu hay thông tin cần tìm kiếm. Đây là một đòi hỏi bắt buộc phải khai báo. Dữ liệu tìm kiếm có thể là một chuỗi ký tự hay một dạng dữ liệu nào đó có trong Excel và được khai báo dưới dạng Variant.

After : Ô được chọn để xác định vị trí tìm kiếm, là tùy chọn, được khai báo dưới dạng Variant, có thể bỏ qua. Việc tìm kiếm sẽ bắt đầu từ ô này. Ô này tương ứng với vị trí của ô hiện hành sau khi việc tìm kiếm hoàn tất. Ô xác định vị trí tìm kiếm này sẽ không được đưa vào quá trình tìm kiếm trừ khi vùng tìm kiếm bao gồm cả ô này. Nếu đối số này không được khai báo thì việc tìm kiếm sẽ bắt đầu sau ô trên cùng bên trái của vùng cần tìm kiếm.

LookIn : Không bắt buộc, khai báo dạng Variant. Đây là một dạng thông tin và thường có giá trị là xlValues.

LookAt : Cách thức tìm kiếm. Là đối số tùy chọn, không bắt buộc, khai báo dưới dạng Variant. LookAt có 2 giá trị : xlWhole (Tìm toàn bộ) hoặc xlPart (tìm một phần).

SearchOrder : Xác định dạng thứ tự tìm kiếm, là tùy chọn không bắt buộc, khai báo dưới dạng Variant. SearchOrder có 2 dạng ứng với 2 hàng số : xlByRows (theo thứ tự dòng) hoặc xlByColumns (theo thứ tự cột)

SearchDirection : Hướng tìm kiếm, là tùy chọn. SearchDirection có 2 dạng ứng với 2 hằng số : xlNext (Tìm kế tiếp, là giá trị mặc định), xlPrevious (Tìm trước đó)

MatchCase : Là tùy chọn để xác định kiểu tìm kiếm có phân biệt chữ Hoa với chữ thường, khai báo dưới dạng Variant. Khai báo là True nếu ta muốn tìm kiếm chính xác. Giá trị mặc định là False.( Không phân biệt chữ in Hoa với chữ thường)

MatchByte : Không bắt buộc, khai báo dạng Variant. Chỉ sử dụng khi ta đã chọn hoặc cài đặt bộ hỗ trợ ngôn ngữ ký tự byte kép. Là True nếu ứng với bộ ký tự byte kép, False nếu ứng với bộ ký tự byte đơn.

SearchFormat : Tìm kiếm theo định dạng. Là tham số tùy chọn, khai báo dưới dạng Variant.

Lưu ý :

- Các thiết lập cho các đối số LookIn, LookAt, SearchOrder MatchByte sẽ được lưu mỗi lần ta sử dụng phương thức này (phương thức Find). Nếu ta không khai báo giá trị cho các đối số vào lần sử dụng phương thức Find tiếp theo, các giá trị thiết lập đã lưu trước đó sẽ được sử dụng. Việc thiết lập các đối số này làm thay đổi các tùy chọn thiết lập trong hộp thoại Find, và việc thay đổi các thiết lập trong hộp thoại Find sẽ làm thay đổi các giá trị đã lưu – là những giá trị được sử dụng nếu ta bỏ qua các đối số này. Để tránh xảy ra việc này, ta nên khai báo các đối số một cách rõ ràng mỗi lần sử dụng phương thức Find này.

- Ta có thể dùng phương thức FindNextFindPrevious để lặp lại việc tìm kiếm. Khi đến vị trí cuối của vùng tìm kiếm được xác định trước đó, excel sẽ bao phủ từ vị trí này đến vị trí đầu tiên của vùng tìm kiếm. Để ngưng việc tìm kiếm ngay khi động tác bao phủ này xảy ra, hãy lưu lại địa chỉ của ô đầu tiên tìm được, sau đó thử so sánh lần lượt mỗi địa chỉ ô được tìm thấy kế tiếp với địa chỉ ô vừa được lưu này.

============================@@@===========================​

Bài viết này tham khảo dựa trên bài dịch nguyên bản của anh Ca_dafi
(Còn tiếp)
 

File đính kèm

Các ví dụ minh họa về Find Method


Áp dụng : Để hiểu rõ hơn về phương thức tìm kiếm Find, ta xét một vài ví dụ minh họa sau :

1.> Ta có dữ liệu như sau :
Hinh1.jpg


Ta sẽ dùng phương thức Find để tìm ra trong vùng A1:A10 những ô nào có ký tự 1
Ta dùng chuột bôi đen vùng A1:A10 và cho chạy Macro sau :
PHP:
Sub Test1()
    Dim Rng As Range
    Set Rng = Selection.Find("1*", LookIn:=xlValues, LookAt:=xlWhole)
    If Not Rng Is Nothing Then
        Do
            MsgBox Rng.Address
            Set Rng = Selection.FindNext(Rng)
        Loop
    End If
End Sub
Lần lượt các kết quả của việc chạy Macro như sau :
hinh2.jpg

Diễn giải về trình tự hoạt động của Macro như sau :

- Tìm trong vùng từ ô A2 đến ô A10 xem ô nào có chứa ký tự 1. Kết quả là ô A3 được tìm thấy đầu tiên và sẽ gán biến Rng bằng ô A3. Các bạn sẽ hỏi là tại sao không tìm bắt đầu từ ô A1 và tại sao không trả về ô A1 đầu tiên mà lại là ô A3. Các bạn xem trong cấu trúc của phương thức Find thì sẽ thấy có một đối số tùy chọn, đó là After. Đây là tham số dùng để xác định vị trí bắt đầu của việc tìm kiếm, việc tìm kiếm sẽ bắt đầu tại vị trí sau ô này. Nếu không khai báo thì việc tìm kiếm sẽ bắt đầu ngay sau ô đầu tiên bên trái của vùng tìm kiếm. Như VD trên thì vùng tìm kiếm Selection (A1:A10), do không khai báo nên việc tìm kiếm sẽ bắt đầu từ ô A2 và ô A3 được tìm thấy đầu tiên.

- Tham số What trong phương thức Find ở VD trên sử dụng thêm ký tự “*” để tìm gần đúng (chuỗi bắt đầu bởi số 1). Bỏ dầu “*” ta sẽ tìm chính xác.

- Nếu như không tìm thấy ô nào trong vùng Selection có chứa số 1 (Biến Rng = Nothing) thì sẽ thoát khỏi thủ tục. Ngược lại, sẽ bắt đầu vào vòng lặp tìm kiếm Do Loop.

- Sau khi tìm thấy và gán biến Rng cho ô A3 thì hiện hộp thoại thông báo về địa chỉ của ô vừa được tìm thấy ( biến Rng). Khi đó tham số After sẽ là ô A3 và việc tìm kiếm sẽ bắt đầu sau ô này.

- Tiếp theo ta sẽ dùng phương thức FindNext(Rng) để tìm đến ô chứa số 1 tiếp theo và ô A5 được tìm thấy, biến Rng sẽ là ô A5 . Quá trình cứ tiếp tục như thế cho đến ô A10.

- Khi dò tìm đến ô A10 thì quá trình dò tìm sẽ được lặp lại như lúc ban đầu, tức là sẽ tìm kiếm lặp lại từ ô A1 đến ô A10. Nhưng khi đó tham số After sẽ là ô A10 và lúc này ô A1 sẽ được tìm thấy. Và tiếp đến là ô A3, A5…sẽ tìm kiếm mãi mãi.

- Khi gán biến Rng bằng ô A5 thì đến từ khóa Loop. Khi gặp từ khóa này thì quá trình tiềm kiếm sẽ được tiếp tục, đến từ khóa Do, hiện hộp thông báo và lại gán biến Rng bằng ô A6….



Đoạn Code trên còn 2 vấn đề cần khắc phục, đó là làm sao để tìm thấy được ô A1 đầu tiên và sẽ kết thúc quá trình tìm kiếm khi dò tìm đến ô A10.

Đoạn Code sau sẽ khắc phục điều này :

PHP:
Sub Test2()
    Dim Rng As Range, LastCell As Range, FirstAddress As String
    Set LastCell = Selection.Cells(Selection.Cells.Count)
    Set Rng = Selection.Find("1*", After:=LastCell, LookIn:=xlValues, LookAt:=xlWhole)
    FirstAddress = Rng.Address
    If Not Rng Is Nothing Then
        Do
            MsgBox Rng.Address
            Set Rng = Selection.FindNext(Rng)
        Loop While FirstAddress <> Rng.Address
    End If
End Sub

- Đầu tiên ta sẽ gán biến LastCell ứng với ô cuối cùng của vùng cần tìm kiếm : LastCell = [A10]

- Trong phương thức Find ta sẽ thấy có xuất hiện thêm tham số After có giá trị là LastCell. Điều này có nghĩa là việc tìm kiếm sẽ bắt đầu sau ô [A10]. Và như thế là ô [A1] sẽ được tìm thấy đầu tiên. Sau đó gán biến Rng bằng ô [A1].

- FirstAddress là chuỗi text ứng với địa chỉ của ô được tìm thấy đầu tiên trong vùng, ta gán biến FirstAddress = ”$A$1”.

- Nếu Rng khác Nothing thì đi đến vòng lặp Do Loop, nếu là Nothing thì thoát khỏi thủ tục.

- Vào vòng lặp Do loop thì quá trình tương tự như đoạn Sub Test1. Tuy nhiên trong đoạn Code này còn có thêm một biểu thức điều kiện : FirstAddress <> Rng.Address.

- Loop While FirstAddress <> Rng.Address : đoạn này có ý nghĩa như sau : quá trình tìm kiếm sẽ được lặp lại nếu như địa chỉ của biến Rng hiện tại khác với địa chỉ của ô đầu tiên được tìm thấy trong vùng vùng ( ô A1). Ngược lại, nếu trùng thì có nghĩa rằng ô này đã được tìm thấy trước đó, sẽ hủy bỏ kết quả này và thoát khỏi thủ tục.
 
Chỉnh sửa lần cuối bởi điều hành viên:
Upvote 0
Qua 2 ví dụ trên ta đã hiểu được phần nào về phương thức Find.

Áp dụng vào thực tế thì ta xem tiếp các bài tập sau :

Cũng với dữ liệu này :
Hinh1.jpg

Bài tập 1.> Yêu cầu đặt ra là tìm trong vùng A1:A10 những ô nào có chứa số 1 thì copy và dán lần lượt vào cột B. Bài toán này tương tự như việc trích lọc ra danh sách theo điều kiện trích lọc.

Ta xem đoạn code sau :

PHP:
Sub TrichLoc1()
    Dim Rng As Range, LastCell As Range, FirstAddress As String
    Set LastCell = Selection.Cells(Selection.Cells.Count)
    Set Rng = Selection.Find("1*", After:=LastCell, LookIn:=xlValues, LookAt:=xlWhole)
    FirstAddress = Rng.Address
    If Not Rng Is Nothing Then
        R = 1
        Do
            Cells(R, 2) = Rng
            R = R + 1
            Set Rng = Selection.FindNext(Rng)
        Loop While FirstAddress <> Rng.Address
    End If
End Sub


Đoạn Code này cũng tương tự như đoạn Code trong thủ tục Test2. Chỉ khác ở chỗ là thay vì sau mỗi lần tìm thấy ô thỏa mãn điều kiện sẽ hiện bảng thông báo địa chỉ của ô đó thì bây giờ sẽ điền giá trị của ô đó vào cột B.
- Nếu biến Rng khác Nothing thì gán cho biến R=1, vào vòng lặp.
- Điền vào ô Cells(1,2) = [B1] giá trị của biến Rng = 1.
- Tăng biến R lên 1 đơn vị R = 1+1 = 2
- Gán biến Rng cho ô tìm được tiếp theo : Rng = [A3]
- Kiểm tra điều kiện lặp, nếu thỏa thì qua trở lại Do
- Điền vào ô Cells(2,2) = [B2] giá trị của biến Rng = 11.
- Tiếp tục cho đến hết và kết quả như sau :

hinh3.jpg


Bài tập 2>. Yêu cầu thứ 2 là tìm trong vùng A1:A10, C1:C5 những ô nào có chứa số 1 thì copy và dán lần lượt vào cột B.

Nếu ta vẫn áp dụng thủ tục trên cho yêu cầu này thì sẽ không chính xác, vì ta sẽ không xác định được ô LastCell.

Ta sẽ tìm hiểu đoạn Code sau :

PHP:
Sub TrichLoc2()
    Dim RngData As Range, Rng As Range, LastCell As Range, FirstAddress As String
    Set RngData = Union(Range("A1:A20"), Range("C1:C5"))
    Set LastCell = Range("C1:C5").Cells(Range("C1:C5").Cells.Count)
    Set Rng = RngData.Find("1*", After:=LastCell, LookIn:=xlValues, LookAt:=xlWhole)
    FirstAddress = Rng.Address
    If Not Rng Is Nothing Then
        R = 1
        Do
            Cells(R, 2) = Rng
            R = R + 1
            Set Rng = RngData.FindNext(Rng)
        Loop While FirstAddress <> Rng.Address
    End If
End Sub

- Ở đây, ta sử dụng phương thức Union để ghép các vùng cần trích lọc lại với nhau.
- Tìm ô cuối cùng trong vùng C1:C5 và gán cho biến LastCell : LastCell = [C5].
- Các bước tiếp theo thì tương tự như các Code đã nêu trên
- Tiếp tục cho đến hết và kết quả như sau :

hinh4.jpg
 
Chỉnh sửa lần cuối bởi điều hành viên:
Upvote 0
Tiếp theo là giải quyết một nhiệm vụ thực tiển về trích lọc Sổ cái tài khoản từ Sổ Nhật ký chung.

Ta có Dữ liệu mẫu như sau :

hinh5.jpg



Vùng A1:D11 là dữ liệu của Sổ NKC
Vùng F1:I11 là kết quả trích lọc TK 111 từ sổ NCK ( Sổ cái TK 111)
Ta xét Macro sau :

PHP:
Sub TrichLocSocai()
    Dim Rng As Range, LastCell As Range
    Dim FirstAddress As String, R As Long, SaveRow As Long
    Application.ScreenUpdating = False
    With Range("B2:C11")
        Set LastCell = .Cells(.Cells.Count)
        Set Rng = .Find("111*", After:=LastCell, LookIn:=xlValues, LookAt:=xlWhole)
        FirstAddress = Rng.Address
        If Not Rng Is Nothing Then
            R = 2
            Do
                SaveRow = Rng.Row
                Select Case Rng.Column
                Case 2
                    Cells(R, 6) = Rng.Offset(, -1)
                    Cells(R, 7) = Rng.Offset(, 1)
                    Cells(R, 8) = Rng.Offset(, 2)
                Case 3
                    Cells(R, 6) = Rng.Offset(, -2)
                    Cells(R, 7) = Rng.Offset(, -1)
                    Cells(R, 9) = Rng.Offset(, 1)
                End Select
                R = R + 1
                Set Rng = .FindNext(Rng)
            Loop While FirstAddress <> Rng.Address And SaveRow <> Rng.Row
        End If
    End With
    Set LastCell = Nothing: Set Rng = Nothing
    Application.ScreenUpdating = True
End Sub

So với cách trích lọc dùng For Next thông thường thì Code này vượt trội hơn về tốc độ. Phù hợp với những cơ sở dữ liệu lớn.
Ta có thể kết hợp với thuộc tính End để làm cho dữ liệu tạo sự linh động cho dữ liệu gốc.

Trên đây là nội dung sơ lược về phương thức Find. Chúng ta sẽ cùng tìm hiểu sâu hơn về phương thức này trong các bài tiếp theo.

 

File đính kèm

Chỉnh sửa lần cuối bởi điều hành viên:
Upvote 0
Tạo sổ quỹ bằng Find Method!

Một cách để làm Sổ Quỹ từ NKC =>In phiếu Thu & Chi.​


(file này khá hoàn thiện chỉ có code là chưa hoàn thiện thôi.)

Các bạn làm kế toán có thể thực hiện.
PHP:
Sub TaoSoQuy()
Dim TkDu As String, GhiChu As String
Dim RngFound As Range, LastCell As Range, MyRng As Range, SoTK As String
Dim eRow As Long, SoLan As Long, iR As Long, iL As Long, TienThu As Double, TienChi As Double
With Application
        .EnableEvents = False:          .DisplayAlerts = False
       .ScreenUpdating = False:       .Calculation = xlCalculationManual
End With
iR = 2000:                                   Sheet5.Select 'Sh SoQuy'
Range(Cells(9, 1), Cells(iR , 8)).ClearContents
Range(Cells(9, 10), Cells(iR , 10)).ClearContents
SoTK = "1111*"
iR = 8
With Sheets("NKC")
    eRow = .[I65000].End(xlUp).Row
    Set MyRng = .Range("I9:J" & eRow) 'Có sửa I10=I9'
    SoLan = WorksheetFunction.CountIf(MyRng, SoTK)
    Set RngFound = .Cells(9, 10) '10=9'
    For iL = 1 To SoLan
        With MyRng
           Set RngFound = .Find(SoTK, After:=RngFound, LookIn:=xlValues, LookAt:=xlWhole)
                With RngFound
                    Select Case RngFound.Column
                    Case 9
                            TienThu = TienThu + .Offset(, 2).Value
                            If InStr(GhiChu, .Offset(, -5)) > 0 Then
                                GhiChu = Trim(GhiChu)
                            Else
                                GhiChu = GhiChu & .Offset(, -5) & ";"
                            End If
                            If InStr(TkDu, .Offset(, 1)) > 0 Then
                                TkDu = TkDu
                            Else
                                TkDu = TkDu & .Offset(, 1) & ";"
                            End If
                            If .Offset(, -6) <> .Offset(1, -6) Then
                                 iR = iR + 1
                                 Cells(iR, 1) = iR - 8
                                 Cells(iR, 3) = .Offset(, -6) 'SoCT'
                                 Cells(iR, 2) = .Offset(, -4) 'NgayHT'
                                 Cells(iR, 5) = .Offset(, -1) & "-" & .Offset(, -2)'DienGiai'
                                 Cells(iR, 6) = TkDu 'TkDu'
                                 Cells(iR, 7) = TienThu 'TienThu'
                                 Cells(iR, 10) = GhiChu 'Note'
                                 TienThu = 0: TkDu = "": GhiChu = ""
                              End If
                        Case 10
                            TienChi = TienChi + .Offset(, 1).Value
                                If InStr(GhiChu, .Offset(, -6)) > 0 Then
                                    GhiChu = Trim(GhiChu)
                                Else
                                    GhiChu = GhiChu & .Offset(, -6) & ";"
                                End If
                            If InStr(TkDu, .Offset(, -1)) > 0 Then
                                TkDu = TkDu
                            Else
                                TkDu = TkDu & .Offset(, -1) & ";"
                            End If
                            If .Offset(, -7) <> .Offset(1, -7) Then '*Neu so ct khac so ct'
                                iR = iR + 1
                                Cells(iR, 1) = iR - 8
                                Cells(iR, 4) = .Offset(, -7) 'SoCT'
                                Cells(iR, 2) = .Offset(, -5) 'NgayHT'
                                Cells(iR, 5) = .Offset(, -2) & "-" & .Offset(, -3)'DienGiai'
                                Cells(iR, 6) = TkDu 'TkDu'
                                Cells(iR, 8) = TienChi 'Tienchi'
                                Cells(iR, 10) = GhiChu 'Note'
                                TienChi = 0: TkDu = "": GhiChu = ""
                            End If
                        End Select
                End With
            End With
        Next iL
End With
Set LastCell = Nothing: Set RngFound = Nothing: Set MyRng = Nothing
With Application
        .EnableEvents = True:          .DisplayAlerts = True
       .ScreenUpdating = True:       .Calculation = xlCalculationAutomatic
End With
End Sub
Trong đây có phần tính tồn cuối ngày làm biếng nên chỉ dùng công thức.
Danh có thể diễn giải code hộ với. Làm rồi mà ngán coi lại.
Làm rồi nên up, vì vàì bạn có nhu cầu.
(Chú ý là khi nhập vào NKC phải theo cấu trúc.)
 

File đính kèm

Lần chỉnh sửa cuối:
Upvote 0
To ThuNghi

Hay đấy. Mới học vòng lặp lấy dữ liệu từ OB (Mr Hiếu) chưa xong nay thấy cái này hay quá (về tốc độ).

Bạn cho 1 ví dụ hoàn chỉnh 1 chút nhen: từ NKC lấy dữ liệu lên sổ Cái các TK, Sổ Quỹ, Phiếu nhập xuất kho, phiếu thu chi, Thẻ kho (Sổ chi tiết vật tư).

Từ đây từ từ học được rùi.

Bạn giúp nhen.

Thân.


P/s: File FindGPE06-TaoSoQuy.rar của bạn chưa hoàn chỉnh đâu nhé:
+ Chưa ẩn dòng thừa.
+ Chưa tính tồn cuối kỳ.

Đề nghị các bạn & tác giả bài này đến đây để thảo luận các vấn đề có liên quan:
(Bài này sẽ xóa trong thời gian tới!)
 
Upvote 0
To ThuNghi

Hay đấy. Mới học vòng lặp lấy dữ liệu từ OB (Mr Hiếu) chưa xong nay thấy cái này hay quá (về tốc độ).

Bạn cho 1 ví dụ hoàn chỉnh 1 chút nhen: từ NKC lấy dữ liệu lên sổ Cái các TK, Sổ Quỹ, Phiếu nhập xuất kho, phiếu thu chi, Thẻ kho (Sổ chi tiết vật tư).
P/s: File FindGPE06-TaoSoQuy.rar của bạn chưa hoàn chỉnh đâu nhé:
+ Chưa ẩn dòng thừa.
+ Chưa tính tồn cuối kỳ.

Đề nghị các bạn & tác giả bài này đến đây để thảo luận các vấn đề có liên quan:
(Bài này sẽ xóa trong thời gian tới!)
Cám ơn nhiều, ít ra cũng có 1 người cần, trong file trên là tôi lấy dữ liệu thực tế mà làm đó. Còn vấn đề bỏ dòng, tổng thì lúc nào OK thì hòan thiện luôn. File FindGPE04 là file in phiếu nhập, dồn 2 file lại và code của Danh nữa là tạo được tòan bộ sổ kế toán theo từng tháng.
Bác HYEN17 ơi, giờ em muốn sửa file, hòan thiện thì vào trang kia hả. Bác chuyển hết toàn bộ file của em qua topic đó với. Em tính up thêm FindGPE07 mà chưa biết up ở đâu.
Thông cảm cho vì em muốn một lấn làm là làm cụ thể và em ứng dụng luôn nên có thể không tập trung về Find nhưng Find vẫn là Method xuyên suốt.
Cám ơn Bác!
 
Upvote 0
Một cách để làm Sổ Quỹ từ NKC =>In phiếu Thu & Chi.​

PHP:
Set RngFound = .Find(SoTK, After:=RngFound, LookIn:=xlValues, LookAt:=xlWhole)
1. Bản chất của Find là sẽ lưu lại các giá trị tham số của lần tìm kiếm trước đó. Vì vậy nếu lần trước đó được Set là :
PHP:
SearchOrder:=xlByColumns
Thì như vậy theo như Code trên sẽ tìm kiếm theo cột, hết cột NỢ mới đến cột CÓ

Điều này cũng có nghĩa rằng nếu lần tìm kiếm trước đó là theo cột thì Code trên sẽ tìm hết Phiếu THU sau đó mới đến Phiếu CHI, vì vậy nếu để cho đúng Sổ Quỹ thì khi chạy xong ta lại phải Sort lại một lần nữa (Code trên không có điều này)

Giải pháp là : Ta thêm vào thành :
PHP:
Set RngFound = .Find(SoTK, After:=RngFound, LookIn:=xlValues, LookAt:=xlWhole, SearchOder:=xlRows)
Như vậy nó sẽ tìm kiếm theo hàng. Đây là một lợi thế khi DATA của ta đã được Sort theo ngày tháng

2. Sổ quỹ thì làm theo tháng hoặc năm, thậm chí là ngày bất kỳ, tuy nhiên Code trên lại không thấy việc so sánh hay loại trừ nếu ngày chứng từ không phù hợp.
Vì DATA là trọn 1 tháng, Sổ Quỹ cũng trong 1 tháng nên ta mới thấy nó luôn đúng

3. Chứng từ thu chi thường có ký hiệu là PTxxx, PCxxx, ta có thể lợi dụng điều này để xác định vùng tìm kiếm chỉ là ở cột chứng từ. Có thể có 2 cách:
- Tìm Phiếu Thu trước, Phiếu Chi sau, sau đó Sort lại theo ngày tháng
- Tìm đồng thời cả 2 phiếu với 2 phương thức Find chạy song song nhau

4. Việc sử dụng biến trung gian là rất tốt, tuy nhiên tôi vẫn thấy bác dùng nhiều biến quá. Có thể rút gọn hơn rất nhiều. Khi chạy một khối lượng lớn thì bác mới thấy việc này hiệu quả.

Chúc vui!
 
Upvote 0
1/Set RngFound = .Find(SoTK, After:=RngFound, LookIn:=xlValues, LookAt:=xlWhole, SearchOder:=xlRows)
Nếu không ghi SearchOder:=... thì mặc định là SearchOder:=xlRows

2. Sổ quỹ thì làm theo tháng hoặc năm, thậm chí là ngày bất kỳ, tuy nhiên Code trên lại không thấy việc so sánh hay loại trừ nếu ngày chứng từ không phù hợp.
Vì DATA là trọn 1 tháng, Sổ Quỹ cũng trong 1 tháng nên ta mới thấy nó luôn đúng

Thêm cái này vào thì OK thôi, trong NKC có Field Thang, có thể tìm theo field này. Đơn giản đã.

3. Chứng từ thu chi thường có ký hiệu là PTxxx, PCxxx, ta có thể lợi dụng điều này để xác định vùng tìm kiếm chỉ là ở cột chứng từ. Có thể có 2 cách:
- Tìm Phiếu Thu trước, Phiếu Chi sau, sau đó Sort lại theo ngày tháng
- Tìm đồng thời cả 2 phiếu với 2 phương thức Find chạy song song nhau

Cũng tính Tìm Phiếu Thu trước, Phiếu Chi sau, và 2 phương thức Find chạy song song, đã thử thấy rắc rối quá. Mà chưa chắc nhanh hơn.

4. Việc sử dụng biến trung gian là rất tốt, tuy nhiên tôi vẫn thấy bác dùng nhiều biến quá. Có thể rút gọn hơn rất nhiều. Khi chạy một khối lượng lớn thì bác mới thấy việc này hiệu quả.
Phần case này có thể rút gọn lại vì có sự đồng nhất là +/-1, chưa hoàn thiện. Đang làm lại.
Cám ơn đóng góp của bạn rất nhiều. Trước giờ mình quen dùng Advace Filter, sang Find cũng còn phải học hỏi nhiều.
 
Upvote 0
Ở các bài viết trên chúng ta đã tìm hiểu về phương thức Find kết hợp với FindNext. Hôm nay ta sẽ nghiên cứu về phương thức FindPrevious.

Ta xét VD sau : Cho dữ liệu :

MinhhoaFindNext.jpg


Tìm trong vùng A1:A10 từ dưới lên trên, những ô nào có chứa ký tự “a” (Có phân biệt chữ in hoa với chữ thường) thì copy và dán lần lượt vào cột B. Kết quả mong muốn như hình minh họa ở trên.


Ta bôi đen vùng A1:A10 và chạy đoạn Code sau :

PHP:
Sub Test3()
    Dim Rng As Range, FirstCell As Range, FirstAddress As String, r As Long
    Application.ScreenUpdating = False
    On Error Resume Next
    With Selection
        Set FirstCell = .Resize(1, 1)
        Set Rng = .Find(What:="*a*", After:=FirstCell, LookIn:=xlValues, LookAt:=xlWhole, _
                        SearchDirection:=xlPrevious, MatchCase:=True)
        FirstAddress = Rng.Address
        If Not Rng Is Nothing Then
            r = 1
            Do
                Cells(r, 2) = Rng
                r = r + 1
                Set Rng = .FindPrevious(Rng)
            Loop While FirstAddress <> Rng.Address
        End If
    End With
    Set FirstCell = Nothing: Set Rng = Nothing
    Application.ScreenUpdating = True
End Sub
</SPAN>
Diễn giải trình tự nội dung của Code như sau :

- Dim Rng as Range, … : khai báo biến trong thủ tục.
- Application.ScreenUpdating = False : Tạm dừng chức năng cập nhật màn hình của ứng dụng.
- On Error Resume Next : câu lệnh điều khiển chương trình. Nếu trong quá trình thi hành thủ tục mà gặp lỗi thì bỏ qua lỗi này và thi hành tiếp các câu lệch tiếp theo.
- Bắt đầu sử dụng câu lệnh With cho đối tượng Selection.
- Gán biến FirstCell bằng với ô đầu tiên trong vùng Selection : FirstCell = [A1]
- Gán biến Rng cho kết quả tìm kiếm đầu tiên của phuơng thức Find.
+ Giá trị tìm kiếm What:=”*a*” có nghĩa là tìm trong vùng Selection tất cả các ô có chứa đựng ký tự “a”.
+ After:=FirstCell : Bắt đầu tìm sau ô FirstCell.
+ SearchDirection:=xlPrevious : có nghĩa là sẽ tìm ngược từ dưới lên trên trong vùng dữ liệu. Ở các bài trước ta không thấy tham số SearchDirection xuất hiện trong phương thức Find, điều này có nghĩa là tham số mặc định sẽ là SearchDirection:=xlNext : Tìm xuôi từ trên xuống dưới.
+ MatchCase:=True : Tìm kiếm có phân biệt chữ thường với chữ in hoa.
Như vậy, biến Rng ngay tại thời điểm này sẽ là Rng = [A8].
- Gán biến FirstAddress bằng địa chỉ của ô vừa được tìm thấy đầu tiên : FirstAddress = ” $A$8”
- Nếu biến Rng khác Nothing thì :
- Gán cho biến r = 1
- Vào vòng lặp Do Loop
- Cells(r, 2) = Rng : điền giá trị của biến Rng vào ô Cells(1,2). Vậy ô [B1] = “Bac”.
- Tăng biến r thêm 1 đơn vị : r = r + 1 = 2
- Gán biến Rng cho ô vừa được tìm thấy kế tiếp (tìm từ dưới lên trên bắt đầu từ ô [A8]). Rng = [A6].
- Nếu địa chỉ của ô vừa tìm thấy khác với địa chỉ của ô đầu tiên được tìm thấy thì tiếp tục vòng lặp Do Loop. Ngược lại thì thoát khỏi vòng lặp.
- End If, End With : kết thúc mệnh đề If và With.
- Gán cho biến FirstCell và biến Rng bằng Nothing : nhằm làm giảm sự chiếm dụng bộ nhớ, giúp thủ tục chạy nhanh hơn.
- Application.ScreenUpdating = True :Khôi phục lại chức năng cập nhật màn hình của ứng dụng.
- Kết thúc thủ tục.
 
Chỉnh sửa lần cuối bởi điều hành viên:
Upvote 0
Ở các bài viết trên chúng ta đã tìm hiểu về phương thức Find kết hợp với FindNext. Hôm nay ta sẽ nghiên cứu về phương thức FindPrevious.

Ta xét VD sau :
Cho dữ liệu :
Tìm trong vùng A1:A10 từ dưới lên trên, những ô nào có chứa ký tự “a” (Có phân biệt chữ in hoa với chữ thường) thì copy và dán lần lượt vào cột B. Kết quả mong muốn như hình minh họa ở trên.

Cái này áp dụng thay thế hàm Match tìm từ dưới lên. Nếu OK thì lấy và exit.
 
Upvote 0
Nếu không ghi SearchOder:=... thì mặc định là SearchOder:=xlRows

Bác hãy thử với chính Code của trên của bác thì thấy ngay thôi:

Thử 1:

  1. Chạy Code với SearchOrder:=xlRows để lấy giá trị mặc định
  2. Chạy Code không có SearchOrder --->> Nó sẽ lấy giá trị mặc định là xlRows
Thử 2 :

  1. Chạy Code với SearchOrder:=xlColumns để lấy giá trị mặc định
  2. Chạy Code không có SearchOrder --->> Nó sẽ lấy giá trị mặc định là xlColumns
Khi đó sẽ cho ra 2 Sổ Quỹ khác nhau.

Chúc vui
 
Upvote 0
Thử 1:

  1. Chạy Code với SearchOrder:=xlRows để lấy giá trị mặc định
  2. Chạy Code không có SearchOrder --->> Nó sẽ lấy giá trị mặc định là xlRows
Thử 2 :

  1. Chạy Code với SearchOrder:=xlColumns để lấy giá trị mặc định
  2. Chạy Code không có SearchOrder --->> Nó sẽ lấy giá trị mặc định là xlColumns
Khi đó sẽ cho ra 2 Sổ Quỹ khác nhau.

Chúc vui
Rất cám ơn, nếu không nhắc chắc không nghĩ ra.
 
Upvote 0
Ở các bài viết trên chúng ta đã tìm hiểu về phương thức Find kết hợp với FindNext. Hôm nay ta sẽ nghiên cứu về phương thức FindPrevious.

Ta xét VD sau :
Cho dữ liệu :

Tìm trong vùng A1:A10 từ dưới lên trên, những ô nào có chứa ký tự “a” (Có phân biệt chữ in hoa với chữ thường) thì copy và dán lần lượt vào cột B. Kết quả mong muốn như hình minh họa ở trên.

Ta xét đoạn Code sau :

PHP:
Sub Test3()
Dim Rng As Range, FirstCell As Range, FirstAddress As String, r As Long
Application.ScreenUpdating = False
On Error Resume Next
With Selection
Set FirstCell = .Resize(1, 1)
Set Rng = .Find(What:="*a*", After:=FirstCell, LookIn:=xlValues, LookAt:=xlWhole, _
                    SearchDirection:=xlPrevious, MatchCase:=True)
</SPAN>



Để :
Tìm trong vùng A1:A10 từ dưới lên trên, những ô nào có chứa ký tự “a” (Có phân biệt chữ in hoa với chữ thường)
thì có thể làm như sau nữa :

PHP:
Set Rng = .Find(What:="a", After:=FirstCell, LookIn:=xlValues, LookAt:=xlPart, _
                    SearchDirection:=xlPrevious, MatchCase:=True)
 
Upvote 0
Tạo sổ quỹ, sổ cái bằng Find Method!

Bổ sung lại file FindGPE06, tạo thêm phần trích sổ cái, còn phiếu thu chi nếu tạo sổ quỹ xong thì in phiếu thu chi chỉ là vlookup. Bạn nào hòan thiện hộ.
Các bác xem và góp ý cho code tạo sổ quỹ giúp.
Xin cám ơn!
PHP:
Sub TaoSoQuy()
With Application
        .EnableEvents = False:        .DisplayAlerts = False:        .ScreenUpdating = False:       .Calculation = xlCalculationManual
End With
Dim TkDu As String, GhiChu As String, SoTK As String
Dim RngFound As Range, LastCell As Range, MyRng As Range
Dim eRow As Long, SoLan As Long, iR As Long, iL As Long, eR As Long, iC As Long
Dim SoTien As Double, TienTon As Double

iR = 8: eR = 1000
Sheets("SoQuy").Select
Range(Cells(9, 1), Cells(eR, 10)).ClearContents
Rows("9:" & eR).EntireRow.Hidden = False
SoTK = "1111*"
TienTon = 0
With Sheets("NKC")
    If .AutoFilterMode = True Then
        .AutoFilterMode = False
    End If
    eRow = .[I65000].End(xlUp).Row
        Set MyRng = .Range("I9:J" & eRow)
        SoLan = WorksheetFunction.CountIf(MyRng, SoTK)
        Set RngFound = .Cells(9, 10)
        For iL = 1 To SoLan
            With MyRng
               Set RngFound = .Find(SoTK, After:=RngFound, SearchOrder:=xlRows, LookIn:=xlValues, LookAt:=xlWhole)
                    With RngFound
                    Select Case RngFound.Column
                    Case 9
                        iC = 0
                    Case 10
                        iC = -1
                    End Select
                        SoTien = SoTien + .Offset(, 2 + iC).Value
                        If InStr(GhiChu, .Offset(, -5 + iC)) > 0 Then
                            GhiChu = Trim(GhiChu)
                        Else
                            GhiChu = GhiChu & .Offset(, -5 + iC) & ";"
                        End If
                        If InStr(TkDu, .Offset(, 1 + iC)) > 0 Then
                            TkDu = TkDu
                        Else
                            TkDu = TkDu & .Offset(, 1 + iC) & ";"
                        End If
                        If .Offset(, -6 + iC) <> .Offset(1, -6 + iC) Then
                            iR = iR + 1
                            Cells(iR, 1) = iR - 8
                            Cells(iR, 3 - iC) = .Offset(, -6 + iC) 'SoCT'
                            Cells(iR, 2) = .Offset(, -4 + iC) 'NgayHT'
                            If Cells(iR, 2) <> Cells(iR - 1, 2) Then 'Lay ton quy'
                                Cells(iR - 1, 9) = TienTon + Cells(8, 9)
                            End If
                            Cells(iR, 5) = .Offset(, -1 + iC) & "-" & .Offset(, -2 + iC) 'DienGiai'
                            Cells(iR, 6) = TkDu 'TkDu'
                            Cells(iR, 7 - iC) = SoTien 'So Tien Thu chi'
                            Cells(iR, 10) = Left(GhiChu, Len(GhiChu) - 1) 'Note'
                            TienTon = TienTon + SoTien * IIf(iC = 0, 1, iC)
                             SoTien = 0: TkDu = "": GhiChu = ""
                        End If
               End With
            End With
        Next iL
        Cells(iR, 9) = TienTon + Cells(8, 9)
End With
Rows(iR + 2 & ":" & eR).EntireRow.Hidden = True
Set LastCell = Nothing: Set RngFound = Nothing: Set MyRng = Nothing
With Application
        .EnableEvents = True:       .DisplayAlerts = True:       .ScreenUpdating = True:      .Calculation = xlCalculationAutomatic
End With
End Sub
Xin gởi kèm file.
Cám ơn nhiều.
 

File đính kèm

Upvote 0
Find lấy dữ liệu từ fDate -> eDate!

Theo yêu cầu của...
Tôi làm 1 ví dụ trích lọc dữ liệu từ ngày đầu -> ngày cuối bằng Find Method.
Mong các bạn góp ý. Chưa biết có nhanh hơn AdFi. Các bạn test thử.
Trên đây tạm bỏ qua về sai định dạng, hay là eDate < fDate.
Xin cám ơn!
PHP:
Sub FindDate()
Dim fDate As Date, eDate As Date, iDate As Date
Dim RngFound As Range, MyRng As Range
Dim eRow As Long, SoLan As Long, iD As Long, iL As Long
Sheet2.Select
Range(Cells(9, 1), Cells(100, 3)).ClearContents
fDate = Cells(6, 1): eDate = Cells(6, 2)
With Sheet1
    .AutoFilterMode = False
    eRow = .[A65000].End(xlUp).Row
    Set MyRng = .Range("A1:A" & eRow)
End With
For iD = fDate To eDate
iDate = CDate(iD) 'Cdate chuyển từ string sang date'
'*** Vấn đề tìm theo Date này có nhiề phần chưa tìm ra
'***Có  thể  ta dùng  iDate=Format(iD,  "Short Date") lúc này phải khai  Dim  iD as  string' 
With Sheet1
    SoLan = WorksheetFunction.CountIf(MyRng, iDate)
'***SoLan này này chính là số lần FindNext.'
    eRow = [a1000].End(xlUp).Row
    Set RngFound = .Cells(1, 1)
    For iL = 1 To SoLan
'***Nên thêm MyRng.number.format="m/d/yyyy" '
'Dự trù MyRng định dạng khác iD'
        With MyRng
           Set RngFound = .Find(iDate, After:=RngFound, SearchOrder:=xlColumns, LookIn:=xlValues, LookAt:=xlWhole)
           With RngFound
            Cells(iL + eRow, 1) = .Value
            Cells(iL + eRow, 2) = .Offset(, 1).Value
            Cells(iL + eRow, 3) = .Offset(, 2).Value
           End With
        End With
    Next iL
End With
Next
Set RngFound = Nothing: Set MyRng = Nothing
End Sub
Code trên là:
1/ Đưa định dạng 2 Range ngày về cùng format
2/ Duyệt từ ngày đầu -> ngày cuối. =>iD
3/ Tìm SoLan xuất hiện iD trong MyRng.
4/ FindNext (SoLan)
5/ Gán vào ....
Từ đó ta có thể vận dụng để tính dd/mm/yy hh:mm
Quan trọng nhất là định dạng.
Đang nghiên cứu tiếp phần format ngày, nó hơi khó hiểu. Đang dừng lại với việc nghiên cứu file Bác Sa trả lời BoyXin.
Các bạn hỗ trợ nhé.
 

File đính kèm

Upvote 0
Anh viết hay lắm, nhưng nếu có thêm diễn giải Code để minh họa thì tuyệt vời.

Theo em thì Trong Code ta nên Insert thêm một Sheet tạm, Copy dữ liệu qua, Sort dữ liệu theo ngày tháng và trích lọc rồi xóa sheet tạm này đi.

From Sa_DQ: Đúng là cần diễn giải thêm, vì trong excel người ta có thể định dạng dữ liệu ngày tháng bằng rất nhiều cách khác nhau. Nếu không định dạng theo khuôn mẫu, không thể tìm ra

Bác SA ơi, anh Kiệt ơi ! các anh đâu rồi mà sao không thấy bài viết của các anh ạ.

Với cả nhóm: Theo mình thì Topic này vẫn là bản thảo của nhóm mà thôi; Nếu đồng ý như vậy, thì bài mở đầu của topic chính thức tổng hợp về phương thức FIND() sẽ của Ca_Dafi;
Ca_Dafi cho bản thảo lần cuối đi (Tất nhiên hoàn toàn Việt hóa đi - không dùng song ngữ được!)
Bài thứ hai, ba. . . theo dàn bài của Ca_dafi sẽ là của HoangDanh & ThuNghi
Để khỏi phân tâm đọc giả, chúng ta sẽ xóa dần cái topic phụ, kể cả cái này trong nay mai!
 
Upvote 0
Vận dụng ý tưởng của HoangDanh282vn, SearchDirection:=xlPrevious, mình viết thử 1 UDF tìm số ct max theo thuật toán dò ngược.
Cú pháp:
MaxSoCT(MyRng As Range, iM As Long, lCtu As String)
MyRng: Vùng chứa số CT.
iM: Tháng cần lấy số CT
lCtu: Loại CT: PT, PN, PX...
PHP:
Public Function MaxSoCT(MyRng As Range, iM As Long, lCtu As String)
''*** SoCT co dang la PTmm/00i ****''
Dim RngFound As Range
Dim eR As Long, SoCT As String, Dem As Long, iSoCT As String
SoCT = lCtu & Format(iM, "00") & "/"
eR = MyRng.Rows.Count
If eR = 0 Then Exit Function
Dem = WorksheetFunction.CountIf(MyRng, SoCT & "*")
If Dem = 0 Then
    iSoCT = SoCT & "001"
    GoTo bien
End If
Set RngFound = MyRng(1)
With MyRng
    Set RngFound = .Find(SoCT & "*", After:=RngFound, SearchOrder:=xlColumns, LookIn:=xlValues, LookAt:=xlPart, SearchDirection:=xlPrevious)
End With
iSoCT = SoCT & Format(Right(RngFound, 3) + 1, "000")
bien:
MaxSoCT = iSoCT
End Function
Các bạn hoàn thiện hộ. Xin cho ví dụ để hòan thiện Find, tạo vd lâu hơn tạo code.
Cám ơn!
 

File đính kèm

Upvote 0
Vận dụng ý tưởng của HoangDanh282vn, SearchDirection:=xlPrevious, mình viết thử 1 UDF tìm số ct max theo thuật toán dò ngược.
Cú pháp:
MaxSoCT(MyRng As Range, iM As Long, lCtu As String)
MyRng: Vùng chứa số CT.
iM: Tháng cần lấy số CT
lCtu: Loại CT: PT, PN, PX...
PHP:
Public Function MaxSoCT(MyRng As Range, iM As Long, lCtu As String)
''*** SoCT co dang la PTmm/00i ****''
Dim RngFound As Range
Dim eR As Long, SoCT As String, Dem As Long, iSoCT As String
SoCT = lCtu & Format(iM, "00") & "/"
eR = MyRng.Rows.Count
If eR = 0 Then Exit Function
Dem = WorksheetFunction.CountIf(MyRng, SoCT & "*")
If Dem = 0 Then
    iSoCT = SoCT & "001"
    GoTo bien
End If
Set RngFound = MyRng(1)
With MyRng
    Set RngFound = .Find(SoCT & "*", After:=RngFound, SearchOrder:=xlColumns, LookIn:=xlValues, LookAt:=xlPart, SearchDirection:=xlPrevious)
End With
iSoCT = SoCT & Format(Right(RngFound, 3) + 1, "000")
bien:
MaxSoCT = iSoCT
End Function
Các bạn hoàn thiện hộ. Xin cho ví dụ để hòan thiện Find, tạo vd lâu hơn tạo code.
Cám ơn!

Thuật toán dò ngược này chỉ có tác dụng khi Dữ liệu đã được Sort theo kiểu tăng dần thôi bác ạ.

Vì vậy, để đúng cho mọi trường hợp thì nên dò tìm tất cả.

Edit
Mà tại sao lại dùng Countif nếu chỉ đếm 1 lần nhỉ ?? Khi không tìm thấy RngFound = Nothing thì có nghĩa countif = 0 rồi.

Thân!
 
Lần chỉnh sửa cuối:
Upvote 0
Function tìm địa chỉ ô cuối cùng có chứa dữ liệu trong một sheet nào đó :

PHP:
Function LastCell(StWs As String) As String
Dim LastRowCell as long, LastColCell as long
On Error Resume Next
With Sheets(StWs)
LastRowCell = .Cells.Find(What:="*", _
                SearchDirection:=xlPrevious, _
                SearchOrder:=xlByRows).Row
LastColCell = .Cells.Find(What:="*", _
                SearchDirection:=xlPrevious, _
                SearchOrder:=xlByColumns).Column
LastCell = StWs & "!" & .Cells(LastRowCell, LastColCell).Address
End With
End Function
 
Lần chỉnh sửa cuối:
Upvote 1
Thêm tùy chọn cho đầy đủ hơn

PHP:
Function LastCell(StWs As String, Optional Opt As Byte = 0) As Variant
Dim LastRowCell As Long, LastColCell As Long
On Error Resume Next
With Sheets(StWs)
LastRowCell = .Cells.Find(What:="*", _
                SearchDirection:=xlPrevious, _
                SearchOrder:=xlByRows).Row
LastColCell = .Cells.Find(What:="*", _
                SearchDirection:=xlPrevious, _
                SearchOrder:=xlByColumns).Column
Select Case Opt
    Case 0: LastCell = StWs & "!" & .Cells(LastRowCell, LastColCell).Address
    Case 1: LastCell = LastRowCell
    Case 2: LastCell = LastColCell
End Select
End With
End Function
 
Upvote 0
Cho em hỏi chút: em có một file excel danh sách các công ty gồm 2 4 cột: tên công ty, mặt hàng họ mua, địa chỉ, điện thoại. Em đã dùng filter để lọc dữ liệu theo tên công ty hay mặt hàng. Nhưng do danh sách công ty và mặt hàng quá dài. Nên mỗi khi tìm tên công ty hay mặt hàng em lại phải dò danh sách từ trên xuống dưới. Em muốn hỏi có cách nào mà giúp em search nhanh như google ko? Chỉ cần gõ tên hay mặt hàng là hiện ra hết!
 
Upvote 0
Thêm tùy chọn cho đầy đủ hơn

PHP:
Function LastCell(StWs As String, Optional Opt As Byte = 0) As Variant
Dim LastRowCell As Long, LastColCell As Long

On Error Resume Next

With Sheets(StWs)
LastRowCell = .Cells.Find(What:="*", _
                SearchDirection:=xlPrevious, _
                SearchOrder:=xlByRows).Row
LastColCell = .Cells.Find(What:="*", _
                SearchDirection:=xlPrevious, _
                SearchOrder:=xlByColumns).Column
Select Case Opt
    Case 0: LastCell = StWs & "!" & .Cells(LastRowCell, LastColCell).Address
    Case 1: LastCell = LastRowCell
    Case 2: LastCell = LastColCell
End Select
End With
End Function

Trong hàm trên, nếu chúng ta dùng
Mã:
On Error Resume Next

Liệu có ảnh hưởng đến kết quả của hàm hay không?

Vbavn
 
Upvote 0
Tìm kiếm dữ liệu trong cột có các ô trộn(#I)

Tìm kiếm trong cột dữ liệu có các ô trộn



Trước tiên xin cảm ơn bạn có biệt danh HoangMinhTien trên GiaiPhapExcel.COM đã đưa ra câu hỏi tại http://giaiphapexcel.com/forum/showthread.php?t=19279, để mới có bài này, mà tác giả tạm gọi là tìm kiếm dữ liệu trong vùng có những ô bị trộn chung.
Ba vấn đề bạn ấy hỏi, đều là những vấn đề, mà theo tôi phương thức tìm kiếm trong GPE.COM ta đề cập đến chưa đầy đủ lắm.

Vấn đề I: Tìm và lọc lấy dữ liệu của 1 ngày riêng biệt.

Về vấn đề tìm dữ liệu thể hiện 1 ngày cụ thể trong năm nào đó, ta đã thấy nếu trong bài #16 của http://giaiphapexcel.com/forum/showthread.php?t=15116 do ThuNghi chủ xướng.

Để tiếp tục tìm hiểu về vấn đề tìm kiếm ngày trong cột dữ liệu đã trộn ô, các bạn hãy cùng tôi xem xét các câu lệnh được đánh từ số 1 cho đến trước câu lệnh được đánh số 2 trong macro được dẫn ra dưới đây.

Trước tiên nói về câu lệnh 13:
Mã:
 Sh.Cells(Zz + 1, "i") = 0
Như trước dòng lệnh 1, ta đã biết biến Zz đang chứa dữ liệu biểu thị dòng cuối của sheets(“Data”) (Các bạn xem thêm trong file đính kèm); Ở đây ta phải thêm giá trị 0 vô cuối cột dữ liệu, để đề phòng trường hợp các ô cuối của cột ‘I’ cần tìm này đã bị trộn nhau lại. Mà ta biết rằng, giả dụ Zz đang bằng 9 & và các ô từ ‘I7:I9’ đã được trộn vô nhau, thì 2 ô ‘I8:I9’ sẽ là 2 ô không chứa trị. Việc này sẽ dẫn chúng ta cùng với excel tưởng rằng dòng ‘7:7’ đã là dòng cuối cùng của dữ liệu. (Thực ra tại ‘B9’ vẫn có dữ liệu, có nghĩa dòng ‘9:9’ mới là dòng cuối chứa dữ liệu)

Chúng ta tiếp. Yêu cầu của bạn HoangMinhTien là nếu dữ liệu ngày cần tìm đã được trộn vào nhau giữa 2, hay 3, . . . dòng trở lên thì phải chép hết chúng sang sheets báo cáo đang có tên là ‘Cau1’, vậy phát sinh vấn đề, cần tìm xem ngày cần tìm đó có 1 hay nhều dòng dữ liệu.

Muốn vậy, ta phải nhờ đến phương thức END(xlDown) trong câu lệnh được đánh số 16 để tìm ra số ô trong cột đã được trộn.

Trên trang tính ‘Data’, nếu 3 ô đã trộn ‘I2:I4’ được ta chọn; tiếp theo ta bấm {STRL} & nhấn kết hợp phím mũi tên xuống, ta sẽ đến được ô ‘I5’

Nhưng khi ta chọn ‘G2’, thậm chí cả 3 ô ‘G2:G4’, sau đó bấm {STRL} & kết hợp phím mũi tên xuống ta sẽ rơi vô ô ‘G9’ là ô cuối của cột ‘G:G’ liên tục có chứa dữ liệu

Điều đó nói lên rằng trong ‘I7:I9’ ta chỉ có ‘I7’ chứa dữ liệu mà thôi. (Nếu sau ‘I9’ không còn dữ liệu, ta sẽ đến ô cuối của cột ‘I:I’ này. (Điều này nói rõ hơn việc chúng ta sao lại phải thêm vô ‘I10’ giá trị 0 )

Trong macro dưới đây, tại [I2] ta có thể thay bằng [I3] hay [I4] cũng chỉ có cùng kết quả.

Mã:
[B]Sub MuiTenXuongMergesCells()[/B]
 [I2].Select:	                            MsgBox Selection.End(xlDown).Address
[B]End Sub[/B]

Trở lại với công cuộc chủ yếu của chúng ta. Các dòng lệnh 17 & 18 dùng để chép các dòng dữ liệu của ngày hôm đó sang sheets báo cáo.

Còn các dòng lệnh từ 19,100 . . . cho đến 109 là trộn các ô trên trang báo cáo cho giống với trang ‘Data’ theo yêu cầu của ‘Thượng đế’ HoangMinhTien mà thôi!
 

File đính kèm

Lần chỉnh sửa cuối:
Upvote 0
Tìm kiếm dữ liệu trong cột có các ô trộn(#2)


Tìm kiếm nhiều dòng dữ liệu, trong đó có một số dòng được trộn.

Chúng ta sẽ đề cập đến nhiệm vụ cần tìm & chép toàn bộ các dòng chứa dữ liệu tại sheets(“Data”) mà trên cột ‘M:M’ đang có trị ‘NG’ hay ‘OK’. Hơn nữa, một số trong các dòng dữ liệu này đã trộn (merge) tại một số trường.

Macrro sẽ thực thi các dòng lệnh từ dòng mang số 2 cho tới trước dòng mang số 3, một khi ta nhập vô ô [D4] một trong các trị nêu trên.

Chúng ta biết chắc 1 điều rằng, trị cần tìm có nhiều trong cột ‘M:M’, nên chúng ta, khác với lần tìm kiếm trị ngày bên trên, cần sử dụng biến MyAdd có dạng chuỗi để ghi lại địa chỉ ô chứa trị ‘NG’ hay ‘OK’ đầu tiên tìm được. Điều này giúp chúng ta thoát khỏi vòng lẫn quẫn hoài trong vòng lặp khi đã xong nhiệm vụ tìm kiếm. (Điều kiện để thoát vòng lặp được ghi tại dòng 211.)

Trình tự các bước trong khi tìm thấy 1 record tại ‘Data’ như sau:
• Gán địa chỉ ô được tìm thấy đầu tiên vô biện kiểu chuỗi;
1. Xác định số dòng mà các records có cùng ngày & cùng “JUDGMENT”. (Các ô được trộn chung trong các trường, trừ [PO] & [QTY]
2 Chép các dòng này sang sheets(“Cau1”) để lập báo cáo
3 Trộn các ô cần trộn ở ‘Cau1’ cho giống với dữ liệu bên ‘Data’
Quá trình tìm được lặp lại cho đến khi nào vẫn thỏa điều kiện, được ghi tại dòng lệnh 211

Trước đây ta hay làm bằng cách khác. Đó là chép record được tìm thấy vô biến kiểu Range đã khai báo trước, như sau:

PHP:
Dim cRng As Range, MyAdd As String
. . . .
Set sRng = Rng.Find(Target.Value, , xlFormulas, xlWhole)
    If Not sRng Is Nothing Then
        MyAdd = sRng.Address
        Do
	If cRng Is Nothing Then
	    Set cRng = sRng
          Else
              Set cRng = Union(cRng, sRng)	
          End If
          Set sRng = Rng.FindNext(sRng)
        Loop While Not sRng Is Nothing And sRng.Address <> MyAdd
    End If
    cRng.Copy Destination:= Sheets(“Cau1”).[A. .]

Làm như thông lệ như vậy sẽ nhanh hơn cách mà chúng ta chép từng dòng như trên. Nhưng ở đây, ta phải còn thực thi đòi hỏi của ‘Đấng Tối Cao” là trộn các ô của hầu hết các trường của các records vừa chép sang ‘Cau1’ cho giống y chang bên ‘Data’. Việc định dạng này được khẳng định trong macro bằng các dòng lệnh từ 201..209 tương ứng.

phát sinh câu hỏi, sao chúng ta không chép hết sang như lối mòn truyền thống, sau đó đi định dạng trộn các ô cần thiết?
Câu trả lời có thể là, ‘Việc hôm nay chớ để ngày mai; Nó sẽ hóa bùn mất!”. Một khi ta làm như vậy, lại phải đi xác định ô nào cần trôn với ô nào không cần trộn & quan trọng hơn là bao nhiêu ô trong 1 cột cần thiết phải trộn.




PHP:
' Đây là macro trong file đính kèm ở bài trên'
Option Explicit
Private Sub Worksheet_Change(ByVal Target As Range)
 On Error GoTo LoiWCh
 Dim Rng As Range, sRng As Range, cRng As Range
 Dim Zz As Long, jRw As Long:                   Dim Sh As Worksheet
 Dim MyAdd As String
 
 Application.ScreenUpdating = False:            Set Sh = Sheets("Data")
 Zz = Sh.[b65500].End(xlUp).Row
1 If Not Intersect(Target, [d3]) Is Nothing Then
    Range("B7").Resize(Zz).EntireRow.Delete
    Set Rng = Sh.Range(Sh.[i1], Sh.Cells(Zz, "i"))
13    Sh.Cells(Zz + 1, "i") = 0
    Set sRng = Rng.Find(Format(Target.Value, "Short Date"), , xlFormulas)
    If Not sRng Is Nothing Then
16       jRw = sRng.End(xlDown).Row - sRng.Row
       Set Rng = [b65500].End(xlUp).Offset(1, -1)
18       Rng.Resize(jRw, 13).Value = sRng.Offset(, -8).Resize(jRw, 13).Value
19       If jRw > 1 Then
100            For Zz = 1 To 13
                Rng.Offset(, Zz - 1).Resize(jRw).Select
                With Selection
                    .VerticalAlignment = xlCenter
                    If Zz <> 2 And Zz <> 7 Then .MergeCells = True
                End With
            Next Zz
106    End If:                                     End If
    Sh.Cells(Zz + 1, "i") = "":                 [d2].Select
    
2 ElseIf Not Intersect(Target, [d4]) Is Nothing Then
    Range("B7").Resize(Zz).EntireRow.Delete
    Set Rng = Sh.Range(Sh.[M1], Sh.Cells(Zz, "M"))
23    Sh.Cells(Zz + 1, "M") = "@"
    Set sRng = Rng.Find(Target.Value, , xlFormulas, xlWhole)
    If Not sRng Is Nothing Then
26        MyAdd = sRng.Address
        Do
28            jRw = sRng.End(xlDown).Row - sRng.Row
            Set cRng = [b65500].End(xlUp).Offset(1, -1)
200            cRng.Resize(jRw, 13).Value = sRng.Offset(, -12).Resize(jRw, 13).Value
            If jRw > 1 Then
                For Zz = 1 To 13
                    cRng.Offset(, Zz - 1).Resize(jRw).Select
                    With Selection
                        .VerticalAlignment = xlCenter
                        If Zz <> 2 And Zz <> 7 Then .MergeCells = True
                    End With
                Next Zz
            End If
            Set sRng = Rng.FindNext(sRng)
211        Loop While Not sRng Is Nothing And sRng.Address <> MyAdd
    End If
213    Sh.Cells(Zz + 1, "M") = "":                 [d2].Select
    
3 ElseIf Not Intersect(Target, [d5]) Is Nothing Then
    Range("B7").Resize(Zz).EntireRow.Delete
    Set Rng = Sh.Range(Sh.[M1], Sh.Cells(Zz, "M"))
    Sh.Cells(Zz + 1, "M") = "@"
    Set sRng = Rng.Find(Target.Value, , xlFormulas, xlWhole)
    If Not sRng Is Nothing Then
        MyAdd = sRng.Address
        Do
            jRw = sRng.End(xlDown).Row - sRng.Row
            Set cRng = [C65500].End(xlUp).Offset(1)
            cRng.Resize(, 11).Value = sRng.Offset(, -10).Resize(, 11).Value
            If jRw > 1 Then
                cRng.Offset(, 4).Value = Application.WorksheetFunction _
                    .Sum(sRng.Offset(, -6).Resize(jRw))
            End If
            Set sRng = Rng.FindNext(sRng)
        Loop While Not sRng Is Nothing And sRng.Address <> MyAdd
    End If
    Sh.Cells(Zz + 1, "M") = "":                 [d2].Select
 End If
 Exit Sub
LoiWCh:                                         End Sub
 
Upvote 0
Áp dụng phương thức để tìm các ô chứa 1 khoảng dữ liệu số.

Macro dưới đây sẽ tìm và tô màu những ô dữ liệu thỏa điều kiện nằm giữa hai trị (kiểu long) mà ta nhập vào;

Nó tìm & cho biết cả những số thực thỏa điều kiện, kể cả số âm lẫn số dương.

Nhưng chúng ta không thể bắt nó tìm bỡi giới han giữa hai số thực.

Nó cho phép tìm trong toàn bộ trang tính (khi ta kích hoạt ban đầu chỉ 1 ô) hay vùng dữ liệu chọn trước (Tất nhiên vùng này có thể là 1 cột, 1 hàng hay vài cột & vài hàng hoặc hai vùng cách nhau.

Số màu dùng để tô các ô tìm thấy chỉ là 5-7 màu, nên màu nền ở ô tìm thây giống nhau chưa hẵn là trị của chúng giống nhau. (Nếu cần, các bạn có thể phát triển thêm, bằng cách tô màu font khác nhau để phân biệt.)


PHP:
Option Explicit
Sub FindAllBetween2Nums()
 Dim StrNum As String:                      Dim bColor As Byte
 Dim lMin As Long, lMax As Long, CllsCount As Long
 Dim sRng As Range, Rng0 As Range, CRng As Range, FRng As Range
 
 StrNum = InputBox("Please enter the first value, then a comma, " _
    & vbNewLine & "followed by the second value" & _
    vbNewLine & "E.g. 1,19", "FIND BETWEEN 2 INTEGER")
 If StrNum = vbNullString Then Exit Sub
 On Error Resume Next
 lMin = Left(StrNum, InStr(1, StrNum, ","))
 lMax = Replace(StrNum, lMin & ",", "")
 If lMax < lMin Then
    CllsCount = lMax:                       lMax = lMin
    lMin = CllsCount
 End If
 If lMin + 1 = lMax Then
    MsgBox "No scope between Min and Max", vbCritical, "GPE.COM"
    Exit Sub
 End If
 If Selection.Cells.Count = 1 Then
    Set CRng = Cells.SpecialCells(xlCellTypeConstants, xlNumbers)
    Set FRng = Cells.SpecialCells(xlCellTypeFormulas, xlNumbers)
 Else
    Set CRng = Selection.SpecialCells(xlCellTypeConstants, xlNumbers)
    Set FRng = Selection.SpecialCells(xlCellTypeFormulas, xlNumbers)
 End If
 If CRng Is Nothing And FRng Is Nothing Then
    MsgBox "Your Selection contains no numbers", vbCritical, "GPE.COM"
    Exit Sub
 ElseIf CRng Is Nothing Or FRng Is Nothing Then
     Set sRng = IIf(FRng Is Nothing, CRng, FRng)
 Else
    Set sRng = Application.Union(FRng, CRng) 'Both'
 End If
 Set Rng0 = sRng.Cells(1, 1)
 Cells.ClearFormats:                        bColor = 33
 CllsCount = sRng.Cells.Count:
 Do Until CllsCount = 0
    CllsCount = CllsCount - 1
    Set Rng0 = sRng.Cells.Find("*", Rng0, LookIn:=xlValues, lookat:=xlWhole)
    If Rng0.Value > lMin And Rng0.Value < lMax Then
        bColor = bColor + 1:            If bColor = 41 Then bColor = 34
        Rng0.Interior.ColorIndex = bColor
    End If
 Loop
 On Error GoTo 0
End Sub
 

File đính kèm

Lần chỉnh sửa cuối:
Upvote 0
Phương thức FIND() với chuỗi ngày & giờ

Xin xem thêm trong file đính kèm để rõ thêm

PHP:
Option Explicit
Sub FindDateTime()
    Dim StrDate As String:                  Dim rCell As Range
    Dim lReply As Long, bColor As Integer
     
    Columns("A:A").NumberFormat = "mm/dd/yyyy hh:mm"
    Columns("B:B").NumberFormat = ("MM/dd/YYYY hh:mm:ss")
5    StrDate = Application.InputBox("Enter a  Date & Time to Locate on This  Worksheet", _
    "FIND DATE & TIME", Format(Now, "mm//dd/yyyy hh:mm:ss"), Type:=1)
    
    If StrDate = "False" Then Exit Sub
7    StrDate = Format(StrDate, "mm/dd/yyyy hh:mm:ss")
    On Error Resume Next
9    Set rCell = Cells.Find(CDate(StrDate), , xlFormulas, xlWhole)
    On Error GoTo 0
    If Not rCell Is Nothing Then
        bColor = rCell.Interior.ColorIndex + 1
        If bColor > 41 Or bColor < 34 Then bColor = 34
        rCell.Interior.ColorIndex = bColor
    Else
        lReply = MsgBox("Date & Time cannot be found. Try Again", vbYesNo)
        If lReply = vbYes Then Run "FindDateTime"
    End If
    Columns("A:A").NumberFormat = "dd/mm/yyyy hh:mm"
    Columns("B:B").NumberFormat = ("dd/mm/YYYY hh:mm:ss")

End Sub

Việc gì sẽ sảy ra, nếu ở các dòng lệnh có ghi số sẽ thay đổi như sau:
Mã:
91    Set rCell = Cells.Find(CDate(StrDate), , xlFormulas, [B]xlPart[/B])
Mã:
51    StrDate = Application.InputBox("Enter a  Date & Time to Locate on This  Worksheet", _
    "FIND DATE & TIME", Format(Now, "mm//dd/yyyy [B]hh:mm[/B]"), Type:=1)
Mã:
71    StrDate = Format(StrDate, "mm/dd/yyyy [B]hh:mm[/B]")
 

File đính kèm

Upvote 0
Tìm kiếm trong vùng có những ô bị ẩn

TÌM KIẾM TRONG VÙNG CÓ NHỮNG Ô BỊ ẨN​

Để tìm hiểu phương thức tìm kiếm trong vùng có một số ô dữ liệu bị ẩn, ta xét đến trang tính có chứa dữ liệu theo như ví dụ bảng sau:

|A|B|C|D|E
1|TT| MaNV |||
2|1|A001|||
3|2|A002|||
6|5|A005|||
7|6|A006||14|=MATCH("A013",B1:B24,0)
8|7|A007|||
9|8|A008||A011|=VLOOKUP(A12,A$1:B27,2)
10|9|A009|||
21|20|A020|||
22|21|A021||A018|=VLOOKUP(18,A$1:B39,2)
23|22|A022|||


Trên trang náy, ta thấy các dòng 4-5 & 11-20 đã bị ẩn đi;

Nếu tại [D7] ta nhập hàm =MATCH("A013",B1:B24,0), kết quả trả về tại đây là ‘14’
(Có nghĩa là hàm MATCH() vẫn xem các ô ẩn cũng như các ô không ẩn.)

Cũng tương tự như vậy với hàm VLOOKUP():
Tại [D9] ta lại dùng công thức =VLOOKUP(A12,A$1:B27,2). Sau khi ta nhấn phím {ENTER} sẽ thấy xuất hiện ‘A011’

Nhưng dùng phương thức FIND() để tìm theo macro sau:
PHP:
Option Explicit

Sub FINDHideCells()
 Dim Rng As Range, sRng As Range
 
 Set Rng = Range([b1], [b1].End(xlDown))
3  Set sRng = Rng.Find("A018", , xlFValues, xlWhole)
 If sRng Is Nothing Then
    MsgBox "No Find"
 Else
    MsgBox sRng.Address
 End If
End Sub

Chúng ta sẽ nhận được câu trả lời lạnh lùng: Không tìm thấy.

Chuyện sẽ khác hoàn toàn, nếu trong câu lệnh được gán số 3 trên kia, chỉ cần chuyền từ xlValues thành xlFormulas

Bạn nào chưa tin, hãy thử dù 1 lần để tự khẳng định.
 
Upvote 0
PHP:
Option Explicit
Sub FINDHideCells()
 Dim Rng As Range, sRng As Range
 Set Rng = Range([b1], [b1].End(xlDown))
3 Set sRng = Rng.Find("A018", , xlFValues, xlWhole)
If sRng Is Nothing Then
         MsgBox "No Find"
Else
        MsgBox sRng.Address
End If
End Sub

Với Macro trên, khi em thay xlComments, xlWhole thì không tìm được trong Comment, nhưng khi thay xlWhole thành xlPart lại hoạt động tốt.
Theo em hiểu xlWhole là tìm toàn bộ, còn xlPart là tìm một phần.
Vậy vì sao tìm trong Comments lại phải dùng phương thức tìm một phần? Nhờ các bạn giải thích giùm. Xin cảm nơn nhiều.
 
Chỉnh sửa lần cuối bởi điều hành viên:
Upvote 0
Quả thật mình cũng mới tìm hiểu về vấn đề này; Sau đây là 1 số kết quả ban đầu

PHP:
Sub FINDComments()
 Dim Rng As Range, sRng As Range, MyAdd As String
 
 Set Rng = Range([b1], [c2].End(xlDown))
2 Set sRng = Rng.Find("*", , xlComments, xlWhole)
 If sRng Is Nothing Then
    MsgBox "No Find"
 Else
    MyAdd = sRng.Address
    Do
        MsgBox sRng.Address:                ConNhieuThuKhacNua
6        sRng = Rng.FindNext(sRng)
    Loop While Not sRng Is Nothing And sRng.Address <> MyAdd
 End If
End Sub
Mã:
[B]Sub ConNhieuThuKhacNua()[/B]
 Dim Comm As Comment
 
 For Each Comm In ActiveSheet.Comments
    If Comm.Author = "GPE.COM" Then
        Comm.Delete
    Else
        MsgBox Comm.Text, , Comm.Author
    End If
 Next
[B]End Sub[/B]
Thứ nhất, mình thấy rằng, thêm vô chổ findWhat:= "*" thì ta có thể xài được thông số xlWhole mà macro không đi sai hướng đã định;
Thứ hai: Thay vì mình muốn tìm tất cả các ô chứa Comments thì macro đầu chưa cho ta thỏa, chắc phải tìm cách khác . . .
Nhưng khi gọi macro thứ hai, thì nó liệt kê tất tần tật các Comments có trên trang tính đang được kích hoạt

(Có thể trả lời này không đủ thỏa đáng với bạn, mong được thông cảm & cùng nhau, chúng ta ngâm cứu tiếp)
 
Upvote 0
Trích dữ liệu từ cột số gồm 5 hay 6 chữ số với yêu cầu có số hàng chục giống nhau

Ví dụ cần rút trích để phân làm 2 loại (được chia theo 2 màu xanh & đỏ) như sau:
|C|D|E|. . .
| 091281 |HJCU|2154254|
| 090201 |HJCU|2154954|
| 101282 |HJCU|2854254|
| 111208 |HJCU|2954254|
| 901200 |HJCU|2154999|. . .

Đây là yêu cầu của 1 bạn để giải quyết bài toán hoàn toàn có trong thực tế:
http://giaiphapexcel.com/forum/showthread.php?p=174537#post174537

Tại topic đó, tôi đã giải quyết bằng cách dùng vòng lặp duyệt từ đầu đến cuối; Hễ gặp trị số nào thỏa điều kiện thì rút trích sang trang tính bên cạnh; Đến lượt nó, sẽ là nguồn để tạo hai bảng báo cáo riêng biệt trên trang tính thứ ba (Xin các bạn xem thêm trong topic đó để rõ hơn)

Nhưng do biết dùng vòng lặp sẽ chậm hơn phương thức FIND(), nên sau một thời gian tiếp tục nghiên cứu hoàn thiện, xin giới thiệu với các bạn sử dụng phương thức FIND() để thực thi nhiệm vụ chắc chắn là nhanh hơn phương cách dùng vòng lặp trên.
Sau đây là macro (Xin các bạn xem file đính kèm):

Chú ý: Hai nút để kích hoạt macro đang là [L5] & [L35] để thay dữ liệu trong từng báo cáo tương ứng

Mã:
[B]Option Explicit[/B]
Private Sub Worksheet_Change(ByVal Target As Range)
'   '
 If Not Intersect(Target, [L5]) Is Nothing Then
   [c7].Resize(15, 3).ClearContents
   RutTrich "*0?", Target.Value
 ElseIf Not Intersect(Target, [L35]) Is Nothing Then
   [c37].Resize(15, 3).ClearContents
   RutTrich "*8?", Target.Value
 End If
[B]End Sub[/B]

PHP:
Sub RutTrich(StrC, Cot5)
 Dim Sh As Worksheet, Sht As Worksheet, Rng As Range, sRng As Range
 Dim MyAdd As String:                        Dim Num2 As Byte
 
 Set Sh = Sheets("DuLieu"):                  Set Sht = Sheets("TrG")
 Set Rng = Sh.Range(Sh.[c3], Sh.[c65500].End(xlUp))
 Sht.[b4].Resize(Rng.Rows.Count, 11).ClearContents
 Num2 = CInt(Left(Right(StrC, 2), 1))
 Set sRng = Rng.Find(StrC, , xlFormulas, xlPart)
 If Not sRng Is Nothing Then
   MyAdd = sRng.Address
   Do
      With sRng
         If (.Value \ 10) Mod 10 = Num2 And .Value / 10 ^ 4 >= 1 Then
            Sht.[b65500].End(xlUp).Offset(1).Resize(, 11).Value = _
               sRng.Offset(, -1).Resize(, 11).Value
         End If
      End With
      Set sRng = Rng.FindNext(sRng)
   Loop While Not sRng Is Nothing And sRng.Address <> MyAdd
 End If
 Set Sh = Nothing
 Set Rng = Switch(Num2 = 8, [c37], Num2 = 0, [c7])
 Rng.Resize(15, 3).Value = Sht.Cells(4 + 15 * (Cot5 - 1), "C").Resize(15, 3).Value
End Sub
 

File đính kèm

Lần chỉnh sửa cuối:
Upvote 0
Find bị lỗi, không tìm thấy Cell chứa giá trị Max

Mình viết Code này có tác dụng như sau:

1. Ở cột A, cứ mỗi loại T1,T2,T3…, thì lấy giá trị M2max, M3max gáng cho cột DJ, DK
2. Tô màu hàng được lấy M2max, M3max
3. Gáng tên của cột B, tương ứng với M2max, M3max vào cột DA

Hỏi GPE
Mình làm code làm được nhiệm vụ 1, còn nhiệm vụ 2, 3 mình làm code theo suy nghĩ là phù hợp, nhưng nó cứ gặp lỗi ở phương thức Find.
Mong các bạn giúp đở, cảm ơn các bạn GPE nhiều.

PHP:
'-- Cap nhat lai C1 max, C2 max
Sub Click_2()
Dim VungAs2 As Range
Dim Cell_M2max As Range
Dim Cell_M3max As Range
Dim eRow As Long
Dim i As Long
Dim n As Integer
Dim M2max As Double '-- Dung dang bien Double ko biet co hop ly ko???
Dim M3max As Double
'--**************************
Range([A7], [CP65536]).Interior.ColorIndex = xlNone
n = 5
eRow = [A65536].End(xlUp).Row
Set VungAs2 = [CO7] 'Vung de chon gia tri M2max
For i = 6 To eRow
  Set VungAs2 = Application.Union(Cells(i, "CO"), VungAs2)
  
  If Cells(i, "A").Value <> Cells(i + 1, "A").Value Then
    n = n + 1
    '-- Lay gia tri Max cua VungAs
    M2max = Application.Max(VungAs2)
    M3max = Application.Max(VungAs2.Offset(, 1))
    Cells(n, "DJ").Value = M2max
    Cells(n, "DK").Value = M3max
    
    '-- Tim Cells tai vi tri Max(VungAs2)
    '-- ??? --- Tai sao Find khong tim thay Cell nao vay, da so la tra ve gia tri Nothing???
    Set Cell_M2max = VungAs2.Find(What:=M2max, LookIn:=xlValues, LookAt:=xlWhole)
    Set Cell_M3max = VungAs2.Offset(, 1).Find(What:=M3max, LookIn:=xlValues, LookAt:=xlWhole)
    
    '-- To mau cho hang co M2max, M3max
    Range(Cells(Cell_M2max.Row, "A"), Cells(Cell_M2max.Row, "CP")).Interior.ColorIndex = 40
    Range(Cells(Cell_M3max.Row, "A"), Cells(Cell_M3max.Row, "CP")).Interior.ColorIndex = 40
    
    '-- Gang ten tim duoc vao cot "DA"
    Cells(n, "DA").Value = Cells(Cell_M2max.Row, "B").Value & "-" & Cells(Cell_M3max.Row, "B").Value
    '-- Bat dau lai VungA2 moi, cho cot A moi
    Set VungAs2 = Cells(i + 1, "CO")
  End If
Next
'--**************************
End Sub

Có file gởi kèm.
 

File đính kèm

Lần chỉnh sửa cuối:
Upvote 0
Mình viết Code này có tác dụng như sau:

1. Ở cột A, cứ mỗi loại T1,T2,T3…, thì lấy giá trị M2max, M3max gáng cho cột DJ, DK
2. Tô màu hàng được lấy M2max, M3max
3. Gáng tên của cột B, tương ứng với M2max, M3max vào cột DA

Hỏi GPE
Mình làm code làm được nhiệm vụ 1, còn nhiệm vụ 2, 3 mình làm code theo suy nghĩ là phù hợp, nhưng nó cứ gặp lỗi ở phương thức Find.
Mong các bạn giúp đở, cảm ơn các bạn GPE nhiều.

PHP:
'-- Cap nhat lai C1 max, C2 max
Sub Click_2()
Dim VungAs2 As Range
Dim Cell_M2max As Range
Dim Cell_M3max As Range
Dim eRow As Long
Dim i As Long
Dim n As Integer
Dim M2max As Double '-- Dung dang bien Double ko biet co hop ly ko???
Dim M3max As Double
'--**************************
Range([A7], [CP65536]).Interior.ColorIndex = xlNone
n = 5
eRow = [A65536].End(xlUp).Row
Set VungAs2 = [CO7] 'Vung de chon gia tri M2max
For i = 6 To eRow
  Set VungAs2 = Application.Union(Cells(i, "CO"), VungAs2)
  
  If Cells(i, "A").Value <> Cells(i + 1, "A").Value Then
    n = n + 1
    '-- Lay gia tri Max cua VungAs
    M2max = Application.Max(VungAs2)
    M3max = Application.Max(VungAs2.Offset(, 1))
    Cells(n, "DJ").Value = M2max
    Cells(n, "DK").Value = M3max
    
    '-- Tim Cells tai vi tri Max(VungAs2)
    '-- ??? --- Tai sao Find khong tim thay Cell nao vay, da so la tra ve gia tri Nothing???
    Set Cell_M2max = VungAs2.Find(What:=M2max, LookIn:=xlValues, LookAt:=xlWhole)
    Set Cell_M3max = VungAs2.Offset(, 1).Find(What:=M3max, LookIn:=xlValues, LookAt:=xlWhole)
    
    '-- To mau cho hang co M2max, M3max
    Range(Cells(Cell_M2max.Row, "A"), Cells(Cell_M2max.Row, "CP")).Interior.ColorIndex = 40
    Range(Cells(Cell_M3max.Row, "A"), Cells(Cell_M3max.Row, "CP")).Interior.ColorIndex = 40
    
    '-- Gang ten tim duoc vao cot "DA"
    Cells(n, "DA").Value = Cells(Cell_M2max.Row, "B").Value & "-" & Cells(Cell_M3max.Row, "B").Value
    '-- Bat dau lai VungA2 moi, cho cot A moi
    Set VungAs2 = Cells(i + 1, "CO")
  End If
Next
'--**************************
End Sub
Có file gởi kèm.
Cái này dùng Consolidate chỉ có vài dòng code là ra, ai lại Find rồi For chi cho cực vậy
Consolidate bạn làm bằng tay còn được nữa là...
 
Upvote 0
Cái này dùng Consolidate chỉ có vài dòng code là ra, ai lại Find rồi For chi cho cực vậy
Consolidate bạn làm bằng tay còn được nữa là...
Mình sẽ nguyên cứu lại xem, vì hiện giờ mình vẫn không biết Consolidate là gì, và dùng như thế nào nữa.
Hiện tại mong bạn nào đó giúp mình khắc phục cái lỗi find này với.cám ơn các bạn.
 
Upvote 0
Mình sẽ nguyên cứu lại xem, vì hiện giờ mình vẫn không biết Consolidate là gì, và dùng như thế nào nữa.
Hiện tại mong bạn nào đó giúp mình khắc phục cái lỗi find này với.cám ơn các bạn.
Rất dể xài ---> Gữi bạn file ví dụ (có hình minh họa)
Nếu bạn làm bằng tay thành công, hãy record macro quá trình bạn thao tác rồi chỉnh lại tí code là xong!
 

File đính kèm

Upvote 0
Rất dể xài ---> Gữi bạn file ví dụ (có hình minh họa)
Nếu bạn làm bằng tay thành công, hãy record macro quá trình bạn thao tác rồi chỉnh lại tí code là xong!
1. Em đã record macro lại, nhưng cũng không làm được gì với cái record đó, vì trong record nó có địa chỉ tuyệt đối của file, như thế nếu chép file vào chổ khác sẽ không chạy được (em có bỏ cái đĩa chỉ đó thử, ai gì bị lỗi) -> bó tay.
2. Consolidate không giải quyết được ý thứ 2 hoăc ý thư 3, Mục đích của mình là xác định được hàng nào chứa M2max, M3max (tô màu hàng đó) và để lấy giá trị cột B (B) gắng cho B (DA) tương ứng.
3. Không biết em hỏi bài ở đây có hợp không? Hay là mở một post mới
 
Upvote 0
Hiện tại mong bạn nào đó giúp mình khắc phục cái lỗi find này với.cám ơn các bạn.

Câu (1) bạn viết vậy là tốn thời gian
Mình làm cho bạn câu (1) & câu (2) như dưới đây, bạn chép toàn bộ thay cho cái macro của bạn; Chú í mình ghi kết quả trên các cột kề phải cột của bạn đã ghi kết quả; Bạn hãy sửa lại theo í của bạn.

Thêm 1 chú í về tên của macro đang lệnh nhau nhiều, bạn sửa lại luôn đi nhe!

Câu (3) chưa hiểu lắm, nên chưa làm

PHP:
Sub TimMax()
 Dim Clls As Range, Rng As Range, sRng As Range
 Dim Rngi As Range, mRng As Range, Rngs As Range
 Dim MyAdd As String
1 ' Tìm Giá Tri Cuc Dai:'
 Set Rng = Range([a6], [A65500].End(xlUp))
 For Each Clls In Range("Cz7:CZ" & [CZ99].End(xlUp).Row)
   Set Rngi = [co5]
   Set sRng = Rng.Find(Clls.Value, , xlFormulas, xlWhole)
   If Not sRng Is Nothing Then
      MyAdd = sRng.Address
      Do
         Set Rngi = Union(Rngi, Cells(sRng.Row, "CO"))
         Set sRng = Rng.FindNext(sRng)
      Loop While Not sRng Is Nothing And sRng.Address <> MyAdd
      With Cells(Clls.Row, "DL")
         .Value = Application.WorksheetFunction.Max(Rngi)
         .Offset(, 1).Value = Application.WorksheetFunction.Max(Rngi.Offset(, 1))
      End With
   End If
2 ' To Màu Các Ô Cuc Dai:'
   Set Rngs = Rngi.Find(Cells(Clls.Row, "DL").Value)
   If Not Rngs Is Nothing Then
      Cells(Rngs.Row, "A").Resize(, 9).Interior.ColorIndex = 34 + (Clls.Row Mod 6)
      Set Rngs = Nothing
   End If
   Set Rngs = Rngi.Offset(, 1).Find(Cells(Clls.Row, "DM").Value)
   If Not Rngs Is Nothing Then
      Cells(Rngs.Row, "A").Resize(, 9).Interior.ColorIndex = 42 - (Clls.Row Mod 6)
      Set Rngs = Nothing
   End If   
   If Not Rngi Is Nothing Then Set Rngi = Nothing
 Next Clls
End Sub
 
Upvote 0
2. Consolidate không giải quyết được ý thứ 2 hoăc ý thư 3, Mục đích của mình là xác định được hàng nào chứa M2max, M3max (tô màu hàng đó) và để lấy giá trị cột B (B) gắng cho B (DA) tương ứng.
trời ơi... tô màu sao không dùng Conditional Formating cho gọn --> Việc gì phải viết code!
File của bạn sao tôi thấy nó tùm lum quá, chẳng biết đường nào mà lần cả ---> Cả đóng cột ẩn và dòng ẩn mà chẳng có tí dử liệu nào
 
Upvote 0
Câu (1) bạn viết vậy là tốn thời gian
Mình làm cho bạn câu (1) & câu (2) như dưới đây, bạn chép toàn bộ thay cho cái macro của bạn; Chú í mình ghi kết quả trên các cột kề phải cột của bạn đã ghi kết quả; Bạn hãy sửa lại theo í của bạn.

Thêm 1 chú í về tên của macro đang lệnh nhau nhiều, bạn sửa lại luôn đi nhe!

Câu (3) chưa hiểu lắm, nên chưa làm
Code của bạn đúng như ý mình, mình đã chỉnh lại ý số 2. và để cho nó làm được ý 3 rồi này:

PHP:
Sub TimMax()
 Dim Clls As Range, Rng As Range, sRng As Range
 Dim Rngi As Range, mRng As Range, Rngs2 As Range, Rngs3 As Range
 Dim MyAdd As String
1 ' Tìm Giá Tri Cuc Dai:'
 Set Rng = Range([a6], [A65500].End(xlUp))
 For Each Clls In Range("Cz7:CZ" & [CZ99].End(xlUp).Row)
   Set Rngi = [co5]
   Set sRng = Rng.Find(Clls.Value, , xlFormulas, xlWhole)
   If Not sRng Is Nothing Then
      MyAdd = sRng.Address
      Do
         Set Rngi = Union(Rngi, Cells(sRng.Row, "CO"))
         Set sRng = Rng.FindNext(sRng)
      Loop While Not sRng Is Nothing And sRng.Address <> MyAdd
      With Cells(Clls.Row, "DL")
         .Value = Application.WorksheetFunction.Max(Rngi)
         .Offset(, 1).Value = Application.WorksheetFunction.Max(Rngi.Offset(, 1))
      End With
   End If
2 ' To Màu Các Ô Cuc Dai:'
   Set Rngs2 = Rngi.Find(Cells(Clls.Row, "DL").Value)
   If Not Rngs2 Is Nothing Then
      Cells(Rngs2.Row, "A").Resize(, 9).Interior.ColorIndex = 34 + (Clls.Row Mod 6)
   End If

   Set Rngs3 = Rngi.Offset(, 1).Find(Cells(Clls.Row, "DM").Value)
   If Not Rngs3 Is Nothing Then
      Cells(Rngs3.Row, "A").Resize(, 9).Interior.ColorIndex = 42 - (Clls.Row Mod 6)
   End If
   If Not Rngi Is Nothing Then Set Rngi = Nothing
3 ' Gang ten cho cot DA
   Cells(Clls.Row, "DA").Value = Cells(Rngs2.Row, "B").Value & "-" & Cells(Rngs3.Row, "B").Value
 Next Clls
End Sub
Tuy là đã làm được điều mình cần làm, nhưng nhìn thuật toán của bạn, bạn cũng dùng find như mình, cách bạn dùng là:
PHP:
Set Rngs2 = Rngi.Find(Cells(Clls.Row, "DL").Value)
Cách mình dùng là: (bị lỗi không tìm thấy Cell_M2max)
PHP:
Set Cell_M2max = VungAs2.Find(What:=M2max, LookIn:=xlValues, LookAt:=xlWhole)
Mình thử làm theo cách của bạn là bỏ đi các phần đằng sau cua find
PHP:
Set Cell_M2max = VungAs2.Find(What:=M2max)
Như thế này lại chạy ok, không biết phải lý giải như thế nào nửa???
???khó hiểu quá Hyên7 oi?
 
Lần chỉnh sửa cuối:
Upvote 0
trời ơi... tô màu sao không dùng Conditional Formating cho gọn --> Việc gì phải viết code!

Ở 7 tầng lầu đều có 7 anh có trị là MAX; nếu đang xài E2K3 thì khó mà dùng CF được NDU à!

To PMHoang:
Trước dòng lệnh
PHP:
Set Cell_M2max = VungAs2.Find(What:=M2max, LookIn:=xlValues, LookAt:=xlWhole)
Bạn thử thêm dòng lệnh
Mã:
MsgBox VungAs2.Address
xem sao;
Và quan trọng nhất là nó không tìm thấy ô đó mà bạn vẫn dùng lệnh gán thì báo lỗi là phải rồi
Bạn thử thêm các dòng lệnh sau vô sau dòng lệnh của bạn:

PHP:
If Cell_M2max Is Nothing Then
    MsgBox "Nothing",,"GPE Xin Luu Í:"
Else
   ' Câu lệnh gán của bạn'
End If
 
Lần chỉnh sửa cuối:
Upvote 0

Tưởng đâu đã làm xong bài toán này rồi, ai gì vẫn chưa xong, lý do là thế này:
1. Lúc trước mình làm thí nghiệm là cột C0 và CP là cột số. nên code chay ok
2. Bây giờ mình đặt công thức vào thì code nó lại bị báo lỗi nữa rồi.
Mình có file gởi kèm (có cả đoạn code của bạn Hyen17 nữa)

Code thí nghiệm ở cột CQ,CR lại làm được việc, còn code chính thức mình cần ở cột CP,CO lại không làm được việc
Trong file gởi kèm có nêu rõ chi tiết.
Mong các bạn giúp đở mình với. cảm ơn các bạn nhiều.
 

File đính kèm

Upvote 0
Vẫn chưa xong, lý do là thế này:
1. Lúc trước mình làm thí nghiệm là cột C0 và CP là cột số. nên code chay ok
2. Bây giờ mình đặt công thức vào thì code nó lại bị báo lỗi nữa rồi.

Code thí nghiệm ở cột CQ,CR lại làm được việc, còn code chính thức mình cần ở cột CP,CO lại không làm được việc
Đó là do tham số xlFormulas gây ra; Dù câu lệnh sau bạn xài 1 cách cụt ngủn như

Set sRng = Rng.FIND(GPE.Value) đi chăng nữa; Nhưng 1 lần nào trước đó, bạn đã xài tham số nói trên. Thì lúc đó Êxcel rất khôn ở chỗ vẫn coi như ta xài tham số đó như những lần trước đó.

Để khắc phục, bạn thử thay tham số trên bằng xlValues xem sao.

(Thấy chưa, khi ta viết tường minh câu lệnh, thì excel sẽ làm nhanh & chính xác hơn đó nghe!)
 
Upvote 0
Đó là do tham số xlFormulas gây ra; Dù câu lệnh sau bạn xài 1 cách cụt ngủn như

Set sRng = Rng.FIND(GPE.Value) đi chăng nữa; Nhưng 1 lần nào trước đó, bạn đã xài tham số nói trên. Thì lúc đó Êxcel rất khôn ở chỗ vẫn coi như ta xài tham số đó như những lần trước đó.

Để khắc phục, bạn thử thay tham số trên bằng xlValues xem sao.

Mình đã sửa cái Find theo LookIn:=xlValues, và nhiều thông số khác mà cũng ko có cái nào làm được việc cả. Chắc là cái Find này làm việc này không được rồi...
 
Upvote 0
Mình đã sửa cái Find theo LookIn:=xlValues mà cũng ko làm được việc cả. Chắc là cái Find này làm việc này không được rồi...

Chớ vội chán nản chứ. Còn cách nữa mà;
Nhưng trước tiên mình & bạn đồng í với nhau, nếu cột dữ liệu không phải do công thức đem lại thì OK, phải không?

Mình cũng gặp tuần trước cái vụ 2 phương thức tìm kiếm này không thể nhốt chung 1 chuồng For. . .Next được; Phải tách ra thôi

Mình gợi í cách tách, nếu khó quá, mình sẽ tiếp sức:

(1) Dời phần 2 ra khỏi vòng lặp:
* Ta phải khai báo biến mảng kiểu Range để chứa 7 vùng ứng với 7 tầng lầu trong khi đang trong vòng lặp
(2)
Áp dụng phương thức tìm ở vòng lặp thứ hai, các ô có giá trị cực đại

Nó cũng giống như ta dùng AdvancedFilter lọc dữ liệu của từng tầng ra riêng & từ kết quả lọc này ta tìm trị cực đại; Sau khi có trị cực đại ta tìm đến địa chỉ cực đại đó.

Hãy cố lên & sẽ có sự hỗ trợ.
 
Upvote 0
Mình đã sửa cái Find theo LookIn:=xlValues, và nhiều thông số khác mà cũng ko có cái nào làm được việc cả. Chắc là cái Find này làm việc này không được rồi...
Như tôi đã nói lần trước, bài này mà dùng Find là không khả thi ---> Ý kiến của tôi là dùng Consolidate, nhanh hơn gấp nhiều lần (tôi chắc với bạn như thế)
Code như sau:
PHP:
Sub LocMax()
  Dim Des As Range, Clls As Range
  On Error GoTo Thoat
  Range("CZ6:DK10000").ClearContents
  With Application.InputBox("Chon vung du lieu" & vbLf & _
    "Luu y: Khong chon tieu de", Type:=8)
    Application.ScreenUpdating = False
    .Interior.ColorIndex = 0
    Range("CZ6").Consolidate .Address(, , 2), 4, 0, 1
    Set Des = Range([CZ6], [CZ6].End(xlDown)).Resize(, .Columns.Count)
    Des.Offset(, 1).Resize(, .Columns.Count - 3).ClearContents
    Des.Offset(, 1).Resize(, .Columns.Count - 12).Delete 1
    For Each Clls In Des.Resize(, 1)
      With Range(.Cells, .Offset(-1))
        .AutoFilter 1, Clls.Text
        .AutoFilter .Columns.Count - 1, Clls(, 11).Text
        Intersect(.Cells, .Offset(1)).SpecialCells(12).Interior.ColorIndex = 44
        .AutoFilter
        .AutoFilter 1, Clls.Text
        .AutoFilter .Columns.Count, Clls(, 12).Text
        Intersect(.Cells, .Offset(1)).SpecialCells(12).Interior.ColorIndex = 44
        .AutoFilter
      End With
    Next Clls
Thoat:
    ActiveSheet.AutoFilterMode = False
    Application.ScreenUpdating = True
  End With
End Sub
- Vòng lập For trong code cũng chỉ quét qua 8 lần (nhờ sự trợ giúp của AutoFilter)
- Vùng dử liệu nguồn cho bạn tự chọn nhờ vào InputBox
Chạy thử file nhé
Lưu ý:
- Khi InputBox hiện ra, bạn chọn vùng dử liệu nguồn nhưng nhớ đừng chọn tiêu đề ---> chỉ chọn từ dòng 6 trở đi (A6 đến CP100 chẳng hạn)
 

File đính kèm

Upvote 0
Thêm 1 chiêu khác, khỏi dùng For luôn mà dùng Conditonal Formating để tô màu
Code ngắn hơn nhiều
PHP:
Sub LocMax()
  Dim Des As Range, Clls As Range
  On Error GoTo Thoat
  Range("CZ6:DK10000").ClearContents
  With Range([A6], [CR1000].End(xlUp))
    Range("CZ6").Consolidate .Address(, , 2), 4, 0, 1
    Set Des = Range([CZ6], [CZ6].End(xlDown)).Resize(, .Columns.Count)
    Des.Offset(, 1).Resize(, .Columns.Count - 3).ClearContents
    Des.Offset(, 1).Resize(, .Columns.Count - 12).Delete 1
Thoat:
  End With
End Sub
Có điều dùng CF thì lại không chơi được với InputBox
 

File đính kèm

Upvote 0
Thêm 1 chiêu khác, khỏi dùng For luôn mà dùng Conditonal Formating để tô màu
Phải công nhận anh ndu đầy sáng tạo. Tưởng như ko làm được thế mà vẫn làm được, nhất là cái điều kien CF.
Cảm ơn anh nhiều

Nhưng ko lấy được tên ở cột "B" (tương ứng với các hàng tô màu) đặt vào cột "DA"
 
Upvote 0
Mình gợi í cách tách, nếu khó quá, mình sẽ tiếp sức:

(1) Dời phần 2 ra khỏi vòng lặp:
* Ta phải khai báo biến mảng kiểu Range để chứa 7 vùng ứng với 7 tầng lầu trong khi đang trong vòng lặp
(2)
Áp dụng phương thức tìm ở vòng lặp thứ hai, các ô có giá trị cực đại

Nó cũng giống như ta dùng AdvancedFilter lọc dữ liệu của từng tầng ra riêng & từ kết quả lọc này ta tìm trị cực đại; Sau khi có trị cực đại ta tìm đến địa chỉ cực đại đó.

Hãy cố lên & sẽ có sự hỗ trợ.
Cảm ơn ban Hyen17 nhiều, mình sẽ cố gắng, có gì nhờ bạn giúp đở nhé.
À, mà mình đã nghĩ ra rồi. đúng là nếu cell chỉ có giá trị thì làm được, mà cell chứa công thức thì find nó tìm là tìm các ký tự trong công thức, chứ nó ko hiểu giá trị cuối cùng của cell, mình bỏ qua vba di, bây giờ mình cứ sử dụng find trong excel binh thường sẽ thấy, nếu mình tìm giá trị cuối cùng của cell có cong thức nó sẽ không tìm được, nhưng nếu mình tìm một chuổi ký tự gì đó trong công thức, chẳn hạn "CN15" thì nó tìm ra. Không biết đó có phải là lý do chính ko?
 
Lần chỉnh sửa cuối:
Upvote 0
Phải công nhận anh ndu đầy sáng tạo. Tưởng như ko làm được thế mà vẫn làm được, nhất là cái điều kien CF.
Cảm ơn anh nhiều

Nhưng ko lấy được tên ở cột "B" (tương ứng với các hàng tô màu) đặt vào cột "DA"
Lấy tên cột B uh? Quá dể ---> Bạn chỉ cần Advanced Filter cột A (1 cột thôi) với điều kiện Unique = True (lọc tại chổ, tức không copy sang nơi khác) ---> Nó sẽ lọc ra cột A những phần tử duy nhất ---> Khi ấy bên cột B bạn nhìn thấy có gì thì cứ copy và paste qua chổ nào bạn cần là xong!
Thử trước đi, khi nào không được tôi sẽ giúp (định làm luôn, nhưng sợ code nhiều khiến bạn rối)
 
Upvote 0
Áp dụng fương thức cho các ô trộn theo cùng hàng (dòng)

Vừa qua khi thử giải đáp cho câu hỏi tại http://www.giaiphapexcel.com/forum/showthread.php?38373-Trích-liệt-nội-dung-trong-bàng-tính, mình gặp trường hợp khá hi hữu & là lần đầu.
Trường hợp đó được mô tả (như file đính kèm) như sau:

Ta có dữ liệu tại cột ‘B’ gồm các hàng từ B2 đến B13 đều chứa chuỗi là “GPE”;
Giờ ta trộn các ô lại, như sau:
Trộn theo chiều dọc gồm: ô ‘B3:B4’ & ‘B6:B8’
Trộn theo chiều ngang gồm các ô ‘B10:C10’ & ‘B12:D12’
Khi ta chạy 3 lần macro dưới đây với biến Col nhập vào từ 1 đến 3 , ta thu kết quả lần lượt từ cột ‘F:G’ cho đến cột ‘J:K’

PHP:
Option Explicit
Sub TimTuOTron()
 Dim Rng As Range, sRng As Range
 Dim MyAdd As String:            Dim Col As Byte
 
 Col = InputBox("Só Cot Càn Tìm:", "GPE", 1)
 
 Set Rng = Range([A1], [A65500].End(xlUp)).Offset(, 1).Resize(, Col)
' Columns("F:G").Clear    '
 Cells(1, 4 + 2 * Col).Value = Rng.Address
 Set sRng = Rng.Find("GPE", , xlFormulas, xlWhole)
 If Not sRng Is Nothing Then
   MyAdd = sRng.Address
   Do
      With Cells(65500, 4 + 2 * Col).End(xlUp).Offset(1)
         .Value = Cells(sRng.Row, "A")
         .Offset(, 1).Value = sRng.Address
      End With
      Set sRng = Rng.FindNext(sRng)
   Loop While Not sRng Is Nothing And sRng.Address <> MyAdd
 End If
End Sub

Qua kết quả ta thấy rằng fương thức tìm kiếm, khi Col=1 sẽ không tìm thấy cụm từ cần tìm trong các ô trộn theo hàng ngang thuộc vùng từ ‘B1:B13’;
Khi Col=2 nó tìm ra chỉ ô trộn ngang thứ nhất, đó là ‘B10:C10’
Đến khi Col=3 nó mới tìm thấy thêm cụm từ nay trong ‘B12:D12’

Có nghĩa là khi ta dùng fương thức FIND() cho cột đầu tiên chứa ô trộn theo dòng, thì kết quả của fương thức là không tìm thấy (Nothing); Để tìm thấy trị trong ô trộn loại này ta fải tìm trong vùng gồm toàn bộ các cột có trong ô trộn.

(Vì là gặp lần đầu, nên mình viết ra đây để các bạn tham khảo thêm & chúc vui!
Cũng xin cảm ơn tác giả topic trích trên đã là nguồn cho bài này của tôi!)
 

File đính kèm

Lần chỉnh sửa cuối:
Upvote 0
Tiếp theo là giải quyết một nhiệm vụ thực tiển về trích lọc Sổ cái tài khoản từ Sổ Nhật ký chung.

Ta có Dữ liệu mẫu như sau :

hinh5.jpg



Vùng A1:D11 là dữ liệu của Sổ NKC
Vùng F1:I11 là kết quả trích lọc TK 111 từ sổ NCK ( Sổ cái TK 111)
Ta xét Macro sau :

PHP:
Sub TrichLocSocai()
    Dim Rng As Range, LastCell As Range
    Dim FirstAddress As String, R As Long, SaveRow As Long
    Application.ScreenUpdating = False
    With Range("B2:C11")
        Set LastCell = .Cells(.Cells.Count)
        Set Rng = .Find("111*", After:=LastCell, LookIn:=xlValues, LookAt:=xlWhole)
        FirstAddress = Rng.Address
        If Not Rng Is Nothing Then
            R = 2
            Do
                SaveRow = Rng.Row
                Select Case Rng.Column
                Case 2
                    Cells(R, 6) = Rng.Offset(, -1)
                    Cells(R, 7) = Rng.Offset(, 1)
                    Cells(R, 8) = Rng.Offset(, 2)
                Case 3
                    Cells(R, 6) = Rng.Offset(, -2)
                    Cells(R, 7) = Rng.Offset(, -1)
                    Cells(R, 9) = Rng.Offset(, 1)
                End Select
                R = R + 1
                Set Rng = .FindNext(Rng)
            Loop While FirstAddress <> Rng.Address And SaveRow <> Rng.Row
        End If
    End With
    Set LastCell = Nothing: Set Rng = Nothing
    Application.ScreenUpdating = True
End Sub

So với cách trích lọc dùng For Next thông thường thì Code này vượt trội hơn về tốc độ. Phù hợp với những cơ sở dữ liệu lớn.
Ta có thể kết hợp với thuộc tính End để làm cho dữ liệu tạo sự linh động cho dữ liệu gốc.

Trên đây là nội dung sơ lược về phương thức Find. Chúng ta sẽ cùng tìm hiểu sâu hơn về phương thức này trong các bài tiếp theo.


Em vẫn chưa hình dung được ý nghĩa của dòng này lắm, nhờ các bác giúp cho
PHP:
Loop While FirstAddress <> Rng.Address And SaveRow <> Rng.Row
 
Upvote 0
Để hiểu được câu lệnh
Loop While FirstAddress <> Rng.Address And SaveRow <> Rng.Row

Ta cần quay lại câu lệnh:

Set Rng = .Find("111*", After:=LastCell, LookIn:=xlValues, LookAt:=xlWhole)

Mà nghĩa tiếng Việt của nó là: Tìm trong vùng đã định ô nào chứa trị "111*" thì gán vô biến Rng (đã khai báo)

Nhưng ta đã
biết, trong vùng cần tìm không chỉ duy nhất 1 ô như vậy;
Nên câu lệnh iêu cầu tìm hoài cho đến khi nào chán thì thôi;

Khi nào chán?, Khi đó địa chỉ ô tìm thấy trùng với địa chỉ ô được tìm thấy lần đầu (Mà Đ/c này được tác giả cẩn thận ấn/gán vô biến FirstAddress & dòng tìm thấy trùng với dòng của ô tìm thấy đầu tiên (Rng.Row - Cái này tác giả lười, chưa gán vô biến nào, mà để khơi khơi vậy thôi)

(Thực ra mình dịch theo nghĩa Until, còn TG viết theo nghĩa While )


Mong là đã giúp bạn được ít nhiều!
 
Upvote 0
& dòng tìm thấy trùng với dòng của ô tìm thấy đầu tiên
Em vẫn ngu ở đoạn này: Theo em nghĩ là Rng.Row là biến chứ gồm nhiều giá trị trong quá trình tìm, sau mỗi lần tìm thấy thì tác giả luôn luôn đặt SaveRow = Rng.Row
Tại sao đến lúc nào đó SaveRow <> Rng.Row thì dừng?
 
Upvote 0
Cho em hỏi nếu đoạn Code sau (bỏ Findnext đi) chỉ còn ngắn gọn như sau

PHP:
Sub TrichLoc1()
    Dim Rng As Range, LastCell As Range, FirstAddress As String
    Set LastCell = Selection.Cells(Selection.Cells.Count)
    Set Rng = Selection.Find("1*", After:=LastCell, LookIn:=xlValues, LookAt:=xlWhole)
End Sub

Vùng chọn Selection gồm 2 ô A1:A3 với A1=1; A2=11; A3=112 thì Rng được hiểu là phương án nào trong 2 phương án sau:
1. Chỉ là 1 Cells duy nhất A1;
2. hay là Range gồm tập hợp 3 Cells (A1; A2, A3)?
 
Upvote 0
Cho em hỏi nếu đoạn Code sau (bỏ Findnext đi) chỉ còn ngắn gọn như sau

PHP:
Sub TrichLoc1()
    Dim Rng As Range, LastCell As Range, FirstAddress As String
    Set LastCell = Selection.Cells(Selection.Cells.Count)
    Set Rng = Selection.Find("1*", After:=LastCell, LookIn:=xlValues, LookAt:=xlWhole)
End Sub

Vùng chọn Selection gồm 2 ô A1:A3 với A1=1; A2=11; A3=112 thì Rng được hiểu là phương án nào trong 2 phương án sau:
1. Chỉ là 1 Cells duy nhất A1;
2. hay là Range gồm tập hợp 3 Cells (A1; A2, A3)?
Bạn thêm dòng MsgBox Rng.Address vào cuối code sẽ biết liền chứ gì
 
Upvote 0
Em vẫn ngu ở đoạn này: Theo em nghĩ là Rng.Row là biến chứ gồm nhiều giá trị trong quá trình tìm, sau mỗi lần tìm thấy thì tác giả luôn luôn đặt SaveRow = Rng.Row
Tại sao đến lúc nào đó SaveRow <> Rng.Row thì dừng?

Bạn HYen17 nhầm lẫn và bạn cũng hiểu sai. Ta xét code đã rút gọn như sau,
Mã:
 FirstAddress = Rng.Address
         If Not Rng Is Nothing Then
              R = 2
              Do
                   SaveRow = Rng.Row           '  <--- ô X
                   ...
                   Set Rng = .FindNext(Rng)    ' <--  ô Y
              Loop While FirstAddress <> Rng.Address And SaveRow <> Rng.Row   ' <-- t
         End If
Giải nghĩa: Sau khi tìm được ô đầu tiên - "Not Rng Is Nothing" thì code nhẩy vào vòng lặp. Sau khi vào vòng lặp thì trong biến SaveRow được lưu địa chỉ của ô "vừa tìm được". Ô "vừa tìm được" không phải là ô đầu tiên tìm được. Ở vòng lặp thứ n (n = 1, 2, ...) thì ô "vừa tìm được" là ô thứ n tìm được. Giả sử vòng lặp thứ n đang thực hiện (n = 1, 2, ...) thì SaveRow chứa địa chỉ ô thứ n tìm được - ô X. Nhưng ở cuối vòng lặp ta có
tức tại thời điểm đó nếu tìm được thì đó là ô Y <> ô X. Vậy tại thời điểm t thì SaveRow = X.Row trong khi đó Rng.Row = Y.Row
Câu "dòng tìm thấy trùng với dòng của ô tìm thấy đầu tiên" phải thay bằng "dòng tìm thấy trùng với dòng của ô tìm thấy ngay trước đó", hay nói rõ hơn thì "dòng của ô hiện thời tìm thấy trùng với dòng của ô được tìm thấy ngay trước đó" hoặc "dòng của ô thứ (n + 1) tìm thấy trùng với dòng của ô thứ n tìm thấy"
Và nên nhớ là với code như trên thì đk THOÁT vòng lặp là OR chứ không phải AND. Tức vòng lặp sẽ kết thúc khi FirstAddress = Rng.Address OR SaveRow = Rng.Row
tức thoát khi FirstAddress = Rng.Address, hoặc SaveRow = Rng.Row, hoặc cả 2
Tôi chỉ giải nghĩa khúc mắc của bạn, tôi không tìm hiểu xem liệu code có đúng hay không.
 
Upvote 0
Vẫn còn chuyện để nói về fương thức FIND() với những ô trộn

ColumnsMergeCells.JPG

Trong hình là một bảng dữ liệu thống kê để quản lý về đất đai;
Tác giả tạo bảng quản lí theo từng hộ;
Mỗi hộ ít nhiều gì cũng chiếm vài chục dòng gồm fần I (mà ta xem như là lí lịch trích ngang của hộ đó) gồm 6 dòng;
Fần II thống kê thửa đất của hộ đó;
(Các bạn thấy rằng biểu mẫu có rất nhiều những ô trộn)
Nhiệm vụ đề ra là ta tìm ra tất cả dữ liệu đã được tô nền màu vàng nhạt & chép vô trang tính khác.

Vấn đề khó nhất ở đây là áp dụng fương thức FIND() vào đâu để tìm ra các dữ liệu về "Số thứ tự thửa đất, "Số tờ của bản đồ", "Diện tích" đất & "Mục đích sử dụng"

Nếu quan sát thật chú tâm, ta sẽ thấy chỉ có thể căn cứ vô các dòng có nhiều ô trộn hàng ngang mang dử liệu "II - THỬA ĐẤT" (Dòng 70, 119,. . . )

Ta sẽ có 1 số cách để tìm kiếm ra các địa chỉ chứa chuỗi này bằng fương thức FIND() như trong macro sau:

PHP:
Option Explicit
Private Sub Worksheet_Activate()
 Dim Sh As Worksheet, Rng As Range, sRng As Range, Rg0 As Range
 Dim jJ As Byte:                                Dim MyAdd As String
 Const StrC As String = "CLN.ODT.HNK.GPE"
 
 [A3].Resize(9999, 8).ClearContents
 Set Sh = ThisWorkbook.Worksheets("SDC")
 Set Rng = Sh.Columns("A:V")
 Set sRng = Rng.Find(Sh.Range("IIT").Value, , xlFormulas, xlWhole)
 If Not sRng Is Nothing Then
    MyAdd = sRng.Address
    Do
1        With [A9999].End(xlUp).Offset(1)
         

9         End With
        Set sRng = Rng.FindNext(sRng)
    Loop While Not sRng Is Nothing And sRng.Address <> MyAdd
 Else
    MsgBox "Nothing"
 End If
 
 Set Sh = ThisWorkbook.Worksheets("SMK")
'. . . . . '
End Sub

Các câu lệnh giữa số 1 & số 9 sẽ để chép những dữ liệu cần tìm sang trang tính đang được kích hoạt (. . .)

Nhưng đến đây ta sẽ gặp lúng túng trong việc chép dữ liệu.

Để hiểu vấn đề, xin các bạn thêm giữa 2 dòng lệnh có đánh số dòng lệnh sau:

Mã:
[b]
    [COLOR=#ff0000]  MsgBox sRng.Offset(4,1).Address, , sRng.Offset(4).Offset(,1)
[/COLOR][/b]

(Các bạn có thể hiểu rõ thêm tại: http://www.giaiphapexcel.com/forum/...-lọc-dữ-liệu-trong-2-sheet-về-1-sheet-kết-quả)


Rất cảm ơn các bạn đã quan tâm!
 
Upvote 0
Find trong FindNext không thực hiện đúng tìm kiếm?

Nếu tôi thực hiện 1 kiểu FindNext trong 1 vùng dữ liệu thì làm đúng, nhưng sau khi tôi tìm dữ liệu có từ FindNext để làm cơ sở tìm kiếm 1 vùng dữ liệu khác thì nó chỉ thực hiện đúng 1 trường hợp đầu tiên. Tại sao?

Mã:
Option Explicit


Sub TEST()
    Dim rngNhap As Range, rngXuat As Range
    Dim curRng As String, fstRng As String
    
    ''restore:
    Sheet2.Range("A3:B12") = Sheet2.Range("D3:E12").Value
    
    MsgBox "Restore OK!"
    
    Set rngNhap = Sheet1.Range("A3:A18")
    
    With Sheet2.Range("B3:B12")
        
        Set rngXuat = .Find(What:="KH_1", LookIn:=xlFormulas, LookAt:=xlWhole)
        If Not rngXuat Is Nothing Then
            fstRng = rngXuat.Address
            Do
                MsgBox "Dia chi duoc tim thay: " & rngXuat.Address
                
[COLOR=#0000ff]                ''Tai sao khong the thuc hien dung khi co them mot FIND METHOD nua???[/COLOR]
[B][COLOR=#ff0000]                'rngNhap.Find(rngXuat.Offset(, -1), LookIn:=xlFormulas, LookAt:=xlWhole).Offset(, 1).ClearContents[/COLOR][/B]
                
                rngXuat.Offset(, -1).Resize(, 2).ClearContents
                Set rngXuat = .FindNext(rngXuat)
                If rngXuat Is Nothing Then Exit Do
            Loop Until rngXuat.Address = fstRng
        End If
    End With
End Sub
 

File đính kèm

Upvote 0


''Tai sao khong the thuc hien dung khi co them mot FIND METHOD nua???

'rngNhap.Find(rngXuat.Offset(, -1), LookIn:=xlFormulas, LookAt:=xlWhole).Offset(, 1).ClearContents
End Sub

Không thể được bạn thân mến à!
Mà chú mày chạy thử code này xem sao:

PHP:
Option Explicit
Sub TEST()
    Dim rngNhap As Range, rngXuat As Range
    Dim curRng As String, fstRng As String
    Dim J As Long
    
    ''restore:
    Sheet2.[D3].Resize(20, 3).Copy Destination:=Sheet2.[A3]
    
    MsgBox "Restore OK!"
    
    Set rngNhap = Sheet1.Range("A3:A18")
    
    With Sheet2.Range("B3:B12")
        
        Set rngXuat = .Find(What:="KH_1", LookIn:=xlFormulas, LookAt:=xlWhole)
        If Not rngXuat Is Nothing Then
            fstRng = rngXuat.Address
            Do
                MsgBox "Dia chi duoc tim thay: " & rngXuat.Address
                J = J + 1
                ''Tai sao khong the thuc hien dung khi co them mot FIND METHOD nua???
                'rngNhap.Find(rngXuat.Offset(, -1), LookIn:=xlFormulas, LookAt:=xlWhole).Offset(, 1).ClearContents
                
                rngXuat.Offset(, -1).Resize(, 2).Interior.ColorIndex = 34 + J
                rngXuat.Offset(, 1).Value = J
                
                Set rngXuat = .FindNext(rngXuat)
                If rngXuat Is Nothing Then Exit Do
            Loop Until rngXuat.Address = fstRng
        End If
    End With
End Sub

Có thể thử như sau:
Đang xài FIND() & trong đó có FINNEXT() rồi thì không xài FIND() được nựa nữa ngay cả trong macro đó,
Có thể xài FIND() nhiều lần & cuối macro là FINDNEXT() thì được.

Mình rút ra từ thực nghiệm thôi!

Cũng giống như tìm trong ô trộn ngang hay ô trộn dọc của bài trước đây vậy!

Thân!
 
Upvote 0
Nếu tôi thực hiện 1 kiểu FindNext trong 1 vùng dữ liệu thì làm đúng, nhưng sau khi tôi tìm dữ liệu có từ FindNext để làm cơ sở tìm kiếm 1 vùng dữ liệu khác thì nó chỉ thực hiện đúng 1 trường hợp đầu tiên. Tại sao?

Mã:
Option Explicit


Sub TEST()
    Dim rngNhap As Range, rngXuat As Range
    Dim curRng As String, fstRng As String
    
    ''restore:
    Sheet2.Range("A3:B12") = Sheet2.Range("D3:E12").Value
    
    MsgBox "Restore OK!"
    
    Set rngNhap = Sheet1.Range("A3:A18")
    
    With Sheet2.Range("B3:B12")
        
        Set rngXuat = .Find(What:="KH_1", LookIn:=xlFormulas, LookAt:=xlWhole)
        If Not rngXuat Is Nothing Then
            fstRng = rngXuat.Address
            Do
                MsgBox "Dia chi duoc tim thay: " & rngXuat.Address
                
[COLOR=#0000ff]                ''Tai sao khong the thuc hien dung khi co them mot FIND METHOD nua???[/COLOR]
[B][COLOR=#ff0000]                'rngNhap.Find(rngXuat.Offset(, -1), LookIn:=xlFormulas, LookAt:=xlWhole).Offset(, 1).ClearContents[/COLOR][/B]
                
                rngXuat.Offset(, -1).Resize(, 2).ClearContents
                Set rngXuat = .FindNext(rngXuat)
                If rngXuat Is Nothing Then Exit Do
            Loop Until rngXuat.Address = fstRng
        End If
    End With
End Sub

Sao nhiều người lập trình mà không đọc help nhỉ? Thường thì người lập trình đọc help - có trong phần
mềm, tìm được trên mạng, trong sách - đến nát bươm mà vẫn không thôi.

Tại sao? Tại sao? Tại bạn không đọc help. Hoặc đọc mà không hiểu.

Sếp ra lệnh: tìm một thằng có biệt hiệu là "béo". Bạn nói: đạ thưa sếp, em tìm thấy rồi. Sếp nói: "tìm
tiếp". Bạn tìm một thằng "béo" thứ hai? Sếp có nói tìm ai đâu, sếp chỉ nói: tìm tiếp. Thế mà bạn hiểu.
Sau đó sếp nói: tìm một thằng có biệt hiệu là "lác". Sau đó sếp nói: "tìm tiếp". Không nói tìm ai nhưng
bạn hiểu là tìm tiếp thằng "lác", đúng không? À, mà tại sao bạn biết tìm tiếp thằng "lác" chứ không phải
thằng "béo"?

Find/FindNext cũng có lôgíc tương tự. Tại sao bạn phải nhập What vào Find? Vì nếu không có thì "tìm
gió trong đồng trống" à? Hơi bị lôgíc, đúng không? Thế tại sao lại không nhập "tìm gì" vào FindNext?
Nếu nhập đầy đủ như Find thì còn sinh ra FindNext làm gì? Cứ một thằng Find là đủ, đúng không? Vậy
FindNext là "tìm tiếp" cái đã được thiết lập trong Find, mà là trong Find cuối cùng ("lác" chứ không
phải "béo"). Cũng hơi bị lôgíc, đúng không?

Nói có sách:

Mã:
Range.FindNext Method 
Continues a search that was begun with the Find method. Finds the next cell that [COLOR=#ff0000]matches those same 
[B]conditions[/B][/COLOR] and returns a Range object that represents that cell. Doesn’t affect the selection or the 
active cell

Bây giờ là code có cả dòng đỏ đỏ

Mã:
Sub TEST()
    Dim rngNhap As Range, rngXuat As Range
    Dim curRng As String, fstRng As String
    ''restore:
    Sheet2.Range("A3:B12") = Sheet2.Range("D3:E12").Value    
    MsgBox "Restore OK!"    
    Set rngNhap = Sheet1.Range("A3:A18")    
    With Sheet2.Range("B3:B12")        
        Set rngXuat = .Find(What:="KH_1", LookIn:=xlFormulas, LookAt:=xlWhole)
        If Not rngXuat Is Nothing Then
            fstRng = rngXuat.Address
            Do
                MsgBox "Dia chi duoc tim thay: " & rngXuat.Address                
                [COLOR=#ff0000]rngNhap.Find(rngXuat.Offset(, -1), LookIn:=xlFormulas, LookAt:=xlWhole).Offset(, 
1).ClearContents[/COLOR]                
                rngXuat.Offset(, -1).Resize(, 2).ClearContents
                Set rngXuat = .FindNext(rngXuat)
                [COLOR=#0000ff]If rngXuat Is Nothing Then Exit Do[/COLOR]
            Loop Until rngXuat.Address = fstRng
        End If
    End With

Ở vòng lặp Do đầu tiên ở chỗ đỏ đỏ bạn thiết lập tìm "NGHIA_2" do What = rngXuat.Offset(, -1) = "NGHIA_2". Vậy FindNext tìm tiếp "NGHIA_2" trong vùng Sheet2.Range("B3:B12"). Rõ ràng trong vùng Sheet2.Range("B3:B12") đâu có "NGHIA_2". Vậy kết quả trả về là Nothing, tức rngXuat = Nothing, và do vậy ra khỏi vòng lặp Do (dòng xanh xanh)
 
Lần chỉnh sửa cuối:
Upvote 0
Sao nhiều người lập trình mà không đọc help nhỉ? Thường thì người lập trình đọc help - có trong phần
mềm, tìm được trên mạng, trong sách - đến nát bươm mà vẫn không thôi.

Tại sao? Tại sao? Tại bạn không đọc help. Hoặc đọc mà không hiểu.

Sếp ra lệnh: tìm một thằng có biệt hiệu là "béo". Bạn nói: đạ thưa sếp, em tìm thấy rồi. Sếp nói: "tìm
tiếp". Bạn tìm một thằng "béo" thứ hai? Sếp có nói tìm ai đâu, sếp chỉ nói: tìm tiếp. Thế mà bạn hiểu.
Sau đó sếp nói: tìm một thằng có biệt hiệu là "lác". Sau đó sếp nói: "tìm tiếp". Không nói tìm ai nhưng
bạn hiểu là tìm tiếp thằng "lác", đúng không? À, mà tại sao bạn biết tìm tiếp thằng "lác" chứ không phải
thằng "béo"?

Find/FindNext cũng có lôgíc tương tự. Tại sao bạn phải nhập What vào Find? Vì nếu không có thì "tìm
gió trong đồng trống" à? Hơi bị lôgíc, đúng không? Thế tại sao lại không nhập "tìm gì" vào FindNext?
Nếu nhập đầy đủ như Find thì còn sinh ra FindNext làm gì? Cứ một thằng Find là đủ, đúng không? Vậy
FindNext là "tìm tiếp" cái đã được thiết lập trong Find, mà là trong Find cuối cùng ("lác" chứ không
phải "béo"). Cũng hơi bị lôgíc, đúng không?

Hỏi là gây chú ý vấn đề Find mà chưa có người nào đề cập tới thôi, chứ theo em hiểu thì "sếp" bảo tìm những thằng "béo" và sếp cũng ra lệnh rằng, từ những thằng "béo" sẽ khai ra những thằng "lác" nó ở đâu.

Từ việc dùng 2 thằng Find này mới biết rằng chúng chỉ dùng chung một công cụ hoạt động, nếu có một câu lệnh khác trên công cụ này thì công cụ này sẽ chuyển hướng khác ngay.

Nó khác hoàn toàn nếu ta thực hiện trên Range hay trên Array, việc nào ra việc đó, vùng nào ra vùng đó, không lằng nhằng, chồng chéo gì cả!
 
Upvote 0
Hỏi là gây chú ý vấn đề Find mà chưa có người nào đề cập tới thôi, chứ theo em hiểu thì "sếp" bảo tìm những thằng "béo" và sếp cũng ra lệnh rằng, từ những thằng "béo" sẽ khai ra những thằng "lác" nó ở đâu.

Từ việc dùng 2 thằng Find này mới biết rằng chúng chỉ dùng chung một công cụ hoạt động, nếu có một câu lệnh khác trên công cụ này thì công cụ này sẽ chuyển hướng khác ngay.

Nó khác hoàn toàn nếu ta thực hiện trên Range hay trên Array, việc nào ra việc đó, vùng nào ra vùng đó, không lằng nhằng, chồng chéo gì cả!

Gọi phương thức Find của range nào thì nôm na là vùng tìm sẽ là range đó. Thế thôi. Còn việc tìm gì do FindNext thực hiện thì được xác định ở Find. Lôgíc nó là thế và help cũng viết rõ ràng. Còn nếu bạn thắc mắc là lấy ở Find cuối hay lấy ở Find nào thì cũng giải thích được thỏa đáng. Giả sử có thể lấy ở Find thứ xyz thì rõ ràng phải nhập cái xyz đó vào. Vì nếu không thì biết lấy ở Find 1, 2, hay 100? Vậy thì nếu không phải nhập, không có chỗ nào nhập thì chỉ còn nước là hiểu lấy ở Find "mới nhất".
Tôi đã cho bạn vd. thực tiễn. Vì những người viết code cho Find/FindNext người ta cũng có cái lôgíc như trong "đời thường". Sếp nói: Tìm thằng "béo". Sau đó sếp nói: "tìm tiếp" (cụt lủn) thì bạn hiểu là tìm tiếp thằng "béo" sau thằng vừa tìm được. Một lúc sau sếp nói: Tìm thằng "lác". Sau đó sếp nói: "tìm tiếp" (cũng cụt lủn) nhưng bạn hiểu ngay là tìm tiếp thằng "lác" chứ không phải tìm tiếp thằng "béo", đúng không?
Nếu bạn lập trình trong Windows API thì bạn thấy nhiều trường hợp có lôgíc như thế. Vd. hàm FindFirstFile/FindNextFile. Tìm gì thì bạn phải nhập vào FindFirstFile. Nếu bạn muốn tìm tiếp cái cần tìm thì bạn gọi FindNextFile nhưng không phải nhập cái cần tìm nữa. Vì nó chẳng qua là "Tìm tiếp". Mà FindNext chính là "tìm tiếp" chứ là cái gì? Bạn có thể tìm tới khi không tìm thấy nữa mới thôi bằng cách cứ gọi FindNextFile trong vòng lặp. Tất nhiên ở thời điểm bất kỳ bạn có thể kết thúc tìm.

Bạn thấy, bạn cho là "lằng nhằng"?. Nếu bạn đọc kỹ help và hiểu từng câu chứ không phải là đọc lướt qua thì bạn thấy nó lôgíc. Mà cho dù bạn cho là không lôgíc thì thôi bạn cứ nhớ là Find/FindNext nó có cái lôgíc quái gở như thế. Và viết code đúng cái lôgíc quái đản như thế là được. Thế thôi.

Mà code người ta viết như thế bạn thấy dùng được thì dùng. Và muốn dùng thì phải theo cái "code" đó. Người ta viết rồi người ta giải thích trong help là code của người ta nó hoạt động thế nào mà bạn làm sai đi rồi bạn trách người ta "lằng nhằng"? Khó hiểu thật.
 
Upvote 0
Mà code người ta viết như thế bạn thấy dùng được thì dùng. Và muốn dùng thì phải theo cái "code" đó. Người ta viết rồi người ta giải thích trong help là code của người ta nó hoạt động thế nào mà bạn làm sai đi rồi bạn trách người ta "lằng nhằng"? Khó hiểu thật.

Để nó hoạt động thì chúng ta phải đặt Find vào trong vòng lặp, và "kêu" đích danh thằng "lính", sau đó, phải kêu tiếp thằng "lính" khác sau khi đã tìm được thằng "lính" đầu, mà không phải FindNext:

Mã:
Sub TEST()
    Dim rngXuat As Range, fstRng As String
    ''restore:
    Sheet2.Range("A3:B12") = Sheet2.Range("D3:E12").Value
    MsgBox "Restore OK!"
    
    Set rngXuat = Sheet2.Range("B3")
    fstRng = rngXuat.Address
    Do
        Set rngXuat = Sheet2.Range("B3:B12").Find([B][COLOR=#0000ff]What:="KH_1", _[/COLOR][/B]
[COLOR=#ff0000][B]                                                  After:=rngXuat, _[/B][/COLOR]
                                                  LookIn:=xlFormulas, _
                                                  LookAt:=xlWhole)
        If rngXuat Is Nothing Then Exit Do
        
        MsgBox "Dia chi duoc tim thay: " & rngXuat.Address
        
        Sheet1.Range("A3:A18").Find(What:=rngXuat.Offset(, -1), _
                                    LookIn:=xlFormulas, _
                                    LookAt:=xlWhole).Offset(, 1).ClearContents
                                    
        rngXuat.Offset(, -1).Resize(, 2).ClearContents
    Loop Until rngXuat.Address = fstRng
End Sub

Có như thế mới không sợ lộn thằng "lính" với thằng "lác"!
 
Lần chỉnh sửa cuối:
Upvote 0
Để nó hoạt động thì chúng ta phải đặt Find vào trong vòng lặp, và "kêu" đích danh thằng "lính", sau đó, phải kêu tiếp thằng "lính" khác sau khi đã tìm được thằng "lính" đầu, mà không phải FindNext:

Chẳng phải bài 61 HYen 17 đã nói:

Có thể thử như sau:
Đang xài FIND() & trong đó có FINNEXT() rồi thì không xài FIND() được nựa nữa ngay cả trong macro đó,
Có thể xài FIND() nhiều lần & cuối macro là FINDNEXT() thì được.
 
Upvote 0
Chẳng phải bài 61 HYen 17 đã nói:

Có thể thử như sau:
Đang xài FIND() & trong đó có FINNEXT() rồi thì không xài FIND() được nựa nữa ngay cả trong macro đó,
Có thể xài FIND() nhiều lần & cuối macro là FINDNEXT() thì được.

Ở đây có một tham số mà ta không cần phải FindNext đó chính là After:=NextRange. Với Find Method thì cách hoạt động của nó rất giống với Range ở điểm, nếu ta không xác định tên sheet, thì xem như địa chỉ là sheet hiện hành; vì thế với Find ta cũng phải xác định cho nó vùng nào là vùng tham chiếu, điều kiện nào là điều kiện dò tìm thì nó mới đảm bảo thực hiện được yêu cầu của mình, nếu không, nó lấy kết quả của lần tìm kiếm cuối cùng làm điều kiện What cho lần tiếp theo.
 
Upvote 0
Ở đây có một tham số mà ta không cần phải FindNext đó chính là After:=NextRange. Với Find Method thì cách hoạt động của nó rất giống với Range ở điểm, nếu ta không xác định tên sheet, thì xem như địa chỉ là sheet hiện hành; vì thế với Find ta cũng phải xác định cho nó vùng nào là vùng tham chiếu, điều kiện nào là điều kiện dò tìm thì nó mới đảm bảo thực hiện được yêu cầu của mình, nếu không, nó lấy kết quả của lần tìm kiếm cuối cùng làm điều kiện What cho lần tiếp theo.

Chỗ đỏ đỏ ... bó tay

Find là phương thức của range. Nếu ta gọi rng.find trong đó rng là range thì range đó là xác định trên sheet xác định rồi chứ sao lại là sheet hiện hành hay không hiện hành? Cho dù đang mở 1000 tập tin và mỗi tập tin có 1000 sheet thì cái rng kia là xác định và duy nhất.

1. Sheet1 là sheet hiện hành

2. Code
Mã:
Set rng = Sheet2.Range("A1:A12")
Set xyz = rng.Find(...)

Làm gì có chuyện "nếu ta không xác định tên sheet", vì sao lại phải "xác định" tên Sheet? rng là Range xác định trên Sheet xác định chứ làm gì có thể là Range trên Sheet khác Sheet2? Cũng làm gì có chuyện "thì xem như địa chỉ là sheet hiện hành"? Sheet2 đâu phải là Sheet hiện hành?

Khi ta gọi phương thức Find là ta đã gọi phương thức Find cho/của Range xác định. Và Range đó nằm trên Sheet xác định của tập tin xác định. Range đó là xác định và duy nhất, vậy sao lại phải "nếu ta không xác định tên sheet" hay ""nếu ta xác định tên sheet""?

Cũng chả phải "vì thế với Find ta cũng phải xác định cho nó vùng nào là vùng tham chiếu" Find đâu có phải là một hàm kiểu như các hàm COPUNT, SUM đâu mà phải xác định cho nó vùng tham chiếu, tức truyền tham số vào? Find là phương thức của Range. Vậy nếu ta gọi Find của Range rng, tức rng.Find thì vùng tìm là rng, nếu gọi phương thức của range rng2, tức rng2.Find thì vùng tìm là rng2. Thế thôi chứ "xác định cho nó" là nghĩa gì?
 
Upvote 0
Vậy theo các Thầy, em làm như thế này có bị gọi là kỳ kỳ không?

Mã:
Sub TEST()
    Dim rngXuat As Range, fstRng As String
    ''restore:
    Sheet2.Range("A3:B12") = Sheet2.Range("D3:E12").Value
    Sheet1.Range("B3:B8") = "IN USED"
    MsgBox "Restore OK!"
    
    Set rngXuat = Sheet2.Range("B3:B12").Find(What:="KH_1", _
                                              LookIn:=xlFormulas, _
                                              LookAt:=xlWhole)
    If rngXuat Is Nothing Then Exit Sub
    fstRng = rngXuat.Address
    
    Do
        Sheet1.Range("A3:A18").Find(What:=rngXuat.Offset(, -1), _
                                    LookIn:=xlFormulas, _
                                    LookAt:=xlWhole).Offset(, 1).ClearContents
                                    
        Set rngXuat = Sheet2.Range("B3:B12").Find(What:="KH_1", _
                                                  After:=rngXuat, _
                                                  LookIn:=xlFormulas, _
                                                  LookAt:=xlWhole)
        If rngXuat Is Nothing Then Exit Do
        
    Loop Until rngXuat.Address = fstRng
End Sub
 
Lần chỉnh sửa cuối:
Upvote 0
Vậy theo các Thầy, em làm như thế này có bị gọi là kỳ kỳ không?

Mã:
Sub TEST()
    Dim rngXuat As Range, fstRng As String
    ''restore:
    Sheet2.Range("A3:B12") = Sheet2.Range("D3:E12").Value
    Sheet1.Range("B3:B8") = "IN USED"
    MsgBox "Restore OK!"
    
    Set rngXuat = Sheet2.Range("B3:B12").Find(What:="KH_1", _
                                              LookIn:=xlFormulas, _
                                              LookAt:=xlWhole)
    If rngXuat Is Nothing Then Exit Sub
    fstRng = rngXuat.Address
    
    Do
        Sheet1.Range("A3:A18").Find(What:=rngXuat.Offset(, -1), _
                                    LookIn:=xlFormulas, _
                                    LookAt:=xlWhole).Offset(, 1).ClearContents
                                    
        Set rngXuat = Sheet2.Range("B3:B12").Find(What:="KH_1", _
                                                  After:=rngXuat, _
                                                  LookIn:=xlFormulas, _
                                                  LookAt:=xlWhole)
        If rngXuat Is Nothing Then Exit Do
        
    Loop Until rngXuat.Address = fstRng
End Sub
Trong code này em thấy một số vấn đề sau:
1. Thiếu câu lệnh On Error Resume Next, dẫn đến sẽ báo lỗi ở câu lệnh Set rngXuat... đầu tiên nếu không tìm thấy, tương tự như vậy với câu lệnh ClearContents.
2. Câu lệnh If rngXuat Is Nothing Then Exit Do là thừa vì điều kiện của If không thể xảy ra trong trường hợp này, nếu xảy ra thì nó đã Exit Sub từ trên rồi, ít ra trong trường hợp này thì rngXuat là ô đầu tiên tìm thấy (nếu chỉ có 1 ô thỏa mãn).
3. Em có thắc mắc nhỏ là tại sao không sử dụng xlValues mà lại là xlFormulas trong các tham số của Find?
 
Upvote 0
Trong code này em thấy một số vấn đề sau:
1. Thiếu câu lệnh On Error Resume Next, dẫn đến sẽ báo lỗi ở câu lệnh Set rngXuat... đầu tiên nếu không tìm thấy, tương tự như vậy với câu lệnh ClearContents.
2. Câu lệnh If rngXuat Is Nothing Then Exit Do là thừa vì điều kiện của If không thể xảy ra trong trường hợp này, nếu xảy ra thì nó đã Exit Sub từ trên rồi, ít ra trong trường hợp này thì rngXuat là ô đầu tiên tìm thấy (nếu chỉ có 1 ô thỏa mãn).
3. Em có thắc mắc nhỏ là tại sao không sử dụng xlValues mà lại là xlFormulas trong các tham số của Find?
1) Không cần On Error Resume Next, bởi ngoài vòng lặp ta đã dùng thủ tục này:

Mã:
    Set [COLOR=#008000][B]rngXuat [/B][/COLOR]= Sheet2.Range("B3:B12").Find(What:="KH_1", _
                                              LookIn:=xlFormulas, _
                                              LookAt:=xlWhole)
[COLOR=#ff0000][B]    If rngXuat Is Nothing Then Exit Sub[/B][/COLOR]
[B][COLOR=#0000ff]    fstRng = rngXuat.Address[/COLOR][/B]
Vì thế, khi nó lọt qua "lưới" màu đỏ là nó sẽ không bao giờ gặp lỗi Error 91.

Ta để Find này ở ngoài là vì chỗ màu xanh làm cái "hang thoát thân" nếu quay vòng lại, đồng thời cũng là nguồn cung cấp cho cái After:=rngXuat của Find trong vòng lặp.

2) Trong trường hợp code này thì nó sẽ thừa, nhưng nếu ta thêm câu lệnh:

Mã:
rngXuat.Offset(, -1).Resize(, 2).ClearContents

thì không có nó sẽ vướng lỗi Error 91.

3) Như đã đề cập ở topic "Đố vui căn bản", anh đã có nói vấn đề này rồi (#41), khi ta dùng xlFormulas sẽ nhanh hơn xlValues khi ta tìm kiếm với "mật độ" dày đặc điều kiện có trong vùng dữ liệu.
 
Lần chỉnh sửa cuối:
Upvote 0
1) Không cần On Error Resume Next, bởi ngoài vòng lặp ta đã dùng thủ tục này:

Mã:
    Set [COLOR=#008000][B]rngXuat [/B][/COLOR]= Sheet2.Range("B3:B12").Find(What:="KH_1", _
                                              LookIn:=xlFormulas, _
                                              LookAt:=xlWhole)
[COLOR=#ff0000][B]    If rngXuat Is Nothing Then Exit Sub[/B][/COLOR]
[B][COLOR=#0000ff]    fstRng = rngXuat.Address[/COLOR][/B]
Vì thế, khi nó lọt qua "lưới" màu đỏ là nó sẽ không bao giờ gặp lỗi Error 91.

Ta để Find này ở ngoài là vì chỗ màu xanh làm cái "hang thoát thân" nếu quay vòng lại, đồng thời cũng là nguồn cung cấp cho cái After:=rngXuat của Find trong vòng lặp.

2) Trong trường hợp code này thì nó sẽ thừa, nhưng nếu ta thêm câu lệnh:

Mã:
rngXuat.Offset(, -1).Resize(, 2).ClearContents

thì không có nó sẽ vướng lỗi Error 91.

3) Như đã đề cập ở topic "Đố vui căn bản", anh đã có nói vấn đề này rồi (không biết nó có còn tồn tại hay không), khi ta dùng xlFormulas sẽ nhanh hơn xlValues khi ta tìm kiếm với "mật độ" dày đặc điều kiện có trong vùng dữ liệu.
1. Đồng ý với anh vấn đề đặt một câu lệnh Set rngXuat =... ở ngoài vòng lặp, em cũng thường làm như vậy, và hình như ai cũng làm như vậy, nhưng chắc chắn rằng chính câu lệnh này sẽ gặp lỗi nếu trong vùng B3:B12 của Sheet2 không chứa giá trị cần tìm. Chính vì vậy mà em đề xuất thêm câu lệnh On Error Resume Next vào đầu Sub.

2. Em thấy câu trả lời của anh chưa thuyết phục, vì khi "lọt lưới" ở câu lệnh Set rngXuat = ... ngoài vòng lặp thì chắc chắn rằng điều kiện rngXuat Is Nothing không thể xảy ra (nếu xảy ra thì nó đã Exit Sub rồi mà), vậy thì cớ gì câu lệnh rngXuat.Offset(, -1).Resize(, 2).ClearContents lại vướng lỗi?!

3. Về vấn đề này thì em sẽ kiểm chứng lại, vì thực sự em chưa để ý đến vấn đề này, chỉ hiểu vấn đề đơn giản: xlValues là tìm trong giá trị ô (nếu có công thức thì nó tìm trong kết quả của công thức đó), còn xlFormulas là tìm trong chính công thức của ô nếu ô đó chứa công thức. Như vậy nếu tìm chuỗi "ABC" thì ô chứa công thức =A1="ABC" sẽ là một ô thỏa mãn điều kiện tìm nếu ta chọn xlFormulas, và là một ô không thỏa mãn điều kiện tìm nếu ta chọn xlValues (tất nhiên là với cùng tùy chọn xlPart).
 
Upvote 0
1. Đồng ý với anh vấn đề đặt một câu lệnh Set rngXuat =... ở ngoài vòng lặp, em cũng thường làm như vậy, và hình như ai cũng làm như vậy, nhưng chắc chắn rằng chính câu lệnh này sẽ gặp lỗi nếu trong vùng B3:B12 của Sheet2 không chứa giá trị cần tìm. Chính vì vậy mà em đề xuất thêm câu lệnh On Error Resume Next vào đầu Sub.

2. Em thấy câu trả lời của anh chưa thuyết phục, vì khi "lọt lưới" ở câu lệnh Set rngXuat = ... ngoài vòng lặp thì chắc chắn rằng điều kiện rngXuat Is Nothing không thể xảy ra (nếu xảy ra thì nó đã Exit Sub rồi mà), vậy thì cớ gì câu lệnh rngXuat.Offset(, -1).Resize(, 2).ClearContents lại vướng lỗi?!

3. Về vấn đề này thì em sẽ kiểm chứng lại, vì thực sự em chưa để ý đến vấn đề này, chỉ hiểu vấn đề đơn giản: xlValues là tìm trong giá trị ô (nếu có công thức thì nó tìm trong kết quả của công thức đó), còn xlFormulas là tìm trong chính công thức của ô nếu ô đó chứa công thức. Như vậy nếu tìm chuỗi "ABC" thì ô chứa công thức =A1="ABC" sẽ là một ô thỏa mãn điều kiện tìm nếu ta chọn xlFormulas, và là một ô không thỏa mãn điều kiện tìm nếu ta chọn xlValues (tất nhiên là với cùng tùy chọn xlPart).
1) Nhắc mới nhớ, là còn một Find thứ 2 chưa bẫy lỗi:

Mã:
Sub TEST()
    Dim rngNhap As Range, rngXuat As Range, fstRng As String
    ''restore:
    Sheet2.Range("A3:B12") = Sheet2.Range("D3:E12").Value
    Sheet1.Range("B3:B8") = "IN USED"
    MsgBox "Restore OK!"
    
    Set rngXuat = Sheet2.Range("B3:B12").Find(What:="KH_1", _
                                              LookIn:=xlFormulas, _
                                              LookAt:=xlWhole)
    If rngXuat Is Nothing Then Exit Sub
    fstRng = rngXuat.Address
    
    Do
        MsgBox rngXuat.Address
        
[COLOR=#0000ff]        Set rngNhap = Sheet1.Range("A3:A18").Find(What:=rngXuat.Offset(, -1), _[/COLOR]
[COLOR=#0000ff]                                    LookIn:=xlFormulas, _[/COLOR]
[COLOR=#0000ff]                                    LookAt:=xlWhole)[/COLOR]

[COLOR=#0000ff]        If Not rngNhap Is Nothing Then[/COLOR]
[COLOR=#0000ff]            rngNhap.Offset(, 1).ClearContents[/COLOR]
[COLOR=#0000ff]        End If[/COLOR]
                                    
[COLOR=#ff0000]        rngXuat.Offset(, -1).Resize(, 2).ClearContents[/COLOR]
        
        Set rngXuat = Sheet2.Range("B3:B12").Find(What:="KH_1", _
[COLOR=#008080]                                                  After:=rngXuat, _[/COLOR]
                                                  LookIn:=xlFormulas, _
                                                  LookAt:=xlWhole)
[COLOR=#800080]        If rngXuat Is Nothing Then Exit Do[/COLOR]
        
    Loop Until rngXuat.Address = fstRng
End Sub

Thường là lỗi bất khả kháng anh mới xài On Error Resume Next, còn không thì phải bẫy lỗi cho kỳ hết mới thôi, dĩ nhiên phải test nhiều tập.

2) Dòng màu đỏ xuất hiện, dòng màu tím bẫy cho nó. Nguyên tắc là, nếu không tìm thấy điều kiện nào thì nó thoát từ vòng loại là OK; tuy nhiên nếu vùng dữ liệu có chứa từ 2 điều kiện trở lên thì nó sẽ phát sinh ra lỗi tại dòng rngXuat.Address = fstRng. Cũng dễ hiểu thôi, nó đã xóa dòng điều kiện rồi thì rngXuat trở thành Nothing thì làm sao có được địa chỉ phải không?

3) nghiaphuc thử luôn trên cái file ở bài này (#1) sẽ biết liền chớ gì!

Em thử copy một ô có giá trị mà không có công thức, em paste special, chọn Formula thì nó ra kết quả là gì? Rồi từ đó em hiểu về thằng Find này. Dĩ nhiên ta phải biết rõ dữ liệu của ta có chứa công thức hay không mới có thể tìm kiếm được với kiểu nào là thuận tiện.

Với xlValues nó sẽ lấy giá trị dù có là công thức hay không, với xlFormula, nó chỉ lấy giá trị và loại trừ giá trị là kết quả từ công thức (có lẽ vì điều này mà nó nhanh hơn chăng?).
 
Lần chỉnh sửa cuối:
Upvote 0
Mình có đoạn code sau:

Dim Data, Arr, Result(), RngTV As Range, FCll1 As Range, FCll2 As Range, FindRng As Range, i As Long, j As Long
Data = Sheet1.Range(Sheet1.[D2], Sheet1.[A65536].End(xlUp))
Set RngTV = Sheet2.Range(Sheet2.[A4], Sheet2.[A65536].End(xlUp))
For i = 1 To UBound(Data, 1)
Set FCll1 = RngTV.Offset(, 2).Find(Data(i, 3), RngTV.Resize(1, 1).Offset(RngTV.Rows.Count - 1, 2), , 1, , 1)
Các bạn cho mình hỏi dòng bôi đỏ và in đậm được hiểu là đang tìm kiếm điều gì! mình so sánh với phương thức FInd được trình bày trong chủ đề mà vẫn chưa hiểu. Mong được sự giúp đở các bạn!
 
Upvote 0
Mình có đoạn code sau:

Dim Data, Arr, Result(), RngTV As Range, FCll1 As Range, FCll2 As Range, FindRng As Range, i As Long, j As Long
Data = Sheet1.Range(Sheet1.[D2], Sheet1.[A65536].End(xlUp))
Set RngTV = Sheet2.Range(Sheet2.[A4], Sheet2.[A65536].End(xlUp))
For i = 1 To UBound(Data, 1)
Set FCll1 = RngTV.Offset(, 2).Find(Data(i, 3), RngTV.Resize(1, 1).Offset(RngTV.Rows.Count - 1, 2), , 1, , 1)
Các bạn cho mình hỏi dòng bôi đỏ và in đậm được hiểu là đang tìm kiếm điều gì! mình so sánh với phương thức FInd được trình bày trong chủ đề mà vẫn chưa hiểu. Mong được sự giúp đở các bạn!
Câu lệnh của Range.Find Method nó chỉ như vầy thôi:

expression.Find(What, After, LookIn, LookAt, SearchOrder, SearchDirection, MatchCase, MatchByte, SearchFormat)

Nhóm có màu xanh tôi không có file nên không biết nó chứa gì trong đó và nó đại diện cho cái gì của After.
 
Upvote 0
vâng đó là cú pháp em vẫn nghĩ! nhưng phương thức FIND của hàm này khiến em đau cả đầu. Thực ra đây là 1 đoạn code trong cách "diễn giải nhật ký thi công " trên diễn đàn của anh Huuthang_bd. đoạn code đem lại hiệu quả rất tốt, nhưng để hiểu đoạn code em không tài nào giải thích được. các bạn có ý kiến gì khác hơn để giải thích đoạn code được không! Mình sẽ đính kèm file nhật ký có đoạn code đầy đủ.
 

File đính kèm

Lần chỉnh sửa cuối:
Upvote 0
PHP:
Dim Data, Arr, Result(), RngTV As Range, FCll1 As Range, FCll2 As Range, FindRng As Range, i As Long, j As Long


Data = Sheet1.Range(Sheet1.[D2], Sheet1.[A65536].End(xlUp))
Set RngTV = Sheet2.Range(Sheet2.[A4], Sheet2.[A65536].End(xlUp))
For i = 1 To UBound(Data, 1)
     Set FCll1 = RngTV.Offset(, 2).Find(Data(i, 3), RngTV.Resize(1, 1).Offset(RngTV.Rows.Count - 1, 2), , 1, , 1)
. . . . 
Next i
Để tìm hiểu câu lệnh màu đỏ đó là gì, ta cần hiểu rõ đâu là giới hạn của vùng RngTV.Offset(, 2)
Theo như câu lệnh trong đó, thì vùng xuất fát điểm của vùng này sẽ là RngTV;
Bản thân RngTV được diễn dịch như sau:
Đó là vùng dữ liệu thuộc cột [A] của trang tính có tên là ‘Sheet2’ bắt đầu từ [A4]
Vậy RngTV.Offset(, 2) sẽ là vùng dữ liệu cột [A] nêu trên được dịch chuyển sang fải 2 cột (nghĩa là cột [C]) cho dù vùng ở cột [C] này có dữ liệu hay không.
Đó là ta đã xác định vùng cần dò tìm;

Tiếp tục, ta sẽ fải xác định cần tìm cái gì trong vùng này;
Điều này thể hiện trong câu lệnh là Data(i,3)
Mà theo như câu lệnh đầu (không kể các mệnh đề định nghĩa biến trên nó), thì đây là 1 biến mảng đang chứa dữ liệu tại cột [A] bắt đầu từ [A2] cho đến ô cuối của cột này có chứa dữ liệu & mở rọng qua fải đến cột [D]
( Cú fáp này ta có thể viết khác đi chút, nhưng chưa nên nói tới ở đây)

Tiếp theo sẽ khảo sát vùng bắt đầu tìm, đó là vùng
RngTV.Resize(1, 1).Offset(RngTV.Rows.Count - 1, 2)
Thú thực mình chưa thể hiểu cái lằng nhằng này là thứ ôn dịch gì !?!
Thông thường tham số này hãn hữu người ta mới xài; còn ở đây muốn hiểu nó cần có file (như HT Nghĩa đã nêu)
Để đi tiếp ta có thể bỏ qua tham biến này (bằng cách là xóa nó đi trong câu lệnh)
Còn các tham biến còn lại theo thứ tự bạn có thể tìm trong bài của Hoàng Danh ở đâu topic.
Nhưng mình xin được bày tỏ là các tham biến này mình luôn xài của VBA đề xuất, như xlFormulas, xlPart,. .. .& mình chúa ghét cách xài như trên. (& bỡi ghép nên chỉ có lời khuyên với bạn là muốn tường tận về nó, bạn cần mở cửa số ‘Immediate’ trong VBE để có thêm trợ giúp khi cần.

Chúc vui trong ngày làm việc đầu tuần.
 
Upvote 0
Mặc dù vẫn chưa sáng tỏ hết cả câu lệnh, nhưng cũng cảm ơn bạn rất nhiều vì đã đóng góp ý kiến. Xin cậy nhờ thêm các cao thủ ra tay giúp với. Nói thật câu lệnh này làm mình đau hết cả đầu mấy tháng nay! Không biết tác giả có theo dõi không xin ra tay chỉ giáo.
 
Upvote 0
Một cách khác bạn có thể tận dụng, đó là hộp thoại MsgBox, như:

Trước hay sau dòng lệnh này ta xài 3 hộp thoại, như
PHP:
'
Set FCll1 = RngTV.Offset(, 2).Find(Data(i, 3), RngTV.Resize(1, 1).Offset(RngTV.Rows.Count - 1, 2), , 1, , 1)
'
 MsgBox RngTV.Offset(, 2).Address
 MsgBox Data(i, 3)
 MsgBox RngTV.Resize(1, 1).Offset(RngTV.Rows.Count - 1, 2).Address
 
Upvote 0
- RngTV là vùng từ A4 đến ô cuối cùng cột A chứa dữ liệu, ví dụ A4:A10;
- RngTV.offset(,2) là C4:C10
- RngTV.resize(1,1) là A4, RngTV.resize(1,1).offset(RngTV.rows.count-1,2) là ô C10. Tham số after ở đây là ô C10, vì sau C10 là C11 nằm ngoài phạm vi nên FIND sẽ bắt đầu tìm từ C4.
Nếu không có tham số after thì FIND sẽ bắt đầu tìm theo thứ tự C5---->C10->C4
Dùng after:=[c10] để ép FIND tìm theo thứ tự C4---->C10
 
Upvote 0
cảm ơn bạn! còn các thông số sau nhờ bạn giải thích hết đoạn code được không !
 
Lần chỉnh sửa cuối:
Upvote 0
Mình xin chỉ bạn cách lần ra những con số vô tri giác đó nè:

PHP:
RngTV.Resize(1, 1).Offset(RngTV.Rows.Count - 1, 2), , 1, , 1)

After, LookIn, LookAt, SearchOrder, SearchDirection, MatchCase, MatchByte, SearchFormat)

Đối chiếu với cú fáp của fương thức FIND() mà ta trích 1 fần bên trên thì

Thành tố RngTV.Resize(1, 1).Offset(RngTV.Rows.Count - 1, 2) ứng với After


Tiếp đến ta xét LookIn

Nó có thể chỉ là xlValues hay xlFormulas

Trong mã lệnh, tác giả đã bỏ qua tham biến này; Nhưng chúng ta & bạn cần thì khảo sát như sau:

Mở CS (cửa sổ) Immediate lên & nhập vô nớ dòng lệnh:
Mã:
? xlFormulas
Để xem nó trả về là số gì.

[thongbao]
LookIn : Không bắt buộc, khai báo dạng Variant. Đây là một dạng thông tin và thường có giá trị là xlValues. [/thongbao]
Cũng tương tự như vậy để bạn biết tham biến còn lại.


& cứ như vậy bạn tự tìm hiểu mới nhớ để bạn xài & chỉ có vậy bạn mới nhớ được!

[thongbao]
LookAt : Cách thức tìm kiếm. Là đối số tùy chọn, không bắt buộc, khai báo dưới dạng Variant. LookAt có 2 giá trị : xlWhole (Tìm toàn bộ) hoặc xlPart (tìm một phần).

SearchOrder : Xác định dạng thứ tự tìm kiếm, là tùy chọn không bắt buộc, khai báo dưới dạng Variant. SearchOrder có 2 dạng ứng với 2 hàng số : xlByRows (theo thứ tự dòng) hoặc xlByColumns (theo thứ tự cột)

SearchDirection : Hướng tìm kiếm, là tùy chọn. SearchDirection có 2 dạng ứng với 2 hằng số : xlNext (Tìm kế tiếp, là giá trị mặc định), xlPrevious (Tìm trước đó)

MatchCase : Là tùy chọn để xác định kiểu tìm kiếm có phân biệt chữ Hoa với chữ thường, khai báo dưới dạng Variant. Khai báo là True nếu ta muốn tìm kiếm chính xác. Giá trị mặc định là False.( Không phân biệt chữ in Hoa với chữ thường)

MatchByte : Không bắt buộc, khai báo dạng Variant. Chỉ sử dụng khi ta đã chọn hoặc cài đặt bộ hỗ trợ ngôn ngữ ký tự byte kép. Là True nếu ứng với bộ ký tự byte kép, False nếu ứng với bộ ký tự byte đơn.

SearchFormat : Tìm kiếm theo định dạng. Là tham số tùy chọn, khai báo dưới dạng Variant.
[/thongbao]

Nhưng là mình thì mình không bao giờ xài nhưng con số vô tri giác đó cho mệt óc!
 
Upvote 0
thanks sư phụ.
Nhờ sư phụ cho 2 cuốn sách mà cũng biết record macro sau đó sửa code Để sài--=0--=0
home nào sư phụ ghé biên hòa sẽ tiếp chiêu " Gà đi dép" nhé--=0--=0
 
Upvote 0
Tìm tên trong 1 danh sách lớn & báo kết quả

Giả sử tôi có danh sách dài họ tên những người cùng cơ quan được trích dưới đây:

[TABLE="width: 175"]
[TR]
[TD]TT[/TD]
[TD]Họ[/TD]
[TD]Tên[/TD]
[/TR]
[TR]
[TD="align: right"]1[/TD]
[TD]Ngô Thị[/TD]
[TD]Hoàng[/TD]
[/TR]
[TR]
[TD="align: right"]2[/TD]
[TD]Nguyễn Viết[/TD]
[TD]Hóa[/TD]
[/TR]
[TR]
[TD="align: right"]3[/TD]
[TD]Phạm [/TD]
[TD]Hùng[/TD]
[/TR]
[TR]
[TD="align: right"]4[/TD]
[TD]Nguyễn Dũng[/TD]
[TD]La[/TD]
[/TR]
[TR]
[TD="align: right"]5[/TD]
[TD]Ngô Khiết[/TD]
[TD]Tường[/TD]
[/TR]
[TR]
[TD="align: right"]6[/TD]
[TD]Trịnh Thanh[/TD]
[TD]Dũng[/TD]
[/TR]
[TR]
[TD="align: right"]7[/TD]
[TD]Lê Hoài[/TD]
[TD]Liên[/TD]
[/TR]
[TR]
[TD="align: right"]8[/TD]
[TD]Trần Minh[/TD]
[TD]Hòa[/TD]
[/TR]
[TR]
[TD="align: right"]9[/TD]
[TD]Hồ[/TD]
[TD]Đắc[/TD]
[/TR]
[TR]
[TD="align: right"]10[/TD]
[TD]Lê [/TD]
[TD]Dũng[/TD]
[/TR]
[TR]
[TD="align: right"]11[/TD]
[TD]Trương[/TD]
[TD]Hà[/TD]
[/TR]
[TR]
[TD="align: right"]12[/TD]
[TD]Tạ Quốc[/TD]
[TD]Kiệt[/TD]
[/TR]
[TR]
[TD="align: right"]13[/TD]
[TD]Từ Công[/TD]
[TD]Tốn[/TD]
[/TR]
[/TABLE]

Nhiệm vụ đề ra là tìm xem trong danh sách đó có 04 người có tên này không; Nếu có thì báo số dòng mà người đó đang có hay ngược lại báo là 'Không có'
[TABLE="width: 128"]
[TR]
[TD]Tìm[/TD]
[TD]Dòng[/TD]
[/TR]
[TR]
[TD]Hoa[/TD]
[TD]Không có[/TD]
[/TR]
[TR]
[TD]Hùng[/TD]
[TD] 4,[/TD]
[/TR]
[TR]
[TD]Dũng[/TD]
[TD] 11, 7,[/TD]
[/TR]
[TR]
[TD]Khiết[/TD]
[TD]Không có[/TD]
[/TR]
[/TABLE]

Macro có nội dung như sau:

PHP:
Option Explicit
Const MyErr = 1 + vbObjectError + 512
Sub TimKiem()
 Dim Cls As Range, sRng As Range, Rng As Range
 Dim MyAdd As String
 On Error GoTo LoiCT
 
 Set Rng = Columns("c:C")
 For Each Cls In Range([G2], [G2].End(xlDown))
    Set sRng = Rng.Find(Cls.Value, , xlFormulas, xlWhole)
    If sRng Is Nothing Then
        Err.Raise Number:=MyErr
    Else
        MyAdd = sRng.Address
        Do
            Cls.Offset(, 1).Value = Str(sRng.Row) & "," & Cls.Offset(, 1).Value
            Set sRng = Rng.FindNext(sRng)
        Loop While Not sRng Is Nothing And sRng.Address <> MyAdd
    End If
 Next Cls
Err_:                   Exit Sub
LoiCT:
    If Err = MyErr Then
        Cls.Offset(, 1).Value = "Không có"
        Resume Next
    Else
        Resume Err_
    End If
End Sub
 
Upvote 0
Tìm ngày (mm/dd/yyyy) trong trường/cột số liệu kiểu [MM/dd/yyyy/hh:mm:cc]

Ta có 1 dữ liệu được trích ra như sau:
[TABLE="width: 240"]
[TR]
[TD]Ngày[/TD]
[TD]Thành tiền[/TD]
[/TR]
[TR]
[TD="align: right"]1/3/2017 0:00[/TD]
[TD="align: right"]4.95[/TD]
[/TR]
[TR]
[TD="align: right"]1/3/2017 1:12[/TD]
[TD="align: right"]5.10[/TD]
[/TR]
[TR]
[TD="align: right"]1/3/2017 2:24[/TD]
[TD="align: right"]5.25[/TD]
[/TR]
[TR]
[TD="align: right"]1/3/2017 3:36[/TD]
[TD="align: right"]5.40[/TD]
[/TR]
[TR]
[TD="align: right"]1/3/2017 4:48[/TD]
[TD="align: right"]5.55[/TD]
[/TR]
[TR]
[TD="align: right"]1/4/2017 6:00[/TD]
[TD="align: right"]5.70[/TD]
[/TR]
[TR]
[TD="align: right"]1/4/2017 7:12[/TD]
[TD="align: right"]5.85[/TD]
[/TR]
[TR]
[TD="align: right"]1/4/2017 8:24[/TD]
[TD="align: right"]6.00[/TD]
[/TR]
[TR]
[TD="align: right"]1/4/2017 9:36[/TD]
[TD="align: right"]6.15[/TD]
[/TR]
[TR]
[TD="align: right"]1/4/2017 10:48[/TD]
[TD="align: right"]6.30[/TD]
[/TR]
[TR]
[TD="align: right"]1/4/2017 12:00[/TD]
[TD="align: right"]6.45[/TD]
[/TR]
[TR]
[TD="align: right"]1/4/2017 13:12[/TD]
[TD="align: right"]6.60[/TD]
[/TR]
[TR]
[TD="align: right"]1/4/2017 14:24[/TD]
[TD="align: right"]6.75[/TD]
[/TR]
[TR]
[TD="align: right"]1/4/2017 15:36[/TD]
[TD="align: right"]6.90[/TD]
[/TR]
[TR]
[TD="align: right"]1/5/2017 16:48[/TD]
[TD="align: right"]7.05[/TD]
[/TR]
[TR]
[TD="align: right"]1/5/2017 18:00[/TD]
[TD="align: right"]7.20[/TD]
[/TR]
[TR]
[TD="align: right"]1/5/2017 19:12[/TD]
[TD="align: right"]7.35[/TD]
[/TR]
[TR]
[TD="align: right"]1/5/2017 20:24[/TD]
[TD="align: right"]7.50[/TD]
[/TR]
[TR]
[TD="align: right"]1/5/2017 21:36[/TD]
[TD="align: right"]7.65[/TD]
[/TR]
[/TABLE]

Nhiệm vụ đề ra là thống kê tổng số đạt được của các ngày trong 1 danh sách như dưới đây:

[TABLE="width: 165"]
[TR]
[TD]Ngày[/TD]
[TD]Tổng tiền[/TD]
[/TR]
[TR]
[TD="align: right"]3/3/2017[/TD]
[TD="align: right"]1,377.06[/TD]
[/TR]
[TR]
[TD="align: right"]3/4/2017[/TD]
[TD="align: right"]1,413.06[/TD]
[/TR]
[TR]
[TD="align: right"]3/5/2017[/TD]
[TD="align: right"]1,205.05[/TD]
[/TR]
[TR]
[TD="align: right"]3/9/2017[/TD]
[TD="align: right"]1,341.80[/TD]
[/TR]
[TR]
[TD="align: right"]3/10/2017[/TD]
[TD="align: right"]21.60[/TD]
[/TR]
[TR]
[TD="align: right"]3/11/2017[/TD]
[TD="align: right"]1,381.20[/TD]
[/TR]
[TR]
[TD="align: right"]3/13/2017[/TD]
[TD="align: right"]1,719.06[/TD]
[/TR]
[TR]
[TD][/TD]
[TD][/TD]
[/TR]
[/TABLE]
Nhiệm vụ này sẽ được hoàn thành bỡi macro sau:

PHP:
Option Explicit
Sub FIND_()
 Dim Rng As Range, sRng As Range, Arr()
 Dim Rws As Long, J As Long, Tmr As Double, Tmp As Double
 Dim MyAdd As String
 
 Tmr = Timer()
 Rws = [c4].CurrentRegion.Rows.Count
 Set Rng = [c4].Resize(Rws)
 Arr() = Range([f4], [f4].End(xlDown)).Value
 ReDim dArr(1 To UBound(Arr()), 1 To 1)
 For J = 1 To UBound(Arr())
    Set sRng = Rng.Find(Arr(J, 1), , xlValues, xlPart)  '**'
    If Not sRng Is Nothing Then
        MyAdd = sRng.Address
        Do
            dArr(J, 1) = sRng.Offset(, 1).Value + dArr(J, 1)
            Set sRng = Rng.FindNext(sRng)
        Loop While Not sRng Is Nothing And sRng.Address <> MyAdd
    End If
 Next J
 [g4].Resize(J - 1).Value = dArr()
 [m5].Value = Timer() - Tmr
End Sub
 
Upvote 0
Thanks đã share bài viết, rất bổ ích với mình :)
 
Upvote 0
Chào các bác, em muốn nhờ các bác viết dùm em Code để đưa dữ liệu vào các bảng tính tổng hợp từ 1 bảng tính. Cụ thể như sau:
Trong file em có Sheet "DL" là để nhập dữ liệu từ ô B1 đến ô B9
các Sheet còn lại là bảng tổng hợp cho dữ liệu được đưa vào từ Sheet "DL" ; Trong các bảng tính tổng hợp này thì 2 cột A và B là cố định không được phép thay đổi theo thứ tự đã có.

Việc em muốn là dữ liệu từ các ô B3 đến ô B9 đưa vào các dòng tương ứng với điều kiện trong các Sheet tổng hợp từ cột C đến cột I
Điều kiện để đưa dữ liệu vào như sau:
Tại Sheet "DL" ô B1 và ô B2 là điều kiện để copy dữ liệu vào Sheet tổng hợp : Với ô B1 là mã số thuế đối chiếu với cột A của Sheet tổng hợp để đưa dữ liệu từ ô B3 đến ô B9 vào dòng có mã số thuế tương ứng ; Với ô B2 là điều kiện để đối chiếu đưa vào Sheet tổng hợp nào.

Ví dụ cụ thể:
Với điều kiện là mã số thuế nhập tại ô B1 là 2901728621 và điều kiện kỳ kê khai tại ô B2 là 01/2017 thì các dữ liệu tại ô B3 đến ô B9 sẽ đưa vào Sheet " Thang 1" tương ứng với dòng 12 là từ ô C12 đến I12. (Tương tự với các mã số thuế khác)
Tương tự với mã số thuế nhập tại Sheet "DL" là 2901862001 và kỳ kê khai là 02/2017 thì các dữ liệu tại ô B3 đến ô B9 sẽ đưa vào Sheet " Thang 2" tương ứng với dòng 8 là từ ô C8 đến I8. (Tương tự với các mã số thuế khác)

Nếu như mã số thuế tại ô B1 của Sheet "DL" không có trong các Sheet tổng hợp thì sẽ hiện thông báo là "Ma So Thue Khong Co Trong Bang Tong Hop"
Và sau khi đưa dữ liệu vào Sheet tổng hợp xong thì dữ liệu được nhập tại ô B1 đến ô B9 sẽ được làm trống để nhập dữ liệu khác.

Rất mong các bác giúp đỡ. :D
 

File đính kèm

Lần chỉnh sửa cuối:
Upvote 0
Bạn vận hành thử file này & cho biết í kiến xem sao

(Xin các bạn xem file tại bài tiếp theo & xin cáo lỗi về chuyện bất tiện này!)
 
Lần chỉnh sửa cuối:
Upvote 0
Bạn vận hành thử file này & cho biết í kiến

Hay quá, nhưng bác cho em hỏi thêm.
Đoạn code này thì khi đặt Button tại Sheet "DL" thì mới thực hiện được và Code không báo lỗi.
Còn nếu đặt ở bất kỳ một Sheet nào khác thì không thực hiện được mà Code sẽ báo lỗi ngay
Vậy có cách nào để khắc phục để có thể tùy ý đặt Button ở bất cứ Sheet nào cũng đc được không bác?

Dòng Code báo lỗi:
Mã:
 Set Sh = ThisWorkbook.Worksheets("Q" & Right("0" & CStr(Month([b2].Value)), 2))
 
Upvote 0
Chuyện "Button tại Sheet 'DL'" đang là hiễn nhiên, do
(1) Bạn đã gắn vố đó, mà không/chưa nêu vấn đề là nó có thể ở trang tính khác;
(2) Dữ liệu vừa nhập đang ở trang đó; Sao lại phải di chuyển đến trang khác làm chi vậy?
Cứ nhấn nút để nó nhập hay báo cho ta biết là không nhập được vô đó, & có thể nhập vô trang tính khác;
Công việc của bạn cùng lắm sẽ là lấy viết ra ghi lại các trang tính có thể nhập mà thôi;

Còn vấn đề đứng ở các trang khác mà nhấn nút & macro vẫn chạy đúng không phải là không làm được; Nhưng bạn phải cho biết lí do & nó là chính đáng không đã!

Chúc vui & thành công.
 
Upvote 0
Em xin trích câu hỏi của bác để diễn giải cho dễ nhé.

Chuyện "Button tại Sheet 'DL'" đang là hiễn nhiên, do
(1) Bạn đã gắn vố đó, mà không/chưa nêu vấn đề là nó có thể ở trang tính khác;
Cái này là do em quên, cứ nghĩ gắn ở đâu cũng đc nên để vào đó cho tiện :D


(2) Dữ liệu vừa nhập đang ở trang đó; Sao lại phải di chuyển đến trang khác làm chi vậy?
Cứ nhấn nút để nó nhập hay báo cho ta biết là không nhập được vô đó, & có thể nhập vô trang tính khác;
Công việc của bạn cùng lắm sẽ là lấy viết ra ghi lại các trang tính có thể nhập mà thôi;
Thực tế thì file làm việc ấy của em nhiều Sheet hơn, nhiều dòng và cột hơn thế nhiều ấy ạ.
Em đã học làm đc 1 số Form nhập liệu đặt ở tất cả 16 Sheet trong file Excel, với cái Form nhập liệu để ghi vào ô B1 đến ô B9. Vì khi kiểm tra, đối chiếu số liệu của Doanh nghiệp còn cần phải đứng ở Sheet tương ứng với thời kỳ kiểm tra. Cái Forrm nhập liệu ấy em vừa nhập được và cũng có thể gọi lại số liệu từ ô B1:B9 trong Sheet "DL" vào lại form để có thể sửa và ghi lại khi đứng ở bất kỳ Sheet nào, đồng thời sau khi đối chiếu xong thì mới ghi dữ liệu vào các Sheet tổng hợp nên em muốn cái nút ghi dữ liệu vào các Sheet mà bác tạo Code giúp em cũng nằm được trên bất kỳ Sheet nào để tiện sử dụng ấy ạ.

Còn vấn đề đứng ở các trang khác mà nhấn nút & macro vẫn chạy đúng không phải là không làm được; Nhưng bạn phải cho biết lí do & nó là chính đáng không đã!
Không biết là với lý do trên bác có thể điều chỉnh lại Code giúp em với được không ạ?

Thêm nữa là nếu được thì nhờ bác ghi chú ở các dòng Code là có tác dụng gì? Lý do là em cũng đang tự học VBA và rất muốn học hỏi từ các Code của các tiền bối.
 
Upvote 0
Bạn thao tác trước đi; Chuyện khác tính sau.

(Ở các trang tính khác, nhưng muốn macro cho kết quả thì nhấn tổ hợp phím {CTRL}+{SHIFT}+R)
 

File đính kèm

Upvote 0
em có file thvt nhờ các anh viết cho em 1 đoạn code để tổng hợp vật tư từ sheet ptvt cùng file
nội dung em có comment trong từng cột
 

File đính kèm

Upvote 0
em có file thvt nhờ các anh viết cho em 1 đoạn code để tổng hợp vật tư từ sheet ptvt cùng file
nội dung em có comment trong từng cột
Bạn nên mơ môtj
em có file thvt nhờ các anh viết cho em 1 đoạn code để tổng hợp vật tư từ sheet ptvt cùng file
nội dung em có comment trong từng cột
Bạn nên mở một topic mới để hỏi. Lý do:
- bài toán của bạn ko thể dùng phương thức Find
- đây là một topic để truyển tải những kiến thức về phương thức Find. Những câu hỏi các bạn nên đưa vào topic khác, như thế sẽ có nhiều người hỗ trợ hơn.
Tôi sẽ cơ cấu lại Topic này cho phù hợp.
Xin cảm ơn !!!
 
Upvote 0
Cho em hỏi nếu em muốn tìm một giá trị lớn hơn 1 thì viết như nào. Em thử như này thì không được: Find(">1")
 
Upvote 0
Tìm trong 1 vùng nào đó:

Dùng hàm MAX() & MIN() trong Excel để tìm cực trị của vùng đó;
Sau đó thiết lập vòng lặp từ 2 trở lên (hay từ giá trị Min tìm được) đến trị cực đại

Chúc bạn thành công với số nguyên dương; Số thập phân thì khó à nha!
 
Upvote 0
Các ví dụ minh họa về Find Method


Áp dụng : Để hiểu rõ hơn về phương thức tìm kiếm Find, ta xét một vài ví dụ minh họa sau :

1.> Ta có dữ liệu như sau :

Hinh1.jpg


Ta sẽ dùng phương thức Find để tìm ra trong vùng A1:A10 những ô nào có ký tự 1
Ta dùng chuột bôi đen vùng A1:A10 và cho chạy Macro sau :

PHP:
Sub Test1()
    Dim Rng As Range
    Set Rng = Selection.Find("1*", LookIn:=xlValues, LookAt:=xlWhole)
    If Not Rng Is Nothing Then
        Do
            MsgBox Rng.Address
            Set Rng = Selection.FindNext(Rng)
        Loop
    End If
End Sub

Lần lượt các kết quả của việc chạy Macro như sau :
hinh2.jpg

Diễn giải về trình tự hoạt động của Macro như sau :

- Tìm trong vùng từ ô A2 đến ô A10 xem ô nào có chứa ký tự 1. Kết quả là ô A3 được tìm thấy đầu tiên và sẽ gán biến Rng bằng ô A3. Các bạn sẽ hỏi là tại sao không tìm bắt đầu từ ô A1 và tại sao không trả về ô A1 đầu tiên mà lại là ô A3. Các bạn xem trong cấu trúc của phương thức Find thì sẽ thấy có một đối số tùy chọn, đó là After. Đây là tham số dùng để xác định vị trí bắt đầu của việc tìm kiếm, việc tìm kiếm sẽ bắt đầu tại vị trí sau ô này. Nếu không khai báo thì việc tìm kiếm sẽ bắt đầu ngay sau ô đầu tiên bên trái của vùng tìm kiếm. Như VD trên thì vùng tìm kiếm Selection (A1:A10), do không khai báo nên việc tìm kiếm sẽ bắt đầu từ ô A2 và ô A3 được tìm thấy đầu tiên.


- Tham số What trong phương thức Find ở VD trên sử dụng thêm ký tự “*” để tìm gần đúng (chuỗi bắt đầu bởi số 1). Bỏ dầu “*” ta sẽ tìm chính xác.

- Nếu như không tìm thấy ô nào trong vùng Selection có chứa số 1 (Biến Rng = Nothing) thì sẽ thoát khỏi thủ tục. Ngược lại, sẽ bắt đầu vào vòng lặp tìm kiếm Do Loop.

- Sau khi tìm thấy và gán biến Rng cho ô A3 thì hiện hộp thoại thông báo về địa chỉ của ô vừa được tìm thấy ( biến Rng). Khi đó tham số After sẽ là ô A3 và việc tìm kiếm sẽ bắt đầu sau ô này.

- Tiếp theo ta sẽ dùng phương thức FindNext(Rng) để tìm đến ô chứa số 1 tiếp theo và ô A5 được tìm thấy, biến Rng sẽ là ô A5 . Quá trình cứ tiếp tục như thế cho đến ô A10.

- Khi dò tìm đến ô A10 thì quá trình dò tìm sẽ được lặp lại như lúc ban đầu, tức là sẽ tìm kiếm lặp lại từ ô A1 đến ô A10. Nhưng khi đó tham số After sẽ là ô A10 và lúc này ô A1 sẽ được tìm thấy. Và tiếp đến là ô A3, A5…sẽ tìm kiếm mãi mãi.

- Khi gán biến Rng bằng ô A5 thì đến từ khóa Loop. Khi gặp từ khóa này thì quá trình tiềm kiếm sẽ được tiếp tục, đến từ khóa Do, hiện hộp thông báo và lại gán biến Rng bằng ô A6….



Đoạn Code trên còn 2 vấn đề cần khắc phục, đó là làm sao để tìm thấy được ô A1 đầu tiên và sẽ kết thúc quá trình tìm kiếm khi dò tìm đến ô A10.

Đoạn Code sau sẽ khắc phục điều này :

PHP:
Sub Test2()
    Dim Rng As Range, LastCell As Range, FirstAddress As String
    Set LastCell = Selection.Cells(Selection.Cells.Count)
    Set Rng = Selection.Find("1*", After:=LastCell, LookIn:=xlValues, LookAt:=xlWhole)
    FirstAddress = Rng.Address
    If Not Rng Is Nothing Then
        Do
            MsgBox Rng.Address
            Set Rng = Selection.FindNext(Rng)
        Loop While FirstAddress <> Rng.Address
    End If
End Sub

- Đầu tiên ta sẽ gán biến LastCell ứng với ô cuối cùng của vùng cần tìm kiếm : LastCell = [A10]

- Trong phương thức Find ta sẽ thấy có xuất hiện thêm tham số After có giá trị là LastCell. Điều này có nghĩa là việc tìm kiếm sẽ bắt đầu sau ô [A10]. Và như thế là ô [A1] sẽ được tìm thấy đầu tiên. Sau đó gán biến Rng bằng ô [A1].

- FirstAddress là chuỗi text ứng với địa chỉ của ô được tìm thấy đầu tiên trong vùng, ta gán biến FirstAddress = ”$A$1”.

- Nếu Rng khác Nothing thì đi đến vòng lặp Do Loop, nếu là Nothing thì thoát khỏi thủ tục.

- Vào vòng lặp Do loop thì quá trình tương tự như đoạn Sub Test1. Tuy nhiên trong đoạn Code này còn có thêm một biểu thức điều kiện : FirstAddress <> Rng.Address.

- Loop While FirstAddress <> Rng.Address : đoạn này có ý nghĩa như sau : quá trình tìm kiếm sẽ được lặp lại nếu như địa chỉ của biến Rng hiện tại khác với địa chỉ của ô đầu tiên được tìm thấy trong vùng vùng ( ô A1). Ngược lại, nếu trùng thì có nghĩa rằng ô này đã được tìm thấy trước đó, sẽ hủy bỏ kết quả này và thoát khỏi thủ tục.
Nhờ bài viết của bạn, tôi hiểu rõ hơn về phương thức FIND. Cám ơn!
 
Upvote 0

Bài viết mới nhất

Back
Top Bottom