Thay Pivot 2 chiều bằng VBA

Liên hệ QC

hoahuongduong1986

Thành viên thường trực
Tham gia
14/11/18
Bài viết
346
Được thích
40
Kính gửi anh chị và các bạn,
Em có dữ liệu giả định theo sheet Data và Pivot tại sheet Pivot. Nếu làm Pivot này theo VBA thì như thế nào ạ. Em cảm ơn ạ.
 

File đính kèm

  • Pivot thay bang code.xlsm
    658.5 KB · Đọc: 25

File đính kèm

  • Pivot thay bang code_hoahuongduong1986.xlsm
    575.8 KB · Đọc: 32
Upvote 0
Kính gửi anh chị và các bạn,
Em có dữ liệu giả định theo sheet Data và Pivot tại sheet Pivot. Nếu làm Pivot này theo VBA thì như thế nào ạ. Em cảm ơn ạ.
Mã:
Sub ABC()
    Dim sArr(), NgaySX(), MH(), Res(), Dic As Object, iKey$, j&
    Dim i&, sRow, k&, iR&, jC&, sR&, sC&
    Application.ScreenUpdating = False
    Set Dic = CreateObject("scripting.dictionary")
    Dic.CompareMode = vbTextCompare
    ReDim NgaySX(1 To 1, 1 To 10000)
    ReDim MH(1 To 10000, 1 To 2)
    With Sheets("Data")
        sArr = .Range("Table1").Value
    End With
    sRow = UBound(sArr)
    For i = 1 To sRow
        iKey = sArr(i, 5)
        If Dic.exists(iKey) = False Then
            sC = sC + 1
            Dic.Item(iKey) = sC
            NgaySX(1, sC) = iKey
        End If
        iKey = sArr(i, 2)
        If Dic.exists(iKey) = False Then
            sR = sR + 1
            Dic.Item(iKey) = sR
            MH(sR, 1) = sArr(i, 2)
        End If
    Next i
    ReDim Res(1 To sR, 1 To sC + 1)
    With Sheets("KQ")
        .Range("A4").CurrentRegion.ClearContents
        .Range("A4").Value = "Ma Hang"
        .Range("B4").Value = "Tong"
        .Range("A5").Resize(sR, 1) = MH
        MH = .Range("A5").Resize(sR, 2).Value
        .Range("C4").Resize(1, sC) = NgaySX
        NgaySX = .Range("C4").Resize(1, sC).Value
        For i = 1 To sR
            Dic.Item(MH(i, 1)) = i
        Next i
        For j = 1 To sC
            Dic.Item(NgaySX(1, j)) = j
        Next j
        For i = 1 To sRow
            iKey = sArr(i, 2)
            iR = Dic.Item(iKey)
            jC = Dic.Item(sArr(i, 5))
            Res(iR, jC) = Res(iR, jC) + sArr(i, 7)
            MH(iR, 2) = MH(iR, 2) + sArr(i, 7)
        Next i
        .Range("C5").Resize(sR, sC) = Res
        .Range("A5").Resize(sR, 2) = MH
    End With
    Application.ScreenUpdating = True
End Sub
Thử 1 cách khác theo code mà mình học được của 1 thầy trên diễn đàn
 
Lần chỉnh sửa cuối:
Upvote 0
Upvote 0
VBA quá mạnh mẽ. Em cảm ơn anh nhiều ạ !
Bạn sửa chút chỗ sKey sau dòng For i =
Rich (BB code):
For i = 1 To UBound(arrS)
    sKey = arrS(i, 2)
Bài đã được tự động gộp:

Thử 1 cách khác theo code mà mình học được của 1 thầy trên diễn đàn
Sao code bị lỗi out of range ở đây bạn nhỉ?
1630136563155.png
 
Upvote 0

File đính kèm

  • Pivot thay bang code_hoahuongduong1986.xlsm
    453.4 KB · Đọc: 9
Upvote 0
trên máy em không có bị gì cả ạ. cũng chưa hiểu tại sao bị lỗi đó
Tôi biết tại sao lỗi rồi. Khi ngày đưa vào mảng lại bị đảo ngày thành tháng. Tôi phải sửa lại là NgaySX(1, sC) = Format(iKey, "mm/dd/yyyy") thì code chạy được (Máy tôi định dạng hệ thống trong Control Panel là dd/MM/yyyy)

Chạy khi trong dữ liệu có 1 mã hàng mà nhiều ngày SX thì có lỗi ghi kết quả như sau:
1630138672461.png
 
Upvote 0
Tôi biết tại sao lỗi rồi. Khi ngày đưa vào mảng lại bị đảo ngày thành tháng. Tôi phải sửa lại là NgaySX(1, sC) = Format(iKey, "mm/dd/yyyy") thì code chạy được (Máy tôi định dạng hệ thống trong Control Panel là dd/MM/yyyy)

