Các bác chỉ giúp, cần viết hàm tổng hợp số liệu.

Liên hệ QC

bun_bo_hue

Thành viên chính thức
Tham gia
31/12/09
Bài viết
78
Được thích
11
Mình muốn dùng macro viết 1 hàm tổng hợp số liệu (như hình bên dưới). Nhưng yêu cầu hàm phải có tốc độ nhanh vì dữ liệu thực tế rất lớn (lưu dạng Excel2007 khoảng 40MB). Các pác siêu lập trình chỉ giúp mình với.

Cám ơn các bác.}}}}}

New Picture.jpg
 

File đính kèm

  • Book8.xls
    26 KB · Đọc: 38
Lần chỉnh sửa cuối:
Em cám ơn anh rất nhiều, đoạn mã của em chạy cho 300,000 dòng chậm quá hà. em mới biết viết thôi, chưa biết làm thế nào cho nó giảm lại. Em sẽ học hỏi anh và các anh chị nhiều hơn.

Cám ơn anh rất nhiều


File gốc đây
http://www.mediafire.com/?14z8z4v6879wibn
Trong ấy đang có code của tôi! Bạn cứ viết thế nào mà ra kết quả giống như tôi là ĂN TIỀN
Ẹc... Ẹc...
 
Upvote 0
Em cám ơn anh rất nhiều, đoạn mã của em chạy cho 300,000 dòng chậm quá hà. em mới biết viết thôi, chưa biết làm thế nào cho nó giảm lại. Em sẽ học hỏi anh và các anh chị nhiều hơn.

Cám ơn anh rất nhiều
Bài này quá lớn với những bạn mới học! Tuy nhiên, đọc file của bạn tôi thấy có cách bố trí dữ liệu xuất thế này:

untitled.JPG

Đây cũng là cách bố trí hay! Tác giả topic cũng nên lưu ý xem liệu ta có nên xuất dữ liệu theo kiểu này không (trực quan đấy chứ)
 
Upvote 0
Mình có 1 ý là sử dụng công thức thay cho việc rà soát cả vùng sẽ bớt đi rất nhiều thời gian. Mặt khác, mảng tên thứ trong tuần đã đwợc tạo theo trình tự nên kết quả luôn được sắp xếp (Khỏi phải lo đoạn này). Code như sau:

