請教各位大神,我有一組excel資料如下,需要依據最好的排序個數平均分攤金額與利息,這部分是否有可能用excel VBA或其他方式達成?因為資料有將近三萬筆,逐一人工作業完眼睛也花了,先感謝了!
Before:在同一ID下的總金額已經拆分(均分)到每筆資料。
AFTER:需要將總金額重新均分到排序最好(數字越小排序越好)的資料中,其餘非最好排序的金額為0。
以ID:001為例,排序最好的為"9",總共有5筆,因此需將總金額(18,812,754)均分5等分(3,762,551)至排序為"9"的資料列中,其餘排序(10、11 or 13)的金額為"0"。利息也是一樣的做法。以此類推ID:002、ID:003.....
感謝各位大神的協助,非常感謝!!!
Access 連結 Excel : 三萬筆資料用連結即可,匯入可能很耗時;下次使用時只要覆蓋 Excel
來源 Excel
讀取 Excel
產出結果
SQL 指令
SELECT Amount.A,
Amount.B,
IIF(X.Counter <> null ,Round(X.DX / X.Counter,0),0) AS C,
Amount.D,
IIF(X.Counter <> null,Round(X.FX / X.Counter,0),0) AS E,
Amount.F,
X.Counter
FROM 工作表1 AS Amount LEFT JOIN (Select Amount.A,Amount.B,Count(Amount.B) As Counter,P.DX,P.FX
From 工作表1 As Amount
Inner Join (
Select A,Min(B) As BX,Max(D) As DX,Max(F) As FX
From 工作表1 As Amount
Group By A
) As P On P.A = Amount.A And P.BX = Amount.B
Group By Amount.A,Amount.B,P.DX,P.FX
) AS X ON (X.B = Amount.B) AND (X.A = Amount.A);
排序欄位取值 => 每個 ID 群組排序欄位取最小值,不一定都是 9
菩薩慈悲:藉由Access,有創意。但效能上,是否多了一層,隔簾望月?感恩感恩 南無阿彌陀佛
問題不在於多一層,而是演算法;
如果 VBA 寫得很出色的話,就可能比 SQL 演算更快;
等版主用三萬筆跑一次就知了.
原來如此,確實,問題的關鍵所在,比皮相的直觀,要來的重要多了。感恩 大師指教,長見識了。感恩感恩讚歎讚歎南無阿彌陀佛
只是如果SQL可行的話,是否可以考慮直接在excel裡頭應用SQL來演算就好?就像在excel VBA裡頭應用ADODB一樣
Excel VBA + SQL
來源 Excel :
重計後 Excel :
SQL :
StrSQl = "SELECT Amount.A, " & _
"Amount.B, " & _
"IIF(X.Counter <> null ,Round(X.DX / X.Counter,0),0) AS C, " & _
"Amount.D, " & _
"IIF(X.Counter <> null,Round(X.FX / X.Counter,0),0) AS E, " & _
"Amount.F, " & _
"X.Counter " & _
"FROM [工作表1$] AS Amount " & _
"LEFT JOIN (Select Amount.A As A,Amount.B As B,Count(Amount.B) As [Counter],P.DX As DX,P.FX As FX " & _
" From [工作表1$] As Amount " & _
" Inner Join ( " & _
" Select A,Min(B) As BX,Max(D) As DX,Max(F) As FX " & _
" From [工作表1$] As Amount " & _
" Group By A " & _
" ) As P On P.A = Amount.A And P.BX = Amount.B " & _
" Group By Amount.A,Amount.B,P.DX,P.FX " & _
") AS X ON (X.B = Amount.B) AND (X.A = Amount.A) "
程式碼太長了不上傳
非常感謝!!!
果然是 大師!沒有源碼,末學無法測試瞭解,有機會再來向 菩薩您討教源程式碼 感恩感恩 讚歎讚歎 南無阿彌陀佛
shanminglo菩薩慈悲,有機會也煩請您將末學的VBA和 大師菩薩的這個跑看看效能比如何。末學也很想知道。這會是要不要急著學SQL的有力動機。末學想對 菩薩您也是適用的。感恩感恩 讚歎讚歎 南無阿彌陀佛
ps. ∵ 大師菩薩用的是Excel公式和SQL,末學曾見聞人反應在大數據時,系統資源會很吃緊,不知確否,還請查證。人無遠慮必有近憂,致遠恐泥,是以君子不為耳。感恩感恩 南無阿彌陀佛
Excel VBA+ADO+SQL入門教程024:初識Recordset物件
主要程式碼片段(可測試用)
Sub Cal_Click()
Dim cnn As Object
Dim rst As Object
Dim strPath As String
Dim StrSQl As String
Dim lngCount As Long
Dim i As Integer
Dim yi As Integer
Dim xi As Integer
Dim ColorValue As Integer
Dim ID_Old As String
Set cnn = CreateObject("ADODB.Connection")
Set rst = CreateObject("ADODB.RecordSet")
'----後期引用Recordset物件
strPath = ThisWorkbook.FullName
'----指定ADO連線的檔案路徑(本工作簿)
cnn.Open "Provider=Microsoft.ACE.OLEDB.12.0;" _
& "Extended Properties=Excel 12.0;" _
& "Data Source=" & strPath
'
StrSQl = "SELECT Amount.A, " & _
"Amount.B, " & _
"IIF(X.Counter <> null ,Round(X.DX / X.Counter,0),0) AS C, " & _
"Amount.D, " & _
"IIF(X.Counter <> null,Round(X.FX / X.Counter,0),0) AS E, " & _
"Amount.F, " & _
"X.Counter " & _
"FROM [工作表1$] AS Amount " & _
"LEFT JOIN (Select Amount.A As A,Amount.B As B,Count(Amount.B) As [Counter],P.DX As DX,P.FX As FX " & _
" From [工作表1$] As Amount " & _
" Inner Join ( " & _
" Select A,Min(B) As BX,Max(D) As DX,Max(F) As FX " & _
" From [工作表1$] As Amount " & _
" Group By A " & _
" ) As P On P.A = Amount.A And P.BX = Amount.B " & _
" Group By Amount.A,Amount.B,P.DX,P.FX " & _
") AS X ON (X.B = Amount.B) AND (X.A = Amount.A) "
'----SQL語句
rst.Open StrSQl, cnn, 1, 3
'----使用Open方法建立記錄集
'
xi = 2
yi = 10
ID_Old = ""
'淡綠色
ColorValue = 35
rst.MoveFirst
Do Until rst.EOF
If ID_Old <> rst.Fields.Item("A") Then
ID_Old = rst.Fields.Item("A")
If ColorValue = 35 Then
'淡黃色
ColorValue = 19
Else
ColorValue = 35
End If
Range(Cells(xi, yi + 1), Cells(xi, yi + 7)).Select
With Selection
'填滿的顏色
.Interior.ColorIndex = ColorValue
End With
Else
If rst.Fields.Item("C") <> 0 Then
Range(Cells(xi, yi + 1), Cells(xi, yi + 7)).Select
With Selection
'填滿的顏色
.Interior.ColorIndex = ColorValue
End With
End If
End If
Cells(xi, yi + 1) = rst.Fields.Item("A")
Cells(xi, yi + 2) = rst.Fields.Item("B")
Cells(xi, yi + 3) = Format(rst.Fields.Item("C"), "#,##0")
Cells(xi, yi + 4) = Format(rst.Fields.Item("D"), "#,##0")
Cells(xi, yi + 5) = Format(rst.Fields.Item("E"), "#,##0")
Cells(xi, yi + 6) = Format(rst.Fields.Item("F"), "#,##0")
Cells(xi, yi + 7) = rst.Fields.Item("Counter")
xi = xi + 1
rst.MoveNext
Loop
'
'----讀取記錄集中的記錄
lngCount = rst.RecordCount
'----記錄的數目
MsgBox "共查詢到:" & lngCount & "條記錄。"
rst.Close
cnn.Close
Set rst = Nothing
Set cnn = Nothing
End Sub
菩薩慈悲:做好收工。請 菩薩垂慈驗收。感恩感恩 南無阿彌陀佛
Option Explicit
Enum ColumnName
ID = 1
Sort = 2
splitAmount = 3
NoSplitAamount = 4
splitInterest = 5
NoSplitInterest = 6
End Enum
Sub AverageShareAmountInterest()
Dim ws As Worksheet, c As Range, r As Long, rc As Long, keyID, itemCell, arr
' Dim dictIDdict As New Scripting.Dictionary
' Dim dictSortCells As New Scripting.Dictionary
' Dim dictNoSplitAamount As New Scripting.Dictionary, dictNoSplitInterest As New Scripting.Dictionary
Dim dictIDdict As Object, dictSortCells As Object, dictNoSplitAamount As Object, dictNoSplitInterest As Object
Dim clnCells As New VBA.Collection
Set dictIDdict = CreateObject("Scripting.Dictionary")
Set dictSortCells = CreateObject("Scripting.Dictionary")
Set dictNoSplitAamount = CreateObject("Scripting.Dictionary")
Set dictNoSplitInterest = CreateObject("Scripting.Dictionary")
Dim splitAmount As Long, splitInterest As Long
Set ws = ActiveSheet
rc = ws.UsedRange.Rows.Count
For r = 2 To rc
If dictIDdict.Exists(Cells(r, ColumnName.ID).Value) Then
Set dictSortCells = dictIDdict(Cells(r, ColumnName.ID).Value)
If dictSortCells.Exists(Cells(r, ColumnName.Sort).Value) Then
Set clnCells = dictSortCells(Cells(r, ColumnName.Sort).Value)
clnCells.Add Cells(r, ColumnName.splitAmount)
Else
Set clnCells = Nothing
clnCells.Add Cells(r, ColumnName.splitAmount)
dictSortCells.Add Cells(r, ColumnName.Sort).Value, clnCells
End If
Else
Set dictSortCells = CreateObject("Scripting.Dictionary")
Set clnCells = Nothing
clnCells.Add Cells(r, ColumnName.splitAmount)
dictSortCells.Add Cells(r, ColumnName.Sort).Value, clnCells
dictIDdict.Add Cells(r, ColumnName.ID).Value, dictSortCells
End If
If Cells(r, ColumnName.NoSplitAamount).Value <> "" Then
dictNoSplitAamount(Cells(r, ColumnName.ID).Value) = Cells(r, ColumnName.NoSplitAamount).Value
End If
If Cells(r, ColumnName.NoSplitInterest).Value <> "" Then
dictNoSplitInterest(Cells(r, ColumnName.ID).Value) = Cells(r, ColumnName.NoSplitInterest).Value
End If
Next r
Randomize
For Each keyID In dictIDdict.Keys
r = RGB(Int(Rnd * 192), Int(Rnd * 192), Int(Rnd * 192))
'r = RGB(Int(Rnd * 256), Int(Rnd * 256), Int(Rnd * 256))
Set dictSortCells = dictIDdict(keyID)
arr = dictSortCells.Keys
Call QuickSortArray(arr, LBound(arr), UBound(arr))
arr = arr(0) '取得最小值(排序最好的)
splitAmount = dictNoSplitAamount(keyID) / dictSortCells(arr).Count
splitInterest = dictNoSplitInterest(keyID) / dictSortCells(arr).Count
For Each itemCell In dictSortCells(arr)
Cells(itemCell.Row, ColumnName.Sort).Font.Color = RGB(255, 0, 0)
Range(Cells(itemCell.Row, ColumnName.ID), Cells(itemCell.Row, ColumnName.NoSplitInterest)).Interior.Color = r
If itemCell.NumberFormat <> "#,###" Then itemCell.NumberFormat = "#,###"
itemCell.Value = splitAmount
If Cells(itemCell.Row, ColumnName.splitInterest).NumberFormat <> "#,###" Then Cells(itemCell.Row, ColumnName.splitInterest).NumberFormat = "#,###"
Cells(itemCell.Row, ColumnName.splitInterest).Value = splitInterest
Next itemCell
Next keyID
End Sub
Private Sub QuickSortArray(ByRef arr As Variant, ByVal left As Long, ByVal right As Long)
Dim i As Long
Dim j As Long
Dim pivot As Variant
Dim temp As Variant
i = left
j = right
pivot = arr((left + right) \ 2)
While i <= j
While arr(i) < pivot And i < right
i = i + 1
Wend
While pivot < arr(j) And j > left
j = j - 1
Wend
If i <= j Then
temp = arr(i)
arr(i) = arr(j)
arr(j) = temp
i = i + 1
j = j - 1
End If
Wend
If left < j Then
Call QuickSortArray(arr, left, j)
End If
If i < right Then
Call QuickSortArray(arr, i, right)
End If
End Sub
您好,請問「¨ú±o³Ì¤pÈ¡]±Æ§Ç³Ì¦nªº¡^」是不是字的編碼跑掉了呢?
jeffeux菩薩慈悲。是的這是我在複製的時候忘了切換回中文語系的緣故,因為VBE只支援Big5編碼。感謝你的指出,等一下我就改過來。但應該不影響程式的運作。只是請菩薩您留意,已經有最佳解答的大師菩薩測試出來在3萬筆資料的情況下,我這個程式需要2分多鐘,而他的最佳解答,最慢的話,卻只需要不到20秒的時間哦。請您斟酌參考。感恩感恩 南無阿彌陀佛
假設
1.工作表中已設定總金額及總利息
1.Before
2.Execute VBA
3.After
還是 海綿寶寶大菩薩您厲害,乾淨利落不囉嗦。只是 您的巨集Main在哪裡呢?可 開源供末學們參考麼?末學偶比較有興趣的是在按下「編輯(E)」按鈕後的風情,不是按下「執行(R)」按鈕後的收割。^_^ 感恩感恩讚歎讚歎海綿寶寶大菩薩南無阿彌陀佛
shanminglo菩薩慈悲,希望 下次能貼上資料或附上樣本,免得還得像末學和 海綿寶寶大神一樣,自己鍵入資料來測試,很煩人的,人一麻煩,就更不想解答了,即使再駕輕就熟…… 好嗎 感恩感恩讚歎讚歎南無阿彌陀佛
C2:
=IF(B2=9,AVERAGEIFS(D:D,A:A,A2,B:B,B2)/COUNTIFS(A:A,A2,B:B,B2),0)
向下複製到C3:C19儲存格。
E2:
=IF(B2=9,AVERAGEIFS(F:F,A:A,A2,B:B,B2)/COUNTIFS(A:A,A2,B:B,B2),0)
向下複製到E3:E19儲存格。