Chạy khi trong dữ liệu có 1 mã hàng mà nhiều ngày SX thì có lỗi ghi kết quả như sau:
View attachment 264938
Nó ra 2 dòng màu vàng kia là do key đưa vào em có để theo mã hàng&ngày. nên khi trả kết quả nó sẽ tác ra làm n lần nếu như cùng mã hàng mà sản xuất ở nhiều ngày
 
Upvote 0
Nó ra 2 dòng màu vàng kia là do key đưa vào em có để theo mã hàng&ngày. nên khi trả kết quả nó sẽ tác ra làm n lần nếu như cùng mã hàng mà sản xuất ở nhiều ngày
Ở bài #2 tôi cũng làm như vậy (kết quả cũng ra như bạn) nên tại bài #6 tôi đã sửa lại và kết quả là (và nên như thế):
1630139168034.png
 
Upvote 0
Ở bài #2 tôi cũng làm như vậy (kết quả cũng ra như bạn) nên tại bài #6 tôi đã sửa lại và kết quả là (và nên như thế):
View attachment 264940
Em cần anh chỉ giáo thêm giúp em.Cái cột tổng.Em chưa biết làm thế nào để cộng dồn. Anh làm thế nào để cái cột tổng nó cộng dồn lại thế ạ. Xin cho em xem code với ạ
 
Upvote 0
Em cần anh chỉ giáo thêm giúp em.Cái cột tổng.Em chưa biết làm thế nào để cộng dồn. Anh làm thế nào để cái cột tổng nó cộng dồn lại thế ạ. Xin cho em xem code với ạ
Tôi dùng 2 dic: 1 cho mã và 1 cho ngày. Code bài #2 đã sửa theo bài #6:
Rich (BB code):
Sub Pivot_VBA2()
Dim arrS, arrD, arrR
Dim i&, j&, k&
Dim Dic As Object, dic2 As Object
Dim sKey$

arrS = Sheet1.Range("A2:G" & Sheet1.Range("B" & Rows.Count).End(xlUp).Row)
ReDim arrR(1 To UBound(arrS), 1 To 100)
ReDim arrD(1 To 100)
Set Dic = CreateObject("Scripting.Dictionary")
Set dic2 = CreateObject("Scripting.Dictionary")
For i = 1 To UBound(arrS)
    sKey = arrS(i, 2)
    If Not Dic.exists(sKey) Then
        j = j + 1
        Dic.Add sKey, j
        arrR(j, 1) = arrS(i, 2)
        If Not dic2.exists(arrS(i, 5)) Then
            k = k + 1
            dic2.Add arrS(i, 5), k
        End If
        arrR(j, dic2.Item(arrS(i, 5)) + 2) = arrS(i, 7)
        arrR(j, 2) = arrS(i, 7)
        arrD(k) = arrS(i, 5)
    Else
        arrR(Dic.Item(sKey), dic2.Item(arrS(i, 5)) + 2) = arrR(Dic.Item(sKey), dic2.Item(arrS(i, 5)) + 2) + arrS(i, 7)
        arrR(Dic.Item(sKey), 2) = arrR(Dic.Item(sKey), 2) + arrS(i, 7)
    End If
Next
With Sheet3
    .Range("C4").Resize(1, 100).ClearContents
    .Range("A5").Resize(1000, 100).ClearContents
    .Range("A5").Resize(j, k + 2) = arrR
    .Range("C4").Resize(1, k) = arrD
    .Range("C4:K" & k + 4).Sort Key1:=.Range("C4"), Orientation:=xlLeftToRight
End With
Set Dic = Nothing:  Set dic2 = Nothing
End Sub
 
Lần chỉnh sửa cuối:
Upvote 0
Tôi dùng 2 dic: 1 cho mã và 1 cho ngày. Code bài #2 đã sửa theo bài #6:
Rich (BB code):
Sub Pivot_VBA2()
Dim arrS, arrD, arrR
Dim i&, j&, k&
Dim Dic As Object, dic2 As Object
Dim sKey$

arrS = Sheet1.Range("A2:G" & Sheet1.Range("B" & Rows.Count).End(xlUp).Row)
ReDim arrR(1 To UBound(arrS), 1 To 100)
ReDim arrD(1 To 100)
Set Dic = CreateObject("Scripting.Dictionary")
Set dic2 = CreateObject("Scripting.Dictionary")
For i = 1 To UBound(arrS)
    sKey = arrS(i, 2)
    If Not Dic.exists(sKey) Then
        j = j + 1
        Dic.Add sKey, j
        arrR(j, 1) = arrS(i, 2)
        If Not dic2.exists(arrS(i, 5)) Then
            k = k + 1
            dic2.Add arrS(i, 5), k
        End If
        arrR(j, dic2.Item(arrS(i, 5)) + 2) = arrS(i, 7)
        arrR(j, 2) = arrS(i, 7)
        arrD(k) = arrS(i, 5)
    Else
        arrR(Dic.Item(sKey), dic2.Item(arrS(i, 5)) + 2) = arrR(Dic.Item(sKey), dic2.Item(arrS(i, 5)) + 2) + arrS(i, 7)
        arrR(Dic.Item(sKey), 2) = arrR(Dic.Item(sKey), 2) + arrS(i, 7)
    End If
