Dùng code VBA thay hàm countifs (dữ liệu khoảng từ 150.000 ~ 300.000 dòng nên hàm countifs gây treo máy)

Liên hệ QC
Bạn ơi, code của bạn là chuẩn rồi. Tuyệt vời, mình đã thử hơn 280.000 dòng, chạy tốt, file không quá nặng như lúc đầu mình làm.
Giờ phiền bạn giúp 1 lần nữa, vì mình định bổ sung thêm 3 cột mà lúc đầu mình không nghĩ tới, bạn giúp mình lần nữa nhé. chuyển 3 cột này dùng bằng code VBA để tính để nhẹ file.
Lưu ý:
1. Các cột ngày tháng không chứa ngày tháng chuẩn. Hãy đưa về dữ liệu chuẩn.

2. AN5:AS5 chỉ chứa các tên như SBW3, Đoạn "Lớp " do định dạng mà có. Tương tự hãy sửa thành AM5 = SD, muốn có thêm "Lớp " thì dùng định dạng.
Mã:
Sub Copy_remove_duplicate()
 Dim lastRow As Long, r As Long, c As Long, ten_lop As String, ngay, ma, dulieu(), ngay_lop(), tieude(), dic As Object, lop As Object
 Dim sh As Worksheet
    Set sh = ThisWorkbook.Sheets("Attendant")
    Range("AL6:AT10000").ClearContents
    lastRow = sh.Cells(Rows.Count, "L").End(xlUp).Row
    If lastRow < 6 Then Exit Sub
    dulieu = sh.Range("L6:L" & lastRow + 1).Value
    ngay_lop = sh.Range("W6:Y" & lastRow + 1).Value
    tieude = sh.Range("AL5:AS5").Value
    Set dic = CreateObject("Scripting.Dictionary")
    dic.comparemode = vbTextCompare
    For r = 1 To UBound(dulieu) - 1
        ngay_lop(r, 1) = Trim(Replace(ngay_lop(r, 1), "Class", "", , , vbTextCompare)) & ngay_lop(r, 3) ' tao ten lop de nhap vao cot Z
        If Not dic.exists(dulieu(r, 1)) Then
            Set lop = CreateObject("Scripting.Dictionary")
            lop.comparemode = vbTextCompare
            If InStr(1, ngay_lop(r, 1), "SD", vbTextCompare) = 1 Then
                lop.Add "SD", 1
            Else
                lop.Add ngay_lop(r, 1), ngay_lop(r, 2)
            End If
            dic.Add CStr(dulieu(r, 1)), lop
        Else
            Set lop = dic.Item(dulieu(r, 1))
            If InStr(1, ngay_lop(r, 1), "SD", vbTextCompare) = 1 Then
                ten_lop = "SD"
                ngay = 1
            Else
                ten_lop = ngay_lop(r, 1)
                ngay = ngay_lop(r, 2)
            End If
            If Not lop.exists(ten_lop) Then
                lop.Add ten_lop, ngay
            Else
                If ten_lop = "SD" Then
                    lop.Item(ten_lop) = lop.Item(ten_lop) + 1
                Else
                    If ngay_lop(r, 2) < lop.Item(ten_lop) Then lop.Item(ten_lop) = ngay_lop(r, 2)
                End If
            End If
            Set dic.Item(dulieu(r, 1)) = lop
        End If
    Next r
    ReDim dulieu(1 To dic.Count, 1 To 9)
    r = 0
    For Each ma In dic.keys
        r = r + 1
        dulieu(r, 1) = "'" & ma
        Set lop = dic.Item(ma)
        For c = 2 To 8
            ten_lop = tieude(1, c)
            If lop.exists(ten_lop) Then
                dulieu(r, c) = lop.Item(ten_lop)
                If c > 2 Then dulieu(r, 9) = dulieu(r, 9) + 1
            End If
        Next c
    Next ma

    With sh
        .Range("Z6").Resize(UBound(ngay_lop)).Value = ngay_lop  ' cot Z
        .Range("AL6:AT6").Resize(UBound(dulieu, 1)).Value = dulieu
    End With
    
    Set dic = Nothing
    Set lop = Nothing
End Sub
Bài đã được tự động gộp:

