Tối ưu code lọc và xóa dữ liệu trùng lặp hàng loạt

Liên hệ QC

letienmai

Thành viên hoạt động
Tham gia
16/7/14
Bài viết
146
Được thích
45
Xin chào mọi người
Do kiến thức lập trình VBA còn hạn chế cụ thể về Array hoặc Dictionary nên xin nhờ anh/chị đoạn code để tăng tốc độ xử lý với dữ liệu từ 20.000 dòng trở lên, hiện tại code mình đang viết mất tầm 10s để xử lý ở cấu hình máy vừa phải, tuy nhiên với file mình gửi lên thì đã được lượt bỏ tất cả các sheet không liên quan. các bước thủ công mình đang làm là:
- Đầu bài: lọc các lệnh sản xuất [Cột H-Sheets("BB_SANXUAT")] thực tế sản xuất trong mỗi ngày của tất cả công đoạn trong dây chuyền sản xuất (có gần 10 công đoạn)
+ B1: Lọc theo ngày báo cáo Cột C-Sheet2
+ B2: Tạo dữ liệu phụ các công đoạn ở Sheet10.Range("V10:AF10")
+ B3: Sử dụng vòng lặp For Next để lọc theo tên công đoạn
+ B4: Sau khi lọc copy các giá trị hiển thị
+ B5: Dán vào Sheets("BC_NGAY")
+ B6: Xóa các giá trị trùng lặp
+ B7: Sắp xếp theo thứ tự nhỏ đến lớn
+ B8: Copy dữ liệu phụ, dán vào vùng cần thể hiện báo cáo
+ B9: ClearContents giá trị ở dữ liệu phụ.
+B10: Kết thúc
Rất mong nhận được sự trợ giúp từ tất cả anh/ chị để code xử lý được nhanh và không bị treo máy.
Xin chân thành cám ơn.
 

File đính kèm

  • Tro giup.xlsb
    289.7 KB · Đọc: 36
Xin chào mọi người
Do kiến thức lập trình VBA còn hạn chế cụ thể về Array hoặc Dictionary nên xin nhờ anh/chị đoạn code để tăng tốc độ xử lý với dữ liệu từ 20.000 dòng trở lên, hiện tại code mình đang viết mất tầm 10s để xử lý ở cấu hình máy vừa phải, tuy nhiên với file mình gửi lên thì đã được lượt bỏ tất cả các sheet không liên quan. các bước thủ công mình đang làm là:
- Đầu bài: lọc các lệnh sản xuất [Cột H-Sheets("BB_SANXUAT")] thực tế sản xuất trong mỗi ngày của tất cả công đoạn trong dây chuyền sản xuất (có gần 10 công đoạn)
+ B1: Lọc theo ngày báo cáo Cột C-Sheet2
+ B2: Tạo dữ liệu phụ các công đoạn ở Sheet10.Range("V10:AF10")
+ B3: Sử dụng vòng lặp For Next để lọc theo tên công đoạn
+ B4: Sau khi lọc copy các giá trị hiển thị
+ B5: Dán vào Sheets("BC_NGAY")
+ B6: Xóa các giá trị trùng lặp
+ B7: Sắp xếp theo thứ tự nhỏ đến lớn
+ B8: Copy dữ liệu phụ, dán vào vùng cần thể hiện báo cáo
+ B9: ClearContents giá trị ở dữ liệu phụ.
+B10: Kết thúc
Rất mong nhận được sự trợ giúp từ tất cả anh/ chị để code xử lý được nhanh và không bị treo máy.
Xin chân thành cám ơn.
Chạy sub
Mã:
Option Explicit
Sub XYZ()
  Dim sArr(), aCDoan(), Res(), dic As Object
  Dim sRow&, sCol&, i&, r&, iR&, j&, jC&, ngay, iKey$, tmp

  With Sheets("BB_SANXUAT")
    If .AutoFilterMode = True Then .AutoFilterMode = False
    i = .Range("C" & Rows.Count).End(xlUp).Row
    If i < 7 Then MsgBox "Khong co du lieu!": Exit Sub
    sArr = .Range("C7:J" & i).Value
  End With
 
  Application.ScreenUpdating = False
  Application.EnableEvents = False
  Set dic = CreateObject("Scripting.Dictionary")
  dic.comparemode = vbTextCompare
  With Sheets("BC_NGAY")
    aCDoan = .Range("F10:P10").Value
    ngay = .Range("N7").Value
  End With
  sCol = UBound(aCDoan, 2)
  ReDim Res(1 To 10, 1 To sCol)
  For j = 1 To sCol
    dic.Item(Application.Trim(Replace(aCDoan(1, j), Chr(10), " "))) = j
  Next j
 
  sRow = UBound(sArr)
  For i = 1 To sRow
    If sArr(i, 1) = ngay Then
      jC = dic.Item(sArr(i, 4))
      If jC > 0 Then
        iKey = sArr(i, 4) & sArr(i, 6)
        If Not dic.exists(iKey) Then
          Res(10, jC) = Res(10, jC) + 1
          Res(Res(10, jC), jC) = sArr(i, 6)
          dic.Add iKey, ""
        End If
      End If
    End If
  Next i

  For j = 1 To sCol 'Xep thu tu
    sRow = Res(10, j)
    For i = 1 To sRow - 1
      tmp = Res(i, j)
      iR = i
      For r = i + 1 To sRow
        If tmp > Res(r, j) Then
          tmp = Res(r, j)
          iR = r
        End If
      Next r
      If iR > i Then
        Res(iR, j) = Res(i, j)
        Res(i, j) = tmp
      End If
    Next i
  Next j
  With Sheets("BC_NGAY")
    .Range("C11:C19").EntireRow.Hidden = False
    .Range("F11:P19") = Res
  End With
  Set dic = Nothing
  Application.EnableEvents = True
  Application.ScreenUpdating = True
