iT邦幫忙

1

Excel資料處理問題協助

  • 分享至 

  • xImage

請教各位大神,我有一組excel資料如下,需要依據最好的排序個數平均分攤金額與利息,這部分是否有可能用excel VBA或其他方式達成?因為資料有將近三萬筆,逐一人工作業完眼睛也花了,先感謝了!
https://ithelp.ithome.com.tw/upload/images/20230605/201278808cB0CIrfsg.jpg

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.....

感謝各位大神的協助,非常感謝!!!

菩薩慈悲:懶得描述也就懶得看懂,幫不上忙。感恩感恩 南無阿彌陀佛
最好的排序個數 ?
平均分攤金額與利息?
菩薩慈悲:這樣就懂了,簡單。只是「18,812,754」5等分不是「3762550.8」?您卻是「3,762,551」,是什麼規則,無條件進位?四捨五入?感恩感恩 南無阿彌陀佛
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中
1
rogeryao
iT邦超人 8 級 ‧ 2023-06-06 23:50:43
最佳解答

Access 連結 Excel : 三萬筆資料用連結即可,匯入可能很耗時;下次使用時只要覆蓋 Excel

來源 Excel
https://ithelp.ithome.com.tw/upload/images/20230606/200850219tc4KLEzsF.jpg
讀取 Excel
https://ithelp.ithome.com.tw/upload/images/20230606/20085021RokTApGh7T.jpg
產出結果
https://ithelp.ithome.com.tw/upload/images/20230606/20085021u44WFwUjdJ.jpg
SQL 指令
https://ithelp.ithome.com.tw/upload/images/20230606/20085021HWuZtH5uxQ.jpg

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,有創意。但效能上,是否多了一層,隔簾望月?感恩感恩 南無阿彌陀佛

rogeryao iT邦超人 8 級 ‧ 2023-06-07 00:34:18 檢舉

問題不在於多一層,而是演算法;
如果 VBA 寫得很出色的話,就可能比 SQL 演算更快;
等版主用三萬筆跑一次就知了.

原來如此,確實,問題的關鍵所在,比皮相的直觀,要來的重要多了。感恩 大師指教,長見識了。感恩感恩讚歎讚歎南無阿彌陀佛
只是如果SQL可行的話,是否可以考慮直接在excel裡頭應用SQL來演算就好?就像在excel VBA裡頭應用ADODB一樣

rogeryao iT邦超人 8 級 ‧ 2023-06-07 07:24:50 檢舉

Excel VBA + SQL
來源 Excel :
https://ithelp.ithome.com.tw/upload/images/20230607/20085021GsqWHiphVY.jpg
重計後 Excel :
https://ithelp.ithome.com.tw/upload/images/20230607/20085021iXHYwYOlAl.jpg
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,末學曾見聞人反應在大數據時,系統資源會很吃緊,不知確否,還請查證。人無遠慮必有近憂致遠恐泥,是以君子不為耳。感恩感恩 南無阿彌陀佛

rogeryao iT邦超人 8 級 ‧ 2023-06-07 19:08:13 檢舉

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
0
孫守真任真甫
iT邦研究生 4 級 ‧ 2023-06-06 03:29:24

菩薩慈悲:做好收工。請 菩薩垂慈驗收。感恩感恩 南無阿彌陀佛

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
jeffeux iT邦新手 4 級 ‧ 2023-06-11 11:13:41 檢舉

您好,請問「¨ú±o³Ì¤p­È¡]±Æ§Ç³Ì¦nªº¡^」是不是字的編碼跑掉了呢?

jeffeux菩薩慈悲。是的這是我在複製的時候忘了切換回中文語系的緣故,因為VBE只支援Big5編碼。感謝你的指出,等一下我就改過來。但應該不影響程式的運作。只是請菩薩您留意,已經有最佳解答的大師菩薩測試出來在3萬筆資料的情況下,我這個程式需要2分多鐘,而他的最佳解答,最慢的話,卻只需要不到20秒的時間哦。請您斟酌參考。感恩感恩 南無阿彌陀佛

3
海綿寶寶
iT邦大神 1 級 ‧ 2023-06-06 07:54:53

假設
1.工作表中已設定總金額及總利息

1.Before
https://ithelp.ithome.com.tw/upload/images/20230606/20001787WKhs5biaR1.png
2.Execute VBA
https://ithelp.ithome.com.tw/upload/images/20230606/20001787YaV98cg0Pw.png
3.After
https://ithelp.ithome.com.tw/upload/images/20230606/20001787GDIEJdFgMN.png

還是 海綿寶寶大菩薩您厲害,乾淨利落不囉嗦。只是 您的巨集Main在哪裡呢?可 開源供末學們參考麼?末學偶比較有興趣的是在按下「編輯(E)」按鈕後的風情,不是按下「執行(R)」按鈕後的收割。^_^ 感恩感恩讚歎讚歎海綿寶寶大菩薩南無阿彌陀佛

shanminglo菩薩慈悲,希望 下次能貼上資料或附上樣本,免得還得像末學和 海綿寶寶大神一樣,自己鍵入資料來測試,很煩人的,人一麻煩,就更不想解答了,即使再駕輕就熟…… 好嗎 感恩感恩讚歎讚歎南無阿彌陀佛

1
ccenjor
iT邦高手 1 級 ‧ 2023-06-06 19:07:45

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儲存格。
https://ithelp.ithome.com.tw/upload/images/20230606/201098819RwL02qWej.jpg

菩薩慈悲:那如問者言有3萬筆,難不成要自己手動複製下去?應該也是可以向下填滿的吧?感恩感恩 南無阿彌陀佛

我要發表回答

立即登入回答