2017-03-10 120 views
1

我想在VB.NET或C#中編寫Linq子句的代碼,該代碼返回所有記錄,直到其中一個列值的總和達到某個值並分組爲止由另一列..獲取列/字段總和等於某個值的行

現在我有一個在MySQL條款:

SELECT 
    O.Id, 
    O.Supp, 
    O.TotalCol, 
    (SELECT 
    sum(TotalCol) FROM Table1 
    WHERE Id <= O.Id) 'bTotal' 
FROM Table1 O 
HAVING bTotal <= 50000 

代碼我已經做了:

對象類:

Class Invoices 
     Public id As Integer 
     Public supplier As String 
     Public total As Decimal 
     Public Sub New(ByVal id As Integer, ByVal supplier As String, ByVal total As Decimal) 
      Me.id = id 
      Me.supplier = supplier 
      Me.total = total 
     End Sub 
End Class 

的未分組列表:

Dim LInvoicesPrincipal As New List(Of Invoices)() 

Dim InvoiceItem As Invoices 

InvoiceItem = New Invoices(1, "SUPP 1", 45000) 
LInvoicesPrincipal.Add(InvoiceItem) 
InvoiceItem = New Invoices(2, "SUPP 1", 6000) 
LInvoicesPrincipal.Add(InvoiceItem) 
InvoiceItem = New Invoices(3, "SUPP 2", 15000) 
LInvoicesPrincipal.Add(InvoiceItem) 
InvoiceItem = New Invoices(4, "SUPP 2", 6000) 
LInvoicesPrincipal.Add(InvoiceItem) 
InvoiceItem = New Invoices(5, "SUPP 1", 4000) 
LInvoicesPrincipal.Add(InvoiceItem) 

在LINQ分組,由提供商(我需要限制的總數的總和到一定值的規則(50000要準確)):

Dim LGroups 

LGroups = _ 
    From oInvoices In LInvoicesPrincipal _ 
    Group oInvoices By oInvoices.supplier _ 
    Into Group Select Group 

而且一個For Each,讓我來驗證分組:

For Each EachList As Invoices() In LGroups 

    For Each EachItem As Invoices In EachList 

    Next 

Next 

這一切現在返回我列出這樣的:

Group 1: 
Invoices(1, "SUPP 1", 45000) 
Invoices(2, "SUPP 1", 6000) 
Invoices(5, "SUPP 1", 4000) 

Group 2: 
Invoices(3, "SUPP 2", 15000) 
Invoices(4, "SUPP 2", 6000) 

但是......我想是這樣的:

Group 1: 
Invoices(1, "SUPP 1", 45000) 
Invoices(5, "SUPP 1", 4000) 

Group 2: 
Invoices(2, "SUPP 1", 6000) 

Group 3: 
Invoices(3, "SUPP 2", 15000) 
Invoices(4, "SUPP 2", 6000) 

(值的總和必須< = 50000每組)

+0

請提供您已經完成的一些安排和代碼。 – kat1330

+0

你最後一個(刪除)問題,它更多的是一個組合的東西或一個最佳適合算法。我不確定你可以在SQL – Plutonix

+0

中做任何事情也許這是兩者的結合......我已經在腦海中循環着同樣的想法,我需要一個喘息的機會...... – Glegan

回答

1

有一個Bin Packing algorithm其工作限制打包給定項目集所需的箱的數量。您似乎最關心的是將供應商的合計總額限制爲< = 50000,而不是創建多少個發票組。似乎只是順便提及供應商的

  • 這應該做你想做的,但它不是一個真正的最佳適合的方法。
  • 我用更多的數據,以獲得更好的畫面:5級的供應商和30張發票
  • 供應商和發票,隨機生成,所以每個供應商沒有6張發票
  • 1供應商發票組是不是隨機的,所以我能測試數據集特別

你已經有一個Invoice類,但我的略有不同:

Public Class Invoice 
    Public Property Id As Int32 
    Public Property Supplier As String 
    Public Property Total As Decimal 

    Public Property GroupId As Int32 
    ... 

我強烈建議您使用實際的Properties而不是公共字段。這使您可以在DataGridView中顯示結果以進行調試。新GroupId屬性允許代碼來識別發票項目已經被添加到InvoiceGroup

Public Class InvoiceGrp 
    Public Property GroupId As Int32 
    Public Property Supplier As String 
    Public ReadOnly Property Total As Decimal 
     Get 
      Return Invoices.Sum(Function(k) k.Total) 
     End Get 
    End Property 

    Public Property Invoices As New List(Of Invoice) 
    ... 

還有一個ToString()覆蓋和Count性能進行調試。首先,將「原始」發票列表需要由供應商進行分組之前它們中的每可處理:隨機選擇從

' group the invoices by supplier 
Dim grpData = Invoices.GroupBy(Function(g) g.Supplier).ToArray() 

' collection to store the results 
Dim groupedInvoices = New List(Of InvoiceGrp) 

Dim groupId = 1 
' create invoice groups one supplier group at a time 
For Each item In grpData 
    Dim gi = GroupInvoices(item, 50000, groupId) ' add ToArray() for debug 
    groupedInvoices.AddRange(gi) 

    ' the GroupId gets incremented for each supplier 
    ' group, use the last one for the start value in the next 
    ' supplier group 
    groupId = gi.Max(Of Int32)(Function(k) k.GroupId) 