Next
With Sheet3
    .Range("C4").Resize(1, 100).ClearContents
    .Range("A5").Resize(1000, 100).ClearContents
    .Range("A5").Resize(j, k + 2) = arrR
    .Range("C4").Resize(1, k) = arrD
End With
Set Dic = Nothing:  Set dic2 = Nothing
End Sub
Cám ơn anh,đã cập nhật lại tại #4
 
Lần chỉnh sửa cuối:
Upvote 0

File đính kèm

  • Pivot VBA.xlsm
    575.8 KB · Đọc: 13
Upvote 0
Kính gửi anh chị và các bạn,
Em có dữ liệu giả định theo sheet Data và Pivot tại sheet Pivot. Nếu làm Pivot này theo VBA thì như thế nào ạ. Em cảm ơn ạ.
Chạy code
Mã:
Sub XYZ()
  Dim sArr(), aNgay(), aTieuDe(), Res(), Dic As Object, ma$, ngay
  Dim i&, sRow, k&, t&, iR&, jC&, sR&, sC&
 
  aTieuDe = Range("A4:B4").Value
  With Sheets("Data")
    sArr = .Range("B2:G" & .Range("B" & Rows.Count).End(xlUp).Row).Value
  End With
  sRow = UBound(sArr)
  ReDim Res(1 To sRow, -1 To 100)
  ReDim aNgay(1 To 100)
  Set Dic = CreateObject("Scripting.Dictionary")
  For i = 1 To sRow
    ngay = sArr(i, 4)
    If Not Dic.exists(ngay) Then
      t = t + 1
      Dic.Add ngay, t
      aNgay(t) = ngay
    End If
    ma = sArr(i, 1)
    If Not Dic.exists(ma) Then
      k = k + 1
      Dic.Add ma, k
      Res(k, -1) = ma
    End If
    iR = Dic.Item(ma): jC = Dic.Item(ngay)
    Res(iR, 0) = Res(iR, 0) + sArr(i, 6)
    Res(iR, jC) = Res(iR, jC) + sArr(i, 6)
  Next i
  With Sheets("KQ")
    .Range("A4").CurrentRegion.ClearContents
    .Range("A4:B4") = aTieuDe
    .Range("A5").Resize(k, t + 2) = Res
    .Range("C4").Resize(, t) = aNgay
    .Range("C4").Resize(k + 1, t).Sort Key1:=.Range("C4"), Orientation:=2
    .Range("A5").Resize(k, t + 2).Sort Key1:=.Range("A5"), Orientation:=1
  End With
End Sub
 
Upvote 0
Pivot table đúng là nhàn tênh.
Viết code mệt lắm.
Tôi mới học được chút ADO, áp dụng vô coi sao.
PHP:
Sub TransformData()
    Dim cnn As Object, Rst As Object
    Dim strQuery As String, fCount As Byte, X As Long
    
    Set cnn = CreateObject("ADODB.Connection")
    Set Rst = CreateObject("ADODB.Recordset")
    
    With cnn
        .connectionstring = "Provider=Microsoft.ACE.OLEDB.12.0" & _
                            ";Data Source=" & ThisWorkbook.FullName & _
                            ";Extended Properties='Excel 12.0 Xml;HDR=No';"
        .Open
    End With
  
    strQuery = _
            "TRANSFORM " & _
                "SUM([t.F7]) " & _
            "SELECT " & _
                "[t.F2] AS [Ma] " & _
            "FROM " & _
                "[Data$] AS t " & _
            "WHERE " & _
                "[t.F3] <> 'Lot' " & _
            "GROUP BY " & _
                "[t.F2] " & _
            "PIVOT " & _
                "[t.F5]"
                
    With Rst
        .ActiveConnection = cnn
        .Source = strQuery
        .Open
    End With
    
    Sheet2.Range("A20").CurrentRegion.Clear
    X = Sheet2.Range("A20").CopyFromRecordset(Rst)
    
    For fCount = 1 To Rst.Fields.Count
        Sheet2.Range("A20").Offset(-1, fCount - 1) = Rst.Fields(fCount - 1).Name
    Next fCount
    
    With Sheet2
        .Range("A20").Offset(X) = "Tong cong"
        .Range("A20").Offset(-1, fCount - 1) = "Tong cong"
        .Range("A20").Offset(X, 1).Resize(, fCount - 1).FormulaR1C1 = "=SUM(R[-" & X & "]C:R[-1]C)"
        .Range("A20").Offset(, fCount - 1).Resize(X).FormulaR1C1 = "=SUM(RC[-" & (fCount - 2) & "]:RC[-1])"
    End With
    
    Rst.Close
    cnn.Close
    Set cnn = Nothing: Set Rst = Nothing
    MsgBox "Done", vbInformation, "GPE"