Anh mô tả đúng, anh viết code, thì Pivot table của tôi bị ế mất
Anh nghĩ thế là sai. Vài ngày nữa người ta đọc các bài viết về chủ đề PT, các quà tặng của anh, thì kiến thức người ta nâng cao thêm. Lúc đó người ta sẽ mở lại bài của anh trongchủ đề này, vừa đọc vừa gật gù, thỉnh thoảng lại vỗ đùi đánh đét một cái vì khoái chí. :D
 
Lần chỉnh sửa cuối:
Bạn ơi, mình mới phát hiện ra, nếu mình cho dữ liệu dưới 150 dòng thì chạy chính xác, nhưng trên 150 dòng thì nó báo lỗi
Do dữ liệu bạn bị thiếu, Cụ thể Ô X126 bạn để rỗng do vậy máy không hiểu phải lấy ô dữ liệu nào.
Hướng khắc phục nếu không sửa dữ liệu. Thêm Dòng lệnh này (dong tô đậm) vào ví trí xen giưa hai dòng lệnh
Arr = sh.Range("L6:Z" & d).Value
On Error Resume Next
ReDim KQ(1 To d - 6, 1 To 8)

và hãy sửa định dạng cho cột AlL6:AL.......... về dạng "Number",
Khối dữ liệu AN6: AS...... định dạng "dd/mm/yyyy" nhé bằng cách thêm dồng lệnh này vào sau dòng lệnh

sh.[AL6].Resize(t, 7) = KQ

.Range("AN6").Resize(t, 7).NumberFormat = "dd/mm/yyyy"
 
Lần chỉnh sửa cuối:
Lưu ý:
1. Các cột ngày tháng không chứa ngày tháng chuẩn. Hãy đưa về dữ liệu chuẩn.

2. AN5:AS5 chỉ chứa các tên như SBW3, Đoạn "Lớp " do định dạng mà có. Tương tự hãy sửa thành AM5 = SD, muốn có thêm "Lớp " thì dùng định dạng.
Mã:
Sub Copy_remove_duplicate()
 Dim lastRow As Long, r As Long, c As Long, ten_lop As String, ngay, ma, dulieu(), ngay_lop(), tieude(), dic As Object, lop As Object
 Dim sh As Worksheet
    Set sh = ThisWorkbook.Sheets("Attendant")
    Range("AL6:AT10000").ClearContents
    lastRow = sh.Cells(Rows.Count, "L").End(xlUp).Row
    If lastRow < 6 Then Exit Sub
    dulieu = sh.Range("L6:L" & lastRow + 1).Value
    ngay_lop = sh.Range("W6:Y" & lastRow + 1).Value
    tieude = sh.Range("AL5:AS5").Value
    Set dic = CreateObject("Scripting.Dictionary")
    dic.comparemode = vbTextCompare
    For r = 1 To UBound(dulieu) - 1
        ngay_lop(r, 1) = Trim(Replace(ngay_lop(r, 1), "Class", "", , , vbTextCompare)) & ngay_lop(r, 3) ' tao ten lop de nhap vao cot Z
        If Not dic.exists(dulieu(r, 1)) Then
            Set lop = CreateObject("Scripting.Dictionary")
            lop.comparemode = vbTextCompare
            If InStr(1, ngay_lop(r, 1), "SD", vbTextCompare) = 1 Then
                lop.Add "SD", 1
            Else
                lop.Add ngay_lop(r, 1), ngay_lop(r, 2)
            End If
            dic.Add CStr(dulieu(r, 1)), lop
        Else
            Set lop = dic.Item(dulieu(r, 1))
            If InStr(1, ngay_lop(r, 1), "SD", vbTextCompare) = 1 Then
                ten_lop = "SD"
                ngay = 1
            Else
                ten_lop = ngay_lop(r, 1)
                ngay = ngay_lop(r, 2)
            End If
            If Not lop.exists(ten_lop) Then
                lop.Add ten_lop, ngay
            Else
                If ten_lop = "SD" Then
                    lop.Item(ten_lop) = lop.Item(ten_lop) + 1
                Else
                    If ngay_lop(r, 2) < lop.Item(ten_lop) Then lop.Item(ten_lop) = ngay_lop(r, 2)
                End If
            End If
            Set dic.Item(dulieu(r, 1)) = lop
        End If
    Next r
    ReDim dulieu(1 To dic.Count, 1 To 9)
    r = 0
    For Each ma In dic.keys
        r = r + 1
        dulieu(r, 1) = "'" & ma
        Set lop = dic.Item(ma)
        For c = 2 To 8
            ten_lop = tieude(1, c)
            If lop.exists(ten_lop) Then
                dulieu(r, c) = lop.Item(ten_lop)
                If c > 2 Then dulieu(r, 9) = dulieu(r, 9) + 1
            End If
        Next c
    Next ma

    With sh
        .Range("Z6").Resize(UBound(ngay_lop)).Value = ngay_lop  ' cot Z
        .Range("AL6:AT6").Resize(UBound(dulieu, 1)).Value = dulieu
    End With
  
    Set dic = Nothing
    Set lop = Nothing