Next 

發票總計:
{4000, 2500, 5000, 10000, 15000, 45000, 6000, 25000}

GroupInvoice方法和輔助:

Private Iterator Function GroupInvoices(grp As IEnumerable(Of Invoice), 
             Limit As Decimal, 
             grpId As Int32) As IEnumerable(Of InvoiceGrp) 

    ' make a copy so we can remove those grouped 
    Dim myGrp = New List(Of Invoice)(grp. 
             Where(Function(k) k.GroupId = -1). 
             OrderByDescending(Function(k) k.Total). 
             ToArray()) 

    While (myGrp.Count > 0) 
     grpId += 1 
     ' NewInvoiceGroup does the actual Group creation 
     Dim newGrp = NextInvoiceGroup(myGrp, Limit, grpId) 
     ' remove grouped items from the ToDo list 
     myGrp.RemoveAll(Function(r) newGrp.Invoices.Contains(r)) 

     Yield newGrp 
    End While 
End Function 

Private Function NextInvoiceGroup(items As List(Of Invoice), 
           Limit As Decimal, 
           nextGrp As Int32) As InvoiceGrp 

    ' this creates one InvGrp with as many Invoices 
    ' as will fit. 
    Dim InvG = New InvoiceGrp With {.Supplier = items(0).Supplier, .GroupId = nextGrp} 

    For Each inv In items 
     If InvG.Total + inv.Total <= Limit Then 
      ' tag Invoice with the InvoiceGrp Id 
      inv.GroupId = nextGrp 
      InvG.Invoices.Add(inv) 
     End If 
    Next 

    Return InvG 
End Function 

重要事項

  • 從與N數發票傳遞到GroupInvoices LINQ的結果各組。
  • 它把它們按金額排序(總數),然後調用NextInvoiceGroup實際創建的總計不超過50k。
  • 這是一個Iterator它允許它在創建時返回每個組。
  • 創建的每個組中的發票都將從列表中刪除,以便在不存在更多信息時完成。同樣,每個發票都有一個GroupId,因此代碼知道哪些已被添加到組中。
  • NextInvoiceGroup將所有發票重複添加到新的InvoiceGrp,直到它達到50k或用完項目。
  • 沒有包含最佳Fit/Bin包裝邏輯(並沒有在帖子中提到),所以供應商的第一批將會更「飽滿」(接近50k),最後一批將會是剩餘物。
  • GroupId應該是所有組
  • 在最後一個獨特的價值,你不僅有InvoiceGrps,但每張發票也有它屬於哪個組的指示。
  • 如果Iterator方法混淆了你,它可以進行重構,以創建並返回一個List(Of InvoiceGroup)
  • 如果要打印的內容到控制檯窗口中,你有時可以看到,如果你交換一些物品從一個組到另一個時,剩餘的可以適合其他一些集團。這自然會是依賴於數據:

調試轉儲:

For Each grp In groupedInvoices 
    Console.WriteLine("{0} Grp# {1}: ct: {2} tot: {3}", grp.Supplier, grp.GroupId, 
         grp.Invoices.Count, grp.Total) 
    For Each inv In grp.Invoices 
     Console.WriteLine("Supp: {0} Inv#: {1} Tot: {2}", 
          inv.Supplier, inv.Id, inv.Total) 
    Next 
    Console.WriteLine() 
Next 

結果(不包括非隨機供應商組)

阿爾法GRP#1:克拉:4 TOT:50000
增刊:阿爾法INV#:2托特:25000
增刊:阿爾法INV#:14托特:15000
增刊:阿爾法INV#:8托特:5000
增刊:阿爾法INV#:16托特:5000

阿爾法GRP#2:克拉:3 TOT:13000
增刊:阿爾法INV#:22托特:5000
增刊:阿爾法INV#:1托特: 4000
增刊:阿爾法INV#:6托特:4000

德爾塔GRP#3:克拉:2 TOT:50000
增刊:德爾塔INV#:4托特:45000
增刊:德爾塔INV#:3托特:5000

Delta Grp#4:ct:5 tot:22500
增刊:德爾塔INV#:5托特:6000
增刊:德爾塔INV#:21托特:6000
增刊:德爾塔INV#:11托特:4000
增刊:德爾塔INV#:12托特:4000
增刊:德爾塔INV#:9托特:2500

回聲GRP#5:克拉:2 TOT:50000
增刊:回聲INV#:23托特:45000
增刊:回聲INV#:24托特:5000

Echo Grp#6:ct:2 tot:50000
增刊:回聲INV#:7托特:25000
增刊:回聲INV#:18托特:25000

回聲GRP#7:克拉:3 TOT:50000
增刊:回聲INV#:20托特:25000
增刊:回聲INV#:19托特:15000
增刊:回聲INV#:25托特:10000

狐步舞GRP#8:克拉:2 TOT:50000
增刊:狐步舞INV#:15托特:45000
提供:FoxTrot Inv#:13 Tot:5000

狐步舞GRP#9:克拉:1 TOT:45000
增刊:狐步舞INV#:17托特:45000

狐步舞GRP#10:CT:1個TOT:6000
增刊:狐步舞INV#:10托特: 6000

+0

優秀的解釋和解決方案!非常感謝你,它非常完美。 – Glegan

相關問題