End Sub
Bài đã được tự động gộp:

Chạy code
Mã:
Sub XYZ()
  Dim sArr(), aNgay(), aTieuDe(), Res(), Dic As Object, ma$, ngay
  Dim i&, sRow, k&, t&, iR&, jC&, sR&, sC&
 
  aTieuDe = Range("A4:B4").Value
  With Sheets("Data")
    sArr = .Range("B2:G" & .Range("B" & Rows.Count).End(xlUp).Row).Value
  End With
  sRow = UBound(sArr)
  ReDim Res(1 To sRow, -1 To 100)
  ReDim aNgay(1 To 100)
  Set Dic = CreateObject("Scripting.Dictionary")
  For i = 1 To sRow
    ngay = sArr(i, 4)
    If Not Dic.exists(ngay) Then
      t = t + 1
      Dic.Add ngay, t
      aNgay(t) = ngay
    End If
    ma = sArr(i, 1)
    If Not Dic.exists(ma) Then
      k = k + 1
      Dic.Add ma, k
      Res(k, -1) = ma
    End If
    iR = Dic.Item(ma): jC = Dic.Item(ngay)
    Res(iR, 0) = Res(iR, 0) + sArr(i, 6)
    Res(iR, jC) = Res(iR, jC) + sArr(i, 6)
  Next i
  With Sheets("KQ")
    .Range("A4").CurrentRegion.ClearContents
    .Range("A4:B4") = aTieuDe
    .Range("A5").Resize(k, t + 2) = Res
    .Range("C4").Resize(, t) = aNgay
    .Range("C4").Resize(k + 1, t).Sort Key1:=.Range("C4"), Orientation:=2
    .Range("A5").Resize(k, t + 2).Sort Key1:=.Range("A5"), Orientation:=1
  End With
End Sub
Việc tối ưu hóa vòng lặp của anh quá tuyệt vời :clapping::clapping::clapping:
 
Upvote 0
Chạy code
Mã:
Sub XYZ()
  Dim sArr(), aNgay(), aTieuDe(), Res(), Dic As Object, ma$, ngay
  Dim i&, sRow, k&, t&, iR&, jC&, sR&, sC&
 
  aTieuDe = Range("A4:B4").Value
  With Sheets("Data")
    sArr = .Range("B2:G" & .Range("B" & Rows.Count).End(xlUp).Row).Value
  End With
  sRow = UBound(sArr)
  ReDim Res(1 To sRow, -1 To 100)
  ReDim aNgay(1 To 100)
  Set Dic = CreateObject("Scripting.Dictionary")
  For i = 1 To sRow
    ngay = sArr(i, 4)
    If Not Dic.exists(ngay) Then
      t = t + 1
      Dic.Add ngay, t
      aNgay(t) = ngay
    End If
    ma = sArr(i, 1)
    If Not Dic.exists(ma) Then
      k = k + 1
      Dic.Add ma, k
      Res(k, -1) = ma
    End If
    iR = Dic.Item(ma): jC = Dic.Item(ngay)
    Res(iR, 0) = Res(iR, 0) + sArr(i, 6)
    Res(iR, jC) = Res(iR, jC) + sArr(i, 6)
  Next i
  With Sheets("KQ")
    .Range("A4").CurrentRegion.ClearContents
    .Range("A4:B4") = aTieuDe
    .Range("A5").Resize(k, t + 2) = Res
    .Range("C4").Resize(, t) = aNgay
    .Range("C4").Resize(k + 1, t).Sort Key1:=.Range("C4"), Orientation:=2
    .Range("A5").Resize(k, t + 2).Sort Key1:=.Range("A5"), Orientation:=1
  End With
End Sub
Riêng việc dùng duy nhất 1 dic e thấy rất tuyệt vời ạ.
Em đang nghĩ bài này phải xài 2, 3 dic mới xử lý được.
 
Upvote 0
Riêng việc dùng duy nhất 1 dic e thấy rất tuyệt vời ạ.
Em đang nghĩ bài này phải xài 2, 3 dic mới xử lý được.
Thực chất 1 Dic duy nhất đó đang chứa 2 loại dữ liệu mã và ngày. Nếu viết tường minh thì 2 Dic: 1 cho mã và 1 cho ngày. Đây là cách làm tắt mới xuất hiện gần đây.
 
Upvote 0
Web KT

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

Back
Top Bottom