End Sub
 
Upvote 0
Chạy sub
Mã:
Option Explicit
Sub XYZ()
  Dim sArr(), aCDoan(), Res(), dic As Object
  Dim sRow&, sCol&, i&, r&, iR&, j&, jC&, ngay, iKey$, tmp

  With Sheets("BB_SANXUAT")
    If .AutoFilterMode = True Then .AutoFilterMode = False
    i = .Range("C" & Rows.Count).End(xlUp).Row
    If i < 7 Then MsgBox "Khong co du lieu!": Exit Sub
    sArr = .Range("C7:J" & i).Value
  End With
 
  Application.ScreenUpdating = False
  Application.EnableEvents = False
  Set dic = CreateObject("Scripting.Dictionary")
  dic.comparemode = vbTextCompare
  With Sheets("BC_NGAY")
    aCDoan = .Range("F10:P10").Value
    ngay = .Range("N7").Value
  End With
  sCol = UBound(aCDoan, 2)
  ReDim Res(1 To 10, 1 To sCol)
  For j = 1 To sCol
    dic.Item(Application.Trim(Replace(aCDoan(1, j), Chr(10), " "))) = j
  Next j
 
  sRow = UBound(sArr)
  For i = 1 To sRow
    If sArr(i, 1) = ngay Then
      jC = dic.Item(sArr(i, 4))
      If jC > 0 Then
        iKey = sArr(i, 4) & sArr(i, 6)
        If Not dic.exists(iKey) Then
          Res(10, jC) = Res(10, jC) + 1
          Res(Res(10, jC), jC) = sArr(i, 6)
          dic.Add iKey, ""
        End If
      End If
    End If
  Next i

  For j = 1 To sCol 'Xep thu tu
    sRow = Res(10, j)
    For i = 1 To sRow - 1
      tmp = Res(i, j)
      iR = i
      For r = i + 1 To sRow
        If tmp > Res(r, j) Then
          tmp = Res(r, j)
          iR = r
        End If
      Next r
      If iR > i Then
        Res(iR, j) = Res(i, j)
        Res(i, j) = tmp
      End If
    Next i
  Next j
  With Sheets("BC_NGAY")
    .Range("C11:C19").EntireRow.Hidden = False
    .Range("F11:P19") = Res
  End With
  Set dic = Nothing
  Application.EnableEvents = True
  Application.ScreenUpdating = True
End Sub
1. Em xin thành thật cám ơn anh @HieuCD đã nhiệt tình và giúp đỡ em rất nhiều trong các bài đăng trợ giúp. Chúc anh có thật nhiều sức khỏe.
2. Code của anh sau khi tối ưu chỉ còn 0.33s so với ban đầu của em là 6.67s siêu tiết giảm 95% thật ngưỡng mộ anh.
3. Đồng thời, Em xin phép hỏi anh thêm về ý nghĩa của các số 4,6,10 trong code anh giúp em, theo em đang hiểu là:
- 4 là cột Cong doan của Sheets("BB_SANXUAT")
- 6 là cột LSX của Sheets("BB_SANXUAT")
- 10 là vị trí dòng chứa tiêu đề của Sheets("BC_NGAY")
- Mong anh giải đáp giúp em để em hiểu được thêm ạ.
-------------------------------------------------------------------------
ReDim Res(1 To
10, 1 To sCol) 'Chỗ 1 to 10 này em chưa hiểu mong anh giải đáp giúp
For j = 1 To sCol
dic.Item(Application.Trim(Replace(aCDoan(1, j), Chr(10), " "))) = j
Next j