End Sub
Bài đã được tự động gộp:


Anh nghĩ thế là sai. Vài ngày nữa người ta đọc các bài viết về chủ đề PT, các quà tặng của anh, thì kiến thức người ta nâng cao thêm. Lúc đó người ta sẽ mở lại bài của anh trongchủ đề này, vừa đọc vừa gật gù, thỉnh thoảng lại vỗ đùi đánh đét một cái vì khoái chí. :D
Tuyệt vời. Code chạy nhanh, khoảng 54 giây là xong lượng dữ liệu hơn 200.000 dòng.
Còn 1 điều nhỏ, nếu cột AL có mã số đại lý trống, thì bạn cho kết quả = 0 hết được không ?
Cảm ơn bạn
1622941780343.png
 
code bị báo lỗi bạn ơi.
Bạn xem nhé,

View attachment 260136

Với lại, nếu được, bạn xem giúp mình 3 cột mới phát sinh mà mình chưa nghĩ tới ở bài #1 (cột Z , cột AM và cột AT). Mình muốn tất cả những phần liệt kê bên dưới đều do code thực hiện, chứ làm bằng hàm rồi kéo xuống khoảng 300.000 dòng là file cực kỳ nặng, chạy không nổi luôn.
Nếu làm bằng code thì chỉ cần nhìn cột A xem dòng cuối cùng ở đâu thì thực hiện tới đó thôi, như vậy sẽ nhẹ file hơn rất nhiều

1. Cột Z : Z6=TRIM(SUBSTITUTE($W6," Class",""))&$Y6 : ghép loại lớp (cột W) với ngày chuyên đề (cột Y) lại thành lớp chuyên đề (bỏ phần " class" : có dấu cách phía trước) rồi fill down công thức xuống đến dòng cuối cùng (dựa theo cột A) .

2. Cột AL : copy cột L qua cột AL , lọc và bỏ đi những mã số trùng, fill down công thức xuống đến dòng cuối cùng (dựa theo cột A) .

3. Cột AM : đếm mã số đại lý (ở cột AL) , xem người này đã học tổng cộng bao nhiêu lớp SD (nếu cột AL trống thì khỏi đếm)
AM6=IF(LEN(AL6)<5,0,COUNTIFS($L$6:$L$300000,AL6,$W$6:$W$300000,"SD Class"))

4. Cột AN6:AS : tìm ra ngày học đầu tiên theo tên lớp đã học

Ví dụ học viên có mã số 0077252 đã học lớp SBW2 ,vào ngày 09/09/2016 và ngày 13/01/2017, vậy kết quả trả về ô AO7 phải là 09/09/2016 (ngày học lớp SBW2 ĐẦU TIÊN)

5. Cột AT : AT6=COUNTA(AN6:AS6) : đếm xem mã số đại lý (ở cột AL) đã học bao nhiêu lớp SBW