Mã:
Sub THop()
Dim Dic As Object, Cls As Range, tam1, tam2, i
Set Dic = CreateObject("Scripting.Dictionary")
With Sheet1
i = 1
For Each Cls In .Range(.[D4], .[D65536].End(3))
If Not Dic.Exists(Cls.Value) Then
Dic.Add Cls.Value, Taochuoi(Cls)
End If
Next
tam1 = Dic.keys
tam2 = Dic.items
For i = 0 To Dic.Count - 1
.Cells(i + 7, "F") = tam1(i) & " ( " & tam2(i) & " )"
Next
End With
Set Dic = Nothing: Set Cls = Nothing
End Sub
'===============================================
Function Taochuoi(ByVal Dk As String) As String
Dim Ar(), i, tam
Dim rg1, rg2, Ir, ch
Ar = Array("Mon", "Tue", "Wed", "Fri", "Thu", "Sat", "Sun")
With Sheet1
Ir = .[D65536].End(3).Row
rg1 = .Name & "!" & .[C4].Resize(Ir - 3).Address
rg2 = .Name & "!" & .[D4].Resize(Ir - 3).Address
For i = 0 To 6
ch = "=SUMPRODUCT(--(" & rg2 & "=""" & Dk & """" & "),--(" & rg1 & "=""" & Ar(i) & """" & "))"
If Evaluate(ch) > 0 Then tam = tam & IIf(Len(tam) > 0, "-", "") & Ar(i)
Next
End With
Taochuoi = tam
End Function
Mình nhờ Ndu test giúp xem thời gian ra sao nhé.
 

File đính kèm

  • Book8_2.xls
    43.5 KB · Đọc: 12
Upvote 0
Mình có 1 ý là sử dụng công thức thay cho việc rà soát cả vùng sẽ bớt đi rất nhiều thời gian. Mặt khác, mảng tên thứ trong tuần đã đwợc tạo theo trình tự nên kết quả luôn được sắp xếp (Khỏi phải lo đoạn này). Code như sau:
Mình nhờ Ndu test giúp xem thời gian ra sao nhé.
Cách này chậm lắm anh à! Vì SUMPRODUCT với nhiều điều kiện thì cũng tương đương với vòng lập For thôi (em thử code của anh, nó treo máy luôn)
Em đang nghĩ đến 1 hướng khác: Dùng PivotTable được không ta? Ai thạo PivotTable trên Excel 2007 làm thử xem (em thấy khó thao tác quá)
 
Upvote 0
Cách này chậm lắm anh à! Vì SUMPRODUCT với nhiều điều kiện thì cũng tương đương với vòng lập For thôi (em thử code của anh, nó treo máy luôn)
Em đang nghĩ đến 1 hướng khác: Dùng PivotTable được không ta? Ai thạo PivotTable trên Excel 2007 làm thử xem (em thấy khó thao tác quá)

Pivot~2007 cũng dễ thao tác mà Bác,
Theo bố trí kiểu bài 22 của Bác thì nó sẽ là thế này:
 

File đính kèm

  • ConsolStr.rar
    303.9 KB · Đọc: 28
Upvote 0
Thử "dọc" nó thôi

Mày mò viết thử xuất ra bảng, tốc độ vẫn chậm, phải mất 7,5 giây
Thay Match bằng một vòng lặp ==> kết quả vẫn thế, chậm hơn tí tẹo
Mệt quá, "hổng" mò nữa
Mã:
Public Sub MoMam()
    Dim Vung As Range, d As Object, Cll As Range, K As Long, Mg(1 To 300000, 1 To 8), TG As Double, iNgay As Range
    Set d = CreateObject("scripting.dictionary")
    Set Vung = Range([f2], [f500000].End(xlUp))
    Set iNgay = [m2:s2]
    TG = Timer:    K = 1
        For Each Cll In Vung
            If Not d.exists(Cll.Value) Then
                d.Add Cll.Value, K
                Mg(K, 1) = Cll.Value
                Mg(K, Application.WorksheetFunction.Match(Cll.Offset(, -3), iNgay, 0) + 1) = "x"
                K = K + 1
            Else
                Mg(d.Item(Cll.Value), Application.WorksheetFunction.Match(Cll.Offset(, -3), iNgay, 0) + 1) = "x"
            End If
        Next
    [l3].Resize(K, 8) = Mg
    MsgBox Timer - TG
End Sub
 
Upvote 0
Mày mò viết thử xuất ra bảng, tốc độ vẫn chậm, phải mất 7,5 giây
Thay Match bằng một vòng lặp ==> kết quả vẫn thế, chậm hơn tí tẹo
Mệt quá, "hổng" mò nữa
Mã:
Public Sub MoMam()
    Dim Vung As Range, d As Object, Cll As Range, K As Long, Mg(1 To 300000, 1 To 8), TG As Double, iNgay As Range
    Set d = CreateObject("scripting.dictionary")
    Set Vung = Range([f2], [f500000].End(xlUp))
    Set iNgay = [m2:s2]
    TG = Timer:    K = 1
        For Each Cll In Vung
            If Not d.exists(Cll.Value) Then
                d.Add Cll.Value, K
                Mg(K, 1) = Cll.Value
                Mg(K, Application.WorksheetFunction.Match(Cll.Offset(, -3), iNgay, 0) + 1) = "x"
                K = K + 1
            Else
                Mg(d.Item(Cll.Value), Application.WorksheetFunction.Match(Cll.Offset(, -3), iNgay, 0) + 1) = "x"
            End If
        Next
    [l3].Resize(K, 8) = Mg
    MsgBox Timer - TG
End Sub
Nguyên tắc để tăng tốc là thế này nè anh ơi:
1> Với bài toán Unique thì không có thuật toán nào qua mặt được với Dictionary và Collect (dùng MACTH, COUNTIF hoặc Find đều chậm)
2> Không nên thực thi tính toán với RANGE mà phải chuyển mọi thứ thuộc RANGE thành ARRAY ---> Ví dụ thế này
PHP:
Dim Clls as Range
For Each Clls in Range("C1:C50000")
......
Next
thì ta nên sửa thành:
PHP:
Dim tmpArr, i as Long
tmpArr = Range("C1:C50000").Value
For i = LBound(tmpArr, 1) to UBound(tmpArr,1)
......
Next
Khi này, muốn tính toán gì thì sẽ tính trên Array ---> Cuối cùng xuất kết quả ra 1 lượt
--------------------------
Em viết code này như vầy:
PHP:
Private Sub ConsolStr2(ByVal sArr1, ByVal sArr2, ByVal Target As Range)
  Dim tArr1, tArr2, Arr(1 To 1000000, 1 To 8)
  Dim wd As New Collection, wdArr, Dic1, Dic2
  Dim i As Long, j As Long, n As Long, k As Long
  Dim Tmp As String, Tmp1 As String, Tmp2 As String
  tArr1 = sArr1: tArr2 = sArr2
  Set Dic1 = CreateObject("Scripting.Dictionary")
  Set Dic2 = CreateObject("Scripting.Dictionary")
  wdArr = Array("Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday")
  Set wd = New Collection
  For k = 0 To 6
    wd.Add CStr(k + 2), wdArr(k)
  Next k
  For i = LBound(tArr1, 1) To UBound(tArr1, 1)
    For j = LBound(tArr1, 2) To UBound(tArr1, 2)
      If tArr1(i, j) <> "" Then
        Tmp1 = tArr1(i, j): Tmp2 = tArr2(i, j)
        Tmp = Tmp1 & Tmp2
        If Not Dic1.Exists(Tmp) Then
          Dic1.Add Tmp, ""
          If Not Dic2.Exists(Tmp1) Then
            n = n + 1
            Dic2.Add Tmp1, n
            Arr(n, 1) = Tmp1
            Arr(n, wd.Item(Tmp2)) = "X"
          Else
            Arr(Dic2.Item(Tmp1), wd.Item(Tmp2)) = "X"
          End If
        End If
      End If
    Next j
  Next i
  Target.Resize(n, 8).Value = Arr
End Sub
PHP:
Sub Main2()
  Dim sArr1, sArr2, Target As Range, TG As Double
  TG = Timer
  sArr1 = Sheet1.Range("F2:F1000000").Value
  sArr2 = Sheet1.Range("C2:C1000000").Value
  Set Target = Sheet1.Range("L3")
  Target.Resize(1000000, 8).Clear
  ConsolStr2 sArr1, sArr2, Target
  MsgBox Timer - TG
End Sub
Tốc độ là 4 giây
 
Lần chỉnh sửa cuối:
Upvote 0
Cám ơn Ndu nhiều

Hihi
Mò ra rồi
Tốc độ KHỦNG
Cám ơn ndu nhiều nhiều
Mã:
Public Sub LaiMo()
    Dim Vung, d As Object, Cll As Range, K As Long, Mg(1 To 300000, 1 To 8), TG As Double, iNgay, I, J, Ngay, Vung2
    Set d = CreateObject("scripting.dictionary")
     Vung = Range([f2], [f500000].End(xlUp)).Value
     Vung2 = Range([c2], [c500000].End(xlUp)).Value
    iNgay = Array("Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday")
    TG = Timer:    K = 1
        For I = LBound(Vung, 1) To UBound(Vung, 1)
        For J = 0 To 6
            If iNgay(J) = Vung2(I, 1) Then Ngay = J + 2: Exit For
        Next
            If Not d.exists(Vung(I, 1)) Then
                d.Add Vung(I, 1), K
                Mg(K, 1) = Vung(I, 1)
                Mg(K, Ngay) = "x"
                K = K + 1
            Else
                Mg(d.Item(Vung(I, 1)), Ngay) = "x"
            End If
        Next
    [l3].Resize(K, 8) = Mg
    MsgBox Timer - TG
End Sub
 
Upvote 0
Hihi
Mò ra rồi
Tốc độ KHỦNG
Cám ơn ndu nhiều nhiều
Mã:
Public Sub LaiMo()
    Dim Vung, d As Object, Cll As Range, K As Long, Mg(1 To 300000, 1 To 8), TG As Double, iNgay, I, J, Ngay, Vung2
    Set d = CreateObject("scripting.dictionary")
     Vung = Range([f2], [f500000].End(xlUp)).Value
     Vung2 = Range([c2], [c500000].End(xlUp)).Value
    iNgay = Array("Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday")
    TG = Timer:    K = 1
        For I = LBound(Vung, 1) To UBound(Vung, 1)
        For J = 0 To 6
            If iNgay(J) = Vung2(I, 1) Then Ngay = J + 2: Exit For
        Next
            If Not d.exists(Vung(I, 1)) Then
                d.Add Vung(I, 1), K
                Mg(K, 1) = Vung(I, 1)
                Mg(K, Ngay) = "x"
                K = K + 1
            Else
                Mg(d.Item(Vung(I, 1)), Ngay) = "x"
            End If
        Next
    [l3].Resize(K, 8) = Mg
    MsgBox Timer - TG
End Sub
Anh con cò "ăn cắp" thời gian nha ---> Lý ra dòng TG = Timer phải nằm ở trên cùng (dưới dòng khai báo biến)
Mà cái này nhanh hơn nữa nè:
PHP:
Sub ConsolStr3()
  Dim tArr1, tArr2, Arr(1 To 300000, 1 To 8), ScrCtr, Dic1, Dic2
  Dim i As Long, j As Long, n As Long, K As Long, TG As Double
  Dim Tmp As String, Tmp1 As String, Tmp2 As String
  TG = Timer
  tArr1 = Sheet1.Range("F2:F300000").Value
  tArr2 = Sheet1.Range("C2:C300000").Value
  Set ScrCtr = CreateObject("MSScriptControl.ScriptControl")
  ScrCtr.Language = "VBScript"
  Set Dic1 = CreateObject("Scripting.Dictionary")
  Set Dic2 = CreateObject("Scripting.Dictionary")
  For i = 1 To UBound(tArr1)
    If tArr1(i, 1) <> "" Then
      Tmp1 = tArr1(i, 1): Tmp2 = tArr2(i, 1): Tmp = Tmp1 & Tmp2
      If Not Dic1.Exists(Tmp) Then
        Dic1.Add Tmp, ""
        If Not Dic2.Exists(Tmp1) Then
          n = n + 1
          Dic2.Add Tmp1, n
          Arr(n, 1) = Tmp1
          Arr(n, ScrCtr.Eval("vb" & Tmp2) + 1) = "X"
        Else
          Arr(Dic2.Item(Tmp1), ScrCtr.Eval("vb" & Tmp2) + 1) = "X"
        End If
      End If
    End If
  Next i
  Sheet1.Range("L3").Resize(n, 8).Value = Arr
  MsgBox Timer - TG
End Sub
-------------------
Nhân tiện đố mọi người biết code trên đã dùng thuật toán gì mà... chẳng thấy "Monday", "Tueday"... nó nằm ở đâu cả vậy?
Ẹc... Ẹc...
 
Upvote 0
Tôi nghĩ lại thấy diễn đàn này cũng có rất nhiều bạn có nhu cầu học VBA như bạn! Vậy sao không tập trung lại chừng 20 người rồi mở lớp?
Lúc trước Bình Admin phát động mà chẳng thấy ai nói gì
Nếu lớp được tổ chức thì sẽ được toàn các cao thủ giảng dạy

20người mới mở đc lớp hả bác ? Vậy khi nào mới có đủ 20người. Bác dạy em trước kô đc hả bác .
 
Upvote 0
Cũng khó chứ chẳng phải dễ ăn đâu ---> Chỉ nội xem trong chuổi Mon-Tue-Wed-Sun, làm sao biến nó thành Mon--->Web,Sun cũng rã rời rồi (vì phải sort chuổi, xem đoạn này "liên tục" thì lấy thằng đầu và cuối)

Học tại đây là ngon lành rồi!
Cách đây 3 năm, khi tôi chưa biết gì, tôi đã học với sư phụ SA_DQ và sư phụ ptm0412 tại đây:
Chập chững đến VBA!
Giới thiệu Cơ bản về vòng lặp For . . . next
Bạn cũng nên vào đây nghiên cứu đi (ngoài ra có thể mua sách do GPE phát hành) ---> Sớm muộn bạn cũng thành cao thủ mà thôi

Bác NDU ơi, em làm được rồi :D. Em ngâm cứu dựa trên nền tảng code của bác, em đã SORT và gộp lại được thứ rồi (ví dụ : "Mon,Fri,Sat,Sun" sẽ thành "2,6-8"). Tốc độ cũng nhanh chả kém nguyên bản code ban đầu của bác (dữ liệu 300ngàn dòng khi chạy code không thấy sự khác biệt khi em thêm code của em vào). Cám ơn bác nhiều lắm ạ ^^.
 
Upvote 0
Web KT
Back
Top Bottom