sRow = UBound(sArr)
For i = 1 To sRow
If sArr(i, 1) = ngay Then
jC = dic.Item(sArr(i,
4))
If jC > 0 Then
iKey = sArr(i,
4) & sArr(i, 6)
If Not dic.exists(iKey) Then
Res(
10, jC) = Res(10, jC) + 1
Res(Res(
10, jC), jC) = sArr(i, 6)
dic.Add iKey, ""
End If
End If
End If
Next i

For j = 1 To sCol 'Xep thu tu
sRow = Res(
10, j)
 
Lần chỉnh sửa cuối:
Upvote 0
1. Em xin thành thật cám ơn anh @HieuCD đã nhiệt tình và giúp đỡ em rất nhiều trong các bài đăng trợ giúp. Chúc anh có thật nhiều sức khỏe.
2. Code của anh sau khi tối ưu chỉ còn 0.33s so với ban đầu của em là 6.67s siêu tiết giảm 95% thật ngưỡng mộ anh.
3. Đồng thời, Em xin phép hỏi anh thêm về ý nghĩa của các số 4,6,10 trong code anh giúp em, theo em đang hiểu là:
- 4 là cột Cong doan của Sheets("BB_SANXUAT")
- 6 là cột LSX của Sheets("BB_SANXUAT")
- 10 là vị trí dòng chứa tiêu đề của Sheets("BC_NGAY")
- Mong anh giải đáp giúp em để em hiểu được thêm ạ.
-------------------------------------------------------------------------
ReDim Res(1 To
10, 1 To sCol) 'Chỗ 1 to 10 này em chưa hiểu mong anh giải đáp giúp
For j = 1 To sCol
dic.Item(Application.Trim(Replace(aCDoan(1, j), Chr(10), " "))) = j
Next j


sRow = UBound(sArr)
For i = 1 To sRow
If sArr(i, 1) = ngay Then
jC = dic.Item(sArr(i,
4))
If jC > 0 Then
iKey = sArr(i,
4) & sArr(i, 6)
If Not dic.exists(iKey) Then
Res(
10, jC) = Res(10, jC) + 1
Res(Res(
10, jC), jC) = sArr(i, 6)
dic.Add iKey, ""
End If
End If
End If
Next i

For j = 1 To sCol 'Xep thu tu
sRow = Res(
10, j)
Bảng kết quả của bạn từ dòng 11 -> 19 là 9 dòng, thêm dòng 10 để ghi số lệnh sản xuất từng công đoạn. Nếu 1 ngày hơn 9 lệnh sản xuất cho 1 công đoạn thì code bị lỗi hoặc không chính xác
sArr = .Range("C7:J" & i).Value
Dữ liệu lấy từ cột C, như vậy tính từ cột C là cột 1, cột thứ 4 của sArr là cột F, cột thứ 6 là cột H
 
Upvote 0
Bảng kết quả của bạn từ dòng 11 -> 19 là 9 dòng, thêm dòng 10 để ghi số lệnh sản xuất từng công đoạn. Nếu 1 ngày hơn 9 lệnh sản xuất cho 1 công đoạn thì code bị lỗi hoặc không chính xác
sArr = .Range("C7:J" & i).Value
Dữ liệu lấy từ cột C, như vậy tính từ cột C là cột 1, cột thứ 4 của sArr là cột F, cột thứ 6 là cột H
Dạ em hiểu rồi ạ. Cám ơn anh đã giúp đỡ và giải thích ạ. Chúc anh thật nhiều sức khỏe.
 
Upvote 0

Tối ưu code​

Không biết tiêu chí thế nào là tối ưu nhỉ.
Mình cũng định thử, nhưng khả năng tiêu chí "Tối ưu" sẽ không đạt nên hóng vậy.
Chỉ tiêu "tối ưu" của tôi là code như thế nào để khi điều kiện thay đổi thì người sử dụng code có thể tự chỉnh sửa theo, không phải lên đây "chạy mượt rồi, nhưng nhờ thêm chút xíu..."
 