View attachment 260139
Dữ liệu file mới vi phạm nghiêm trọng chuẩn mực cơ sở dữ liệu là không được để trống dữ liệu quan trọng như thời gian, mã số ...
Chạy code
Mã:
Option Explicit
Sub XYZ()
  Dim aDaiLy(), aLop(), aCDe(), aTieuDe(), Res$(), Res2(), dic As Object
  Dim sRow&, sCol&, i&, r&, iR&, j&, jC&, loai$, ngay, iKey$
 
  Set dic = CreateObject("Scripting.Dictionary")
  dic.CompareMode = vbTextCompare
  With Sheets("Attendant")
    .Range("AL6:AS10000").ClearContents
    i = .Range("L" & Rows.Count).End(xlUp).Row
    If i < 6 Then Exit Sub
    aDaiLy = .Range("L6:L" & i).Value
    aLop = .Range("W6:Y" & i).Value
    aTieuDe = .Range("AM5:AS5").Value
  End With
  sRow = UBound(aLop):  sCol = UBound(aTieuDe, 2)
  ReDim aCDe(1 To sRow, 1 To 1)
  ReDim Res(1 To sRow, 1 To 1)
  ReDim Res2(1 To sRow, 1 To sCol + 1)
  For j = 2 To sCol
    dic.Item(aTieuDe(1, j)) = j
  Next j
  For i = 1 To sRow
    If aLop(i, 1) <> Empty Then
      loai = Split(" " & aLop(i, 1), " ")(1)
      aCDe(i, 1) = loai & aLop(i, 3)
      ngay = aLop(i, 2)
      If ngay <> Empty Then
        ngay = DateValue(Mid(ngay, 7, 4) & Mid(ngay, 3, 4) & Mid(ngay, 1, 2))
        iKey = aDaiLy(i, 1)
        If iKey <> Empty Then
          If Not dic.Exists(iKey) Then
            r = r + 1
            dic.Add iKey, r
            Res(r, 1) = iKey
          End If
          iR = dic.Item(iKey):    jC = dic.Item(aCDe(i, 1))
          If loai = "SD" Then Res2(iR, 1) = Res2(iR, 1) + 1
          If jC > 0 Then
            iKey = aDaiLy(i, 1) & aCDe(i, 1)
            If Not dic.Exists(iKey) Then
              dic.Add iKey, ngay
              Res2(iR, jC) = ngay
              Res2(iR, sCol + 1) = Res2(iR, sCol + 1) + 1
            ElseIf ngay < dic.Item(iKey) Then
              dic.Item(iKey) = ngay
              Res2(iR, jC) = ngay
            End If
          End If
        End If
      End If
    End If
  Next i
  With Sheets("Attendant")
    .Range("Z6").Resize(sRow).Value = aCDe
    .Range("AL6").Resize(r).Value = Res
    .Range("AM6").Resize(r, sCol + 1).Value = Res2
    .Range("AN6").Resize(r, sCol - 1).NumberFormat = "dd/mm/yyyy"
  End With
  Set dic = Nothing
End Sub
 
Dữ liệu file mới vi phạm nghiêm trọng chuẩn mực cơ sở dữ liệu là không được để trống dữ liệu quan trọng như thời gian, mã số ...
Chạy code
Mã:
Option Explicit
Sub XYZ()
  Dim aDaiLy(), aLop(), aCDe(), aTieuDe(), Res$(), Res2(), dic As Object
  Dim sRow&, sCol&, i&, r&, iR&, j&, jC&, loai$, ngay, iKey$
 
  Set dic = CreateObject("Scripting.Dictionary")
  dic.CompareMode = vbTextCompare
  With Sheets("Attendant")
    .Range("AL6:AS10000").ClearContents
    i = .Range("L" & Rows.Count).End(xlUp).Row
    If i < 6 Then Exit Sub
    aDaiLy = .Range("L6:L" & i).Value
    aLop = .Range("W6:Y" & i).Value
    aTieuDe = .Range("AM5:AS5").Value
  End With
  sRow = UBound(aLop):  sCol = UBound(aTieuDe, 2)
  ReDim aCDe(1 To sRow, 1 To 1)
  ReDim Res(1 To sRow, 1 To 1)
  ReDim Res2(1 To sRow, 1 To sCol + 1)
  For j = 2 To sCol
    dic.Item(aTieuDe(1, j)) = j
  Next j
  For i = 1 To sRow
    If aLop(i, 1) <> Empty Then
      loai = Split(" " & aLop(i, 1), " ")(1)
      aCDe(i, 1) = loai & aLop(i, 3)
      ngay = aLop(i, 2)
      If ngay <> Empty Then
        ngay = DateValue(Mid(ngay, 7, 4) & Mid(ngay, 3, 4) & Mid(ngay, 1, 2))
        iKey = aDaiLy(i, 1)
        If iKey <> Empty Then
          If Not dic.Exists(iKey) Then
            r = r + 1
            dic.Add iKey, r
            Res(r, 1) = iKey
          End If
          iR = dic.Item(iKey):    jC = dic.Item(aCDe(i, 1))
          If loai = "SD" Then Res2(iR, 1) = Res2(iR, 1) + 1
          If jC > 0 Then
            iKey = aDaiLy(i, 1) & aCDe(i, 1)
            If Not dic.Exists(iKey) Then
              dic.Add iKey, ngay
              Res2(iR, jC) = ngay
              Res2(iR, sCol + 1) = Res2(iR, sCol + 1) + 1
            ElseIf ngay < dic.Item(iKey) Then
              dic.Item(iKey) = ngay
              Res2(iR, jC) = ngay
            End If
          End If
        End If
      End If
    End If
  Next i
  With Sheets("Attendant")
    .Range("Z6").Resize(sRow).Value = aCDe
    .Range("AL6").Resize(r).Value = Res
    .Range("AM6").Resize(r, sCol + 1).Value = Res2
    .Range("AN6").Resize(r, sCol - 1).NumberFormat = "dd/mm/yyyy"
  End With
  Set dic = Nothing
