bigbabol89
Thành viên thường trực
- Tham gia
- 15/10/12
- Bài viết
- 225
- Được thích
- 34
Bài này powerquery unpivot columns nhanh lắmXin chào các anh chị,
Em muốn nhờ các anh chị giúp : chuyển dữ liệu từ cột thành hàng ( như file đính kèm ).
Mong các anh chị giúp đỡ ạ.
Em cám ơn ạ.
View attachment 257071
Cái này em cũng không biết làm, mà do file em cũng chia sẻ cho nhiều người nên em nghĩ dùng vba sẽ hợp hơn ( vì mọi người cũng giống em )Bài này powerquery unpivot columns nhanh lắm
Em làm thử thấy cũng nhanh mà anh, mà không rõ ý "chưa chắc" của anh là gìChưa chắc nhé.
Power query thì hơi phiền phức về phần office nữa. Không phải office nào cũng cóCái này em cũng không biết làm, mà do file em cũng chia sẻ cho nhiều người nên em nghĩ dùng vba sẽ hợp hơn ( vì mọi người cũng giống em )
Thử power query với 100 ngàn dòng, unpivot 4 cột trở thành 400 ngàn dòng thì sẽ biết nhanh hay chậm nếu so với VBA sử dụng mảng. Ưu điểm của Power query là đơn giản, thao tác bằng tay đơn giản, ai cũng tự làm được. Nếu lưu kết quả ở Data Model thì file cũng nhẹ hơn, mà dùng được cho các báo cáo khác.Em làm thử thấy cũng nhanh mà anh, mà không rõ ý "chưa chắc" của anh là gì
Xin chạy thử code. Chỗ nào sai mọi người góp ý thêmXin chào các anh chị,
Em muốn nhờ các anh chị giúp : chuyển dữ liệu từ cột thành hàng ( như file đính kèm ).
Mong các anh chị giúp đỡ ạ.
Em cám ơn ạ.
Sub ABC()
Dim iR&, iC&, i&, sArr(), Res(), j&, K&
With Sheets("Sheet1")
iR = .Range("A" & Rows.Count).End(3).Row - 1
iC = .Cells(2, "XFD").End(1).Column
sArr = .Range("A2").Resize(iR, iC).Value
End With
ReDim Res(1 To UBound(sArr, 1) * (iC - 1), 1 To 3)
For j = 2 To UBound(sArr, 2)
For i = 2 To UBound(sArr, 1)
K = K + 1
Res(K, 1) = sArr(i, 1)
Res(K, 2) = sArr(1, j)
Res(K, 3) = sArr(i, j)
Next
Next
With Sheets("Sheet2")
.Range("A2").Resize(10000, 3).ClearContents
If K Then .Range("A2").Resize(K, 3).Value = Res
End With
MsgBox "Da xong"
End Sub
Code chạy rất tốt anh ạ.Xin chạy thử code. Chỗ nào sai mọi người góp ý thêm
Mã:Sub ABC() Dim iR&, iC&, i&, sArr(), Res(), j&, K& With Sheets("Sheet1") iR = .Range("A" & Rows.Count).End(3).Row - 1 iC = .Cells(2, "XFD").End(1).Column sArr = .Range("A2").Resize(iR, iC).Value End With ReDim Res(1 To UBound(sArr, 1) * (iC - 1), 1 To 3) For j = 2 To UBound(sArr, 2) For i = 2 To UBound(sArr, 1) K = K + 1 Res(K, 1) = sArr(i, 1) Res(K, 2) = sArr(1, j) Res(K, 3) = sArr(i, j) Next Next With Sheets("Sheet2") .Range("A2").Resize(10000, 3).ClearContents If K Then .Range("A2").Resize(K, 3).Value = Res End With MsgBox "Da xong" End Sub
Sub iTransposeData()
Application.CutCopyMode = False
'--------------------------------------------------------------------------------------------------------------------------------------------
For Each tbl In ActiveSheet.ListObjects
If tbl.Name = "tbl_Data" Then tbl.Unlist
Next
'--------------------------------------------------------------------------------------------------------------------------------------------
Dim MyTblRange As Range: Set MyTblRange = Application.InputBox("Select the source data-range to transpose:", "Data source", Type:=8)
ActiveSheet.ListObjects.Add(SourceType:=xlSrcRange, Source:=MyTblRange, xlListObjectHasHeaders:=xlYes).Name = "tbl_Data"
'--------------------------------------------------------------------------------------------------------------------------------------------
Dim iRngDestination As Range: Set iRngDestination = Application.InputBox("Select a range to place your data:", "Data unpivotcolumn", Type:=8)
'--------------------------------------------------------------------------------------------------------------------------------------------
For i = 1 To ActiveWorkbook.Queries.Count
If ActiveWorkbook.Queries.Item(i).Name = "qry_Data" Then ActiveWorkbook.Queries.Item(i).Delete
Next i
'--------------------------------------------------------------------------------------------------------------------------------------------
ActiveWorkbook.Queries.Add Name:="qry_Data", Formula:= _
"let" & Chr(13) & "" & Chr(10) & " Source = Excel.CurrentWorkbook(){[Name=""tbl_Data""]}[Content]," & Chr(13) & "" & Chr(10) & " #""Changed Type"" = Table.TransformColumnTypes(Source,{{""Date"", type datetime}, {""A"", Int64.Type}, {""B"", Int64.Type}, {""C"", Int64.Type}, {""D"", Int64.Type}})," & Chr(13) & "" & Chr(10) & " #""Unpivoted Columns"" = Table.UnpivotOtherColumns(#""Changed Type"", {""Date""}, ""Attribute"", ""Value"")," & Chr(13) & "" & Chr(10) & " #""" & _
"Changed Type1"" = Table.TransformColumnTypes(#""Unpivoted Columns"",{{""Date"", type date}})," & Chr(13) & "" & Chr(10) & " #""Sorted Rows"" = Table.Sort(#""Changed Type1"",{{""Attribute"", Order.Ascending}})" & Chr(13) & "" & Chr(10) & "in" & Chr(13) & "" & Chr(10) & " #""Sorted Rows"""
'--------------------------------------------------------------------------------------------------------------------------------------------
With ActiveSheet.ListObjects.Add(SourceType:=0, Source:= _
"OLEDB;Provider=Microsoft.Mashup.OleDb.1;Data Source=$Workbook$;Location=""qry_Data"";Extended Properties=""""" _
, Destination:=iRngDestination).QueryTable
.CommandType = xlCmdSql
.CommandText = Array("SELECT * FROM [qry_Data]")
.BackgroundQuery = True
.RefreshStyle = xlInsertDeleteCells
.ListObject.DisplayName = "tbl_Data_" & Format(Now(), "DDMMYY_hhmmss")
.Refresh BackgroundQuery:=False
End With
'--------------------------------------------------------------------------------------------------------------------------------------------
End Sub
Sub iTransposeSQL()
Dim MyTbl As Range: Set MyTbl = Application.InputBox("Select the source data-range to transpose:", "Data source", Type:=8)
Dim iDestination As Range: Set iDestination = Application.InputBox("Select a range to place your data:", "Data unpivotcolumn", Type:=8)
Dim mTbl As String: mTbl = "[" & ActiveSheet.Name & "$" & Replace(MyTbl.Address, "$", "") & "]"
'-----------------------------------------------------------------------------------------------------------------------------------------
Dim mSQL As String
mSQL = "SELECT [DATE], 'A', A FROM " & mTbl & Chr(10) & "UNION ALL" & Chr(10)
mSQL = mSQL & "SELECT [DATE], 'B', B FROM " & mTbl & Chr(10) & "UNION ALL" & Chr(10)
mSQL = mSQL & "SELECT [DATE], 'C', C FROM " & mTbl & Chr(10) & "UNION ALL" & Chr(10)
mSQL = mSQL & "SELECT [DATE], 'D', D FROM " & mTbl
'-----------------------------------------------------------------------------------------------------------------------------------------
Application.CutCopyMode = False
'-----------------------------------------------------------------------------------------------------------------------------------------
With CreateObject("ADODB.Connection")
.Open "Provider=Microsoft.ACE.OLEDB.12.0;Extended Properties=""Excel 12.0;HDR=YES"";Data Source=" & ThisWorkbook.FullName
iDestination.Offset(1, 0).CopyFromRecordset .Execute(mSQL)
End With
'-----------------------------------------------------------------------------------------------------------------------------------------
End Sub
=S_JoinV(A2:E100000) |
' _,
' ___ _ _ _ ___(_)
'/ __| / \ | \| | _ | |
'\__ \/ \ \| \\ | _ \ |
'|___/_/ \_|_|\_|___/_|
'
Option Explicit
#If VBA7 Then
Private Declare PtrSafe Function SetTimer Lib "user32" (ByVal hwnd As LongPtr, ByVal nIDEvent As LongPtr, ByVal uElapse As LongPtr, ByVal lpTimerFunc As LongPtr) As Long
Private Declare PtrSafe Function KillTimer Lib "user32" (ByVal hwnd As LongPtr, ByVal nIDEvent As LongPtr) As Long
#Else
Private Declare Function SetTimer Lib "user32" (ByVal HWnd As Long, ByVal nIDEvent As Long, ByVal uElapse As Long, ByVal lpTimerFunc As Long) As Long
Private Declare Function KillTimer Lib "user32" (ByVal HWnd As Long, ByVal nIDEvent As Long) As Long
#End If
Private Type TypeArgs
Action As Long
Target As Range
Caller As Range
Formula As String
End Type
#If VBA7 And Win64 Then
Private gTimerID As LongPtr
#Else
Private gTimerID As Long
#End If
Private Args() As TypeArgs, WorkIndex As Integer
Function S_JoinV(ByVal Target As Range) As Variant
On Error Resume Next
Dim k&, r, F$
Set r = Application.Caller
F = r(1, 1).Formula
k = UBound(Args)
S_JoinV = Target(2, 1).Value
ReDim Preserve Args(1 To k + 1)
With Args(k + 1)
Set .Target = Target
.Formula = F
Set .Caller = r
.Action = 0
End With
If gTimerID = 0 Then
gTimerID = SetTimer(0&, 0&, 1, AddressOf S_JoinV_callback)
End If
End Function
Private Sub S_JoinV_callback()
On Error Resume Next
Call KillTimer(0&, gTimerID): gTimerID = 0
S_JoinV_working
On Error GoTo 0
End Sub
Private Sub S_JoinV_working()
On Error Resume Next
Dim UA%, s$
UA = UBound(Args)
If UA > 0 Then
WorkIndex = WorkIndex + 1
Dim a As TypeArgs, AP As Object
a = Args(WorkIndex)
Set AP = a.Caller.Parent.Parent.Parent
If a.Action <> 0 Or a.Caller.Formula <> a.Formula Then
GoTo N
End If
Args(WorkIndex).Action = 1
'//'//'//'//'//'//'//'//'//'//'//'//'//'//'//'//'//'//'//'//
Dim SU As Boolean, AC As Long
SU = AP.ScreenUpdating
AC = AP.Calculation
If AC <> -4135 Then
AP.Calculation = -4135
End If
If SU Then
AP.ScreenUpdating = False
End If
Dim w, r, lr&, C%
Set w = a.Target.Parent
Set r = a.Target(2, 1)
lr = r(a.Target.Rows.Count - 1, 1).End(3).Row - r.Row + 1
a.Caller(2, 1).Resize(w.Rows.Count - a.Caller.Row, 3).ClearContents
If lr > 0 And a.Target.Columns.Count > 1 Then
For C = 1 To a.Target.Columns.Count - 1
If C * lr + 1 > w.Rows.Count Then
Exit For
End If
If C = 1 Then
a.Caller((C - 1) * lr + 2, 1).Resize(lr - 1, 1).Value = r(2, 1).Resize(lr - 1, 1).Value
Else
a.Caller((C - 1) * lr + 1, 1).Resize(lr, 1).Value = r(1, 1).Resize(lr, 1).Value
End If
a.Caller((C - 1) * lr + 1, 2).Resize(lr, 1).Value = r(0, C + 1).Value
a.Caller((C - 1) * lr + 1, 3).Resize(lr, 1).Value = r(1, C + 1).Resize(lr, 1).Value
Next
End If
If SU And SU <> AP.ScreenUpdating Then
AP.ScreenUpdating = SU
End If
If AP.Calculation <> AC And AC = -4105 Then
AP.Calculation = AC
End If
Ends:
'//'//'//'//'//'//'//'//'//'//'//'//'//'//'//'//'//'//'//'//
Set r = Nothing
Set w = Nothing: Set AP = Nothing
N:
If WorkIndex >= UA Then
Erase Args: WorkIndex = 0
Else
gTimerID = SetTimer(0&, 0&, 1, AddressOf S_JoinV_callback)
End If
End If
On Error GoTo 0
End Sub