Upvote 0
Kính gửi anh @HieuCD cùng các anh/chị
Sau khi anh trợ giúp code em có mài mò viết lại theo chiều dọc của cột, hiện tại em đang vướng làm sao gom các kết quả lại vào một ô, em có sử dụng hàm join nhưng do trình độ còn non kém nên chạy cứ bị lỗi.
Rất mong anh cùng các a/c quan tâm giúp đỡ.
Xin cám ơn.
1629187400934.png
 

File đính kèm

  • Tro giup.xlsb
    295 KB · Đọc: 15
Upvote 0
Kính gửi anh @HieuCD cùng các anh/chị
Sau khi anh trợ giúp code em có mài mò viết lại theo chiều dọc của cột, hiện tại em đang vướng làm sao gom các kết quả lại vào một ô, em có sử dụng hàm join nhưng do trình độ còn non kém nên chạy cứ bị lỗi.
Rất mong anh cùng các a/c quan tâm giúp đỡ.
Xin cám ơn.
View attachment 264241
Trong Sub lsx_doc,sau dòng:
Mã:
Next i
Bạn thêm đoạn sau (vì code cũ của Bác @HieuCD mình chưa đủ khả năng để sửa lên giữ nguyên, mà lâu rồi cũng không thấy Bác ấy viết bài :():
Mã:
            '// thêm vào
            Dim Noi(): ReDim Noi(1 To UBound(kq), 1 To 1)
            For i = LBound(kq, 1) To UBound(kq, 2)
                For j = LBound(kq, 2) To UBound(kq, 2)
                    If Not IsEmpty(kq(i, j)) Then
                        If Not IsEmpty(Noi(i, 1)) Then
                            Noi(i, 1) = Noi(i, 1) & ";" & kq(i, j)
                        Else
                            Noi(i, 1) = kq(i, j)
                        End If
                    End If
                Next j
            Next i
            .Range("E24:N34").ClearContents
            '.Range("E24:N34").Value = kq
            .Range("E24:E34").Value = Noi
 
Upvote 0
Kính gửi anh @HieuCD cùng các anh/chị
Sau khi anh trợ giúp code em có mài mò viết lại theo chiều dọc của cột, hiện tại em đang vướng làm sao gom các kết quả lại vào một ô, em có sử dụng hàm join nhưng do trình độ còn non kém nên chạy cứ bị lỗi.
Rất mong anh cùng các a/c quan tâm giúp đỡ.
Xin cám ơn.
View attachment 264241
Rất đơn giản... bạn chỉ việc copy kết quả ngang thành dọc là xong
 
Upvote 0
Kính gửi anh @HieuCD cùng các anh/chị
Sau khi anh trợ giúp code em có mài mò viết lại theo chiều dọc của cột, hiện tại em đang vướng làm sao gom các kết quả lại vào một ô, em có sử dụng hàm join nhưng do trình độ còn non kém nên chạy cứ bị lỗi.
Rất mong anh cùng các a/c quan tâm giúp đỡ.
Xin cám ơn.
View attachment 264241
Nếu rách việc thì có thể tham khảo cách dùng dic trong code dưới đây.
code dưới được viết cho nhật ký đã sort cột C
Mã:
Option Explicit

Sub lsx_doc_()
Dim Nhatky
Dim Congdoan
Dim Kq
Dim Ngay
Dim tenDong
Dim rws
Dim i, j, k

Dim dic As New Scripting.Dictionary
dic.CompareMode = TextCompare

With Sheet2
    .AutoFilterMode = False
    i = .Range("C" & Rows.Count).End(xlUp).Row
    Nhatky = .Range("C7:H" & i).Value
    rws = UBound(Nhatky)
End With
Congdoan = Sheet10.Range("D24:D34").Value
Ngay = Sheet10.Range("N7").Value
ReDim Kq(1 To UBound(Congdoan), 1 To 3)

k = 0
For i = 1 To rws
    If Nhatky(i, 1) = Ngay Then
        dic(i) = i
        k = 1
    Else
        If k = 1 Then Exit For
    End If
Next i
tenDong = dic.Keys
   
For i = 1 To UBound(Kq)
    Kq(i, 1) = i
    Kq(i, 2) = Congdoan(i, 1)
    dic.RemoveAll
    k = 0
    For j = 0 To UBound(tenDong)
        If Nhatky(tenDong(j), 4) = Kq(i, 2) Then
            If dic.Count = 0 Then
                Kq(i, 3) = Nhatky(tenDong(j), 6)
                dic(Nhatky(tenDong(j), 6)) = ""
            Else
                If Nhatky(tenDong(j), 6) <> "" Then
                    If dic.Exists(Nhatky(tenDong(j), 6)) = False Then
                        dic(Nhatky(tenDong(j), 6)) = ""
                        Kq(i, 3) = Kq(i, 3) & ", " & Nhatky(tenDong(j), 6)
                    End If
                End If
            End If
       
            Nhatky(tenDong(j), 4) = ""
        End If
    Next j
Next i

With Sheet10
    .Range("C36").Resize(UBound(Kq), UBound(Kq, 2)).ClearContents
    .Range("C36").Resize(UBound(Kq), UBound(Kq, 2)) = Kq
End With
End Sub
 
Upvote 0
Mình xin cám ơn sự giúp đỡ của @Hoàng Nhật Phương nhé. mặc dù code đã chạy đúng kết quả như mong muốn nhưng chắc phải xem lâu lắm mới ngộ ra được.
Mình thắc mắc tại sao phải dùng phủ định của phủ định Not rồi IsEmpty không, không trống làm mấy gà mơ như mình cứ quắn cả não có cách nào viết dễ hiểu hơn xíu không bạn he..
Bài đã được tự động gộp:

Nếu rách việc thì có thể tham khảo cách dùng dic trong code dưới đây.
code dưới được viết cho nhật ký đã sort cột C
Cám ơn bạn đã giúp đỡ nhưng thú thật mình cũng hơi ngại việc Sort dữ liệu.
 
Lần chỉnh sửa cuối:
Upvote 0
Mình xin cám ơn sự giúp đỡ của @Hoàng Nhật Phương nhé. mặc dù code đã chạy đúng kết quả như mong muốn nhưng chắc phải xem lâu lắm mới ngộ ra được.
Mình thắc mắc tại sao phải dùng phủ định của phủ định Not rồi IsEmpty không, không trống làm mấy gà mơ như mình cứ quắn cả não có cách nào viết dễ hiểu hơn xíu không bạn he..
Bài đã được tự động gộp:


Cám ơn bạn đã giúp đỡ nhưng thú thật mình cũng hơi ngại việc Sort dữ liệu.
Dữ liệu trong file bài 10 của bạn đã sort trước theo cột C rồi đấy thôi.
 
Upvote 0
Mình xin cám ơn sự giúp đỡ của @Hoàng Nhật Phương nhé. mặc dù code đã chạy đúng kết quả như mong muốn nhưng chắc phải xem lâu lắm mới ngộ ra được.
Mình thắc mắc tại sao phải dùng phủ định của phủ định Not rồi IsEmpty không, không trống làm mấy gà mơ như mình cứ quắn cả não có cách nào viết dễ hiểu hơn xíu không bạn he..
Bài đã được tự động gộp:


Cám ơn bạn đã giúp đỡ nhưng thú thật mình cũng hơi ngại việc Sort dữ liệu.
Ủa trong bài có câu lệnh:
Mã:
If Not dic.Exists(iKey)  then
câu đó có khiến bạn quắn cả não không vậy ?

thay vì viết:
Mã:
If Not IsEmpty(kq(i, j)) Then

Bạn có thể viết:
Mã:
If IsEmpty(Noi(i, 1)) = False Then

hoặc:
Mã:
If len(kq(i, j)) Then <=>If len(kq(i, j)) > 0 Then

FASLE=0 , còn lại <>0 => TRUE (cá nhân mình hiểu vậy)
 
Upvote 0
If < cái gì đó > Then

thì

< cái gì đó > nghĩa là cái gì đó khác giá trị mặc định. Kiểu này là loại vơ đũa cả nắm, thay vì đối chiếu cụ thể cái gì đó bên vế phải.
 
Upvote 0
thay vì viết:
Mã:
If Not IsEmpty(kq(i, j)) Then
Bạn có thể viết:
Mã:
If IsEmpty(Noi(i, 1)) = False Then
Mình chưa đọc mấy bài trên nên không đóng góp gì cả nhé, nhưng câu này bạn chỉ người ta viết thế này gọn hơn chứ:
Mã:
if Noi(i,1)<>"" then
 
Upvote 0
Web KT

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

Back
Top Bottom