End Sub
Do mấy cái file báo cáo có khi nó trống ở ô mã số đại lý nên khi mình import dữ liệu vào nó cũng trống theo. Với cột ngày tháng từ file báo cáo (xuất từ phần mềm trên web xuống), mình cũng nhận ra nó định dạng tùm lum hết á, nên khó để dùng tính toán. Hay là khi chạy code, cột AL, mình cho xóa bỏ luôn những code trống, được không
 
Do mấy cái file báo cáo có khi nó trống ở ô mã số đại lý nên khi mình import dữ liệu vào nó cũng trống theo. Với cột ngày tháng từ file báo cáo (xuất từ phần mềm trên web xuống), mình cũng nhận ra nó định dạng tùm lum hết á, nên khó để dùng tính toán. Hay là khi chạy code, cột AL, mình cho xóa bỏ luôn những code trống, được không
Code của mình cột AL không có dòng trống
Bài đã được tự động gộp:

Dữ liệu ngày tháng xuất từ phần mềm ra dạng Text như trong file khá tốt dể xử lý
 
Bạn ơi, code của bạn là chuẩn rồi. Tuyệt vời, mình đã thử hơn 280.000 dòng, chạy tốt, file không quá nặng như lúc đầu mình làm.
Giờ phiền bạn giúp 1 lần nữa, vì mình định bổ sung thêm 3 cột mà lúc đầu mình không nghĩ tới, bạn giúp mình lần nữa nhé. chuyển 3 cột này dùng bằng code VBA để tính để nhẹ file.
1. Cột AM : Lớp SD Class : đếm tổng số lớp SD , SD Class (ở cột W) mà mã số đại lý đã theo học bằng cách dò mã số đại lý (cột AL), dò trong vùng L:W. (hiện mình đang dùng công thức =IF(LEN(AL6)<5,0,COUNTIFS($L$6:$L$300000,AL6,$W$6:$W$300000,"SD Class"))
2. Cột AT : tính tổng số lớp SBW mà mã số đại lý đã theo học AT6=COUNTA(AN6:AS6)
3. Cột Z6=TRIM(SUBSTITUTE($W6," Class",""))&$Y6
Góp thêm một cách, hy vọng đúng ý
chúc vui và thành công.
 

File đính kèm

  • MSDL-LOP.xlsm
    5.2 MB · Đọc: 11
Dạ, nếu em để dữ liệu thực tế vào, dung lượng file lên đến 65MB, không cách gì gởi lên diễn đàn nổi ạ. Do em dùng hàm countifs như bài #1, em kéo fill xuống cái treo máy luôn, nhưng bạn ptm0412 nói ở trên, thì cái công thức của em cũng không đúng, nên em xin trình bày lại như sau :
Cột AL là lọc và bỏ mã số đại lý bị trùng.
Rồi sau đó từ cột AN6:AS... là tìm ra ngày học đầu tiên theo tên lớp đã học

Ví dụ học viên có mã số 0098218 đã học lớp SBW1 ,vào ngày 25/08/2018 và ngày 06/08/2018, vậy kết quả trả về ô AN9 phải là 28/05/2018 (ngày học lớp SBW1 ĐẦU TIÊN)

Tương tự :
học viên có mã số 0098218 đã học lớp SBW4 ,vào ngày 16/07/2018, vậy kết quả trả về ô AQ9 là

Nếu làm với dữ liệu lớn thì data không nên định dạng gì. tốt nhất là để định dạng mặc định, 300 nghìn dòng mà 65M chắc là bạn có nhiều định dạng, tôi thường xử lý dữ liệu trên 500 nghìn dòng mà cũng chưa tới 10M. bạn có thể xóa bớt đi định dạng để file nhỏ lại xem như thế nào?
 
Nếu làm với dữ liệu lớn thì data không nên định dạng gì. tốt nhất là để định dạng mặc định, 300 nghìn dòng mà 65M chắc là bạn có nhiều định dạng, tôi thường xử lý dữ liệu trên 500 nghìn dòng mà cũng chưa tới 10M. bạn có thể xóa bớt đi định dạng để file nhỏ lại xem như thế nào?
Cỡ trình độ bạn, 500 nghìn dòng mà phải dùng VBA là triệu chứng thoái hoá, tụt hậu.
Thời buổi bi giờ, làm viêc "thường xuyên" trên chục nghìn dòng là phải tu bổ kiến thức với Data Model, và những thứ tương tự.
 
Còn 1 điều nhỏ, nếu cột AL có mã số đại lý trống, thì bạn cho kết quả = 0 hết được không ?
Sau dòng
Mã:
For r = 1 To UBound(dulieu) - 1

thì thêm 1 dòng

Mã:
If Len(dulieu(r, 1)) Then

Trước dòng
Mã:
Next r

thì thêm 1 dòng

Mã:
End If
---------
Quan điểm của tôi là dữ liệu LUÔN LUÔN phải chuẩn. Không có chuyện cứ mỗi lần dùng dữ liệu là lại phải xoay xở. Chỉ trừ trường hợp việc sửa dữ liệu mất rất nhiều thơi gian.

Với kiểu ngày tháng như cột T và X thì bạn sửa như sau: chọn cả cột T -> thẻ Data -> Text to Columns -> Next -> Next -> chọn Date và chọn DMY -> nhấn Finish -> làm tương tự với cột X.
 
Sau dòng
Mã:
For r = 1 To UBound(dulieu) - 1

thì thêm 1 dòng

Mã:
If Len(dulieu(r, 1)) Then

Trước dòng
Mã:
Next r

thì thêm 1 dòng

Mã:
End If
---------
Quan điểm của tôi là dữ liệu LUÔN LUÔN phải chuẩn. Không có chuyện cứ mỗi lần dùng dữ liệu là lại phải xoay xở. Chỉ trừ trường hợp việc sửa dữ liệu mất rất nhiều thơi gian.

Với kiểu ngày tháng như cột T và X thì bạn sửa như sau: chọn cả cột T -> thẻ Data -> Text to Columns -> Next -> Next -> chọn Date và chọn DMY -> nhấn Finish -> làm tương tự với cột X.
hôm nay mình chạy báo cáo ra thì phát hiện 1 vấn đề nhỏ :

Cột AM : đếm số lượng lớp SD mà học viên đã tham gia, nhờ các bạn chỉnh code lại 1 chút :

Nhìn cột AL, lấy mã số đại lý, đối chiếu với cột L:Z , đếm số lớp SD mà học viên đã học.

Quy tắc đếm : Nếu bên cột X mà không có ngày học thì lớp đó bỏ, không tính

1 lớp mà học nhiều lần cũng chỉ tính 1 (như hình thì lớp SD1, học viên này học 3 lần nhưng chỉ tính 1)

Chỉ đếm những lớp có 2 chữ cái bắt đầu là SD (những lớp SBW thì không đếm, không cộng vào)

1622994645556.png

Hiện tại, code cũ của mình đang cho ra kết quả đếm là 11

1622996790822.png

Sub Attendant_2_value()
Dim lastRow As Long, r As Long, c As Long, ten_lop As String, ngay, ma, dulieu(), ngay_lop(), tieude(), dic As Object, lop As Object
Dim sh As Worksheet
Set sh = ThisWorkbook.Sheets("Attendant")
Range("AL6:AT300000").ClearContents

lastRow = sh.Cells(Rows.Count, "L").End(xlUp).Row
If lastRow < 6 Then Exit Sub
dulieu = sh.Range("L6:L" & lastRow + 1).Value
ngay_lop = sh.Range("W6:Y" & lastRow + 1).Value
tieude = sh.Range("AL5:AS5").Value
Set dic = CreateObject("Scripting.Dictionary")
dic.CompareMode = vbTextCompare
For r = 1 To UBound(dulieu) - 1
If Len(dulieu(r, 1)) Then
ngay_lop(r, 1) = Trim(Replace(ngay_lop(r, 1), "Class", "", , , vbTextCompare)) & ngay_lop(r, 3) ' tao ten lop de nhap vao cot Z
If Not dic.Exists(dulieu(r, 1)) Then
Set lop = CreateObject("Scripting.Dictionary")
lop.CompareMode = vbTextCompare
If InStr(1, ngay_lop(r, 1), "SD", vbTextCompare) = 1 Then
lop.Add "SD", 1
Else
lop.Add ngay_lop(r, 1), ngay_lop(r, 2)
End If
dic.Add CStr(dulieu(r, 1)), lop
Else
Set lop = dic.Item(dulieu(r, 1))
If InStr(1, ngay_lop(r, 1), "SD", vbTextCompare) = 1 Then
ten_lop = "SD"
ngay = 1
Else
ten_lop = ngay_lop(r, 1)
ngay = ngay_lop(r, 2)
End If
If Not lop.Exists(ten_lop) Then
lop.Add ten_lop, ngay
Else
If ten_lop = "SD" Then
lop.Item(ten_lop) = lop.Item(ten_lop) + 1
Else
If ngay_lop(r, 2) < lop.Item(ten_lop) Then lop.Item(ten_lop) = ngay_lop(r, 2)
End If
End If
Set dic.Item(dulieu(r, 1)) = lop
End If
End If
Next r
ReDim dulieu(1 To dic.Count, 1 To 9)
r = 0
For Each ma In dic.Keys
r = r + 1
dulieu(r, 1) = "'" & ma
Set lop = dic.Item(ma)
For c = 2 To 8
ten_lop = tieude(1, c)
If lop.Exists(ten_lop) Then
dulieu(r, c) = lop.Item(ten_lop)
If c > 2 Then dulieu(r, 9) = dulieu(r, 9) + 1
End If
Next c
Next ma

With sh
.Range("Z6").Resize(UBound(ngay_lop)).Value = ngay_lop ' cot Z
.Range("AL6:AT6").Resize(UBound(dulieu, 1)).Value = dulieu
End With

Set dic = Nothing
Set lop = Nothing
End Sub

Bạn giúp mình làm nhé.
Cảm ơn
 

File đính kèm

  • book1.xlsm
    5.3 MB · Đọc: 15
Lần chỉnh sửa cuối:
Do mấy cái file báo cáo có khi nó trống ở ô mã số đại lý nên khi mình import dữ liệu vào nó cũng trống theo. Với cột ngày tháng từ file báo cáo (xuất từ phần mềm trên web xuống), mình cũng nhận ra nó định dạng tùm lum hết á, nên khó để dùng tính toán. Hay là khi chạy code, cột AL, mình cho xóa bỏ luôn những code trống, được không
Có nguyên tắc vàng trong ứng dụng tin học trong quản lý: Làm ít sai ít làm nhiều sai nhiều, những gì phần mềm xử lý được hãy giao cho phần mềm, hạn chế tối thiểu thao tác thủ công trực tiếp trên dữ liệu, chỉ cần 1 thao tác sai có khả năng dẫn đến sai hàng loạt kết quả
 
hôm nay mình chạy báo cáo ra thì phát hiện 1 vấn đề nhỏ :
Code tôi viết thay cho công thức của bạn ở bài #19. Bây giờ bạn mới thay đổi cách tính, mới trình bầy chi tiết. Đó là lỗi của bạn chứ có phải của những người giúp đâu.
 
Code tôi viết thay cho công thức của bạn ở bài #19. Bây giờ bạn mới thay đổi cách tính, mới trình bầy chi tiết. Đó là lỗi của bạn chứ có phải của những người giúp đâu.
Dạ, anh nói đúng, chắc em nói chưa tốt nên khiến anh hiểu nhầm, em không có nói là code của anh viết bị sai , hihihihi. Có thể do bài #19 em trình bày thiếu ý, nên anh viết code theo những gì em nói. Giờ xuất báo cáo ra, thấy sai vài người nên em kiểm tra lại mới phát hiện ra sai sót này. nhờ anh giúp điều chỉnh bổ sung thêm dùm em nhé.
Cảm ơn anh
 
Dạ, anh nói đúng, chắc em nói chưa tốt nên khiến anh hiểu nhầm, em không có nói là code của anh viết bị sai , hihihihi. Có thể do bài #19 em trình bày thiếu ý, nên anh viết code theo những gì em nói. Giờ xuất báo cáo ra, thấy sai vài người nên em kiểm tra lại mới phát hiện ra sai sót này. nhờ anh giúp điều chỉnh bổ sung thêm dùm em nhé.
Cảm ơn anh
1. Tôi làm cho bạn lần cuối. Nếu bạn lại thay đổi yêu cầu thì bạn tự làm.

2. Tôi đã nhắc rất nhiều lần là dữ liệu phải chuẩn. Chuyện ngày tháng thì tôi đã chỉ cho bạn cách đưa về chuẩn, cùng lắm mất mười mấy giây. Còn chuyện mã cũng không thể có như hình ở dưới. Nếu X26 có ngày tháng thì sẽ có 2 dòng kết quả ứng với 2 mã là 155592 và 0155592, thay cho 1 kết quả 0155592. Bạn không quan tâm tới dữ liệu thì tự bạn lo thôi.

ma so.jpg

Mã:
Sub Copy_remove_duplicate()
 Dim lastRow As Long, r As Long, c As Long, ten_lop As String, ngay, ma, SDx, dulieu(), ngay_lop(), tieude(), dic As Object, lop As Object
 Dim sh As Worksheet
    Set sh = ThisWorkbook.Sheets("Attendant")
    Range("AL6:AT10000").ClearContents
    lastRow = sh.Cells(Rows.Count, "L").End(xlUp).Row
    If lastRow < 6 Then Exit Sub
    dulieu = sh.Range("L6:L" & lastRow + 1).Value
    ngay_lop = sh.Range("W6:Y" & lastRow + 1).Value
    tieude = sh.Range("AL5:AS5").Value
    Set dic = CreateObject("Scripting.Dictionary")
    dic.comparemode = vbTextCompare
    For r = 1 To UBound(dulieu) - 1
        If Len(dulieu(r, 1)) Then   '   nếu mã đang xét khác Rỗng
            ngay_lop(r, 1) = Trim(Replace(ngay_lop(r, 1), "Class", "", , , vbTextCompare)) & ngay_lop(r, 3) ' tao ten lop de nhap vao cot Z
            If Len(ngay_lop(r, 2)) Then '   nếu có ngày học mới xét
                If Not dic.exists(dulieu(r, 1)) Then    '   chưa có mã trong từ điển
                        Set lop = CreateObject("Scripting.Dictionary")
                        lop.comparemode = vbTextCompare
                        If InStr(1, ngay_lop(r, 1), "SD", vbTextCompare) = 1 Then
                            lop.Add ngay_lop(r, 1), 1
                        Else
                            lop.Add ngay_lop(r, 1), ngay_lop(r, 2)
                        End If
                        dic.Add CStr(dulieu(r, 1)), lop
                Else    '   đã có mã trong từ điển
                    Set lop = dic.item(dulieu(r, 1))
                    If InStr(1, ngay_lop(r, 1), "SD", vbTextCompare) = 1 Then   '   lớp là SD...
                        If Not lop.exists(ngay_lop(r, 1)) Then lop.Add ngay_lop(r, 1), 1
                    ElseIf Not lop.exists(ngay_lop(r, 1)) Then  '   lớp SBW... nhưng chưa có trong từ điển lop
                        lop.Add ngay_lop(r, 1), ngay_lop(r, 2)
                    ElseIf ngay_lop(r, 2) < lop.item(ngay_lop(r, 1)) Then  ' lớp SBW... đã có trong từ điển lop nhưng có ngày < ngày hiện có trong từ điển
                        lop.item(ngay_lop(r, 1)) = ngay_lop(r, 2)
                    End If
                    Set dic.item(dulieu(r, 1)) = lop
                End If
            End If
        End If
    Next r
    ReDim dulieu(1 To dic.Count, 1 To 9)
    r = 0
    For Each ma In dic.keys
        r = r + 1
        dulieu(r, 1) = "'" & ma
        Set lop = dic.item(ma)
        For c = 3 To 8
            ten_lop = tieude(1, c)
            If lop.exists(ten_lop) Then
                dulieu(r, c) = lop.item(ten_lop)
                dulieu(r, 9) = dulieu(r, 9) + 1
            End If
        Next c
        ten_lop = tieude(1, 2)
        For Each SDx In lop.keys
            If InStr(1, SDx, ten_lop, vbTextCompare) Then dulieu(r, 2) = dulieu(r, 2) + 1
        Next SDx
    Next ma

    With sh
        .Range("Z6").Resize(UBound(ngay_lop)).Value = ngay_lop  ' cot Z
        .Range("AL6:AT6").Resize(UBound(dulieu, 1)).Value = dulieu
    End With
    
    Set dic = Nothing
    Set lop = Nothing
End Sub
 
Web KT
Back
Top Bottom