I'm trying to use Linq groupby and where with date conditions in vb.net
Please Guide Me.
PIQ is total purchase quantity
SIQ is total sales quantity
BLC is total purchase quantity minus total sales quantity
Thanks
Public Class Form3
Private Purchase, Sales As New List(Of Invoice)
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
LoadData()
End Sub
Private PurchaseDetails,SalesDetails As New List(Of Detail)
Private Function CreateConnection() As String
Return ("Provider=Microsoft.ACE.OLEDB.12.0;Data Source=|DataDirectory|\TRIAL2.accdb;Persist Security Info=False;")
End Function
Private Sub LoadData()
Using Connection = New OleDbConnection(CreateConnection())
Purchase = CType(Connection.Query(Of Invoice)("SELECT * FROM PURCHASE"), List(Of Invoice))
PurchaseDetails = CType(Connection.Query(Of Detail)("SELECT * FROM PURCHASEDETAILS"), List(Of Detail))
Sales = CType(Connection.Query(Of Invoice)("SELECT * FROM SALES"), List(Of Invoice))
SalesDetails = CType(Connection.Query(Of Detail)("SELECT * FROM SALESDETAILS"), List(Of Detail))
End Using
Dim ps = From p In Purchase
Where p.DATEINVO >= CDate("01/01/2024")
From pd In PurchaseDetails
Select
pd.ITEM,
PIQ = pd.QTY,
SIQ = 0,
BLC = pd.QTY
Order By ITEM
Dim ss = From s In Sales
Where s.DATEINVO >= CDate("01/01/2024")
From sd In SalesDetails
Select
sd.ITEM,
PIQ = 0,
SIQ = sd.QTY,
BLC = -sd.QTY
Order By ITEM
Dim Card_temp = ps.Union(ss).OrderBy(Function(w) w.ITEM)
Dim Card As New List(Of ItemCards3)
Dim RunningBalance As Integer = 0
For Each ct In Card_temp
Dim sc As New ItemCards3
With sc
.ITEM = ct.ITEM
.PIQ = ct.PIQ
.SIQ = ct.SIQ
.BLC = ct.PIQ - ct.SIQ
'.BLC = RunningBalance + ct.BLC
'RunningBalance = .BLC
End With
Card.Add(sc)
Next
DataGridView1.DataSource = Card
End Sub
End Class
Public Class Invoice
Property INVONO() As String
Property DATEINVO() As Date
Property CUSTCODE() As String
End Class
Public Class Detail
Public Property INVONO() As String
Public Property NOD() As Integer
Public Property ITEM() As String
Public Property QTY() As Integer
End Class
Public Class ItemCards3
Public Property ITEM() As String
Public Property PIQ As Integer
Public Property SIQ As Integer
Public Property BLC As Integer
End Class
Table PURCHASE
INVONO | DATEINVO | CUSTCODE |
---|---|---|
PI1000 | 25-Jan-23 | 001 |
PI1001 | 29-Jan-23 | 002 |
PI1002 | 29-Jan-24 | 003 |
PI0003 | 30-Jan-24 | 004 |
Table PURCHASEDETAILS
INVONO | NOD | ITEM | QTY |
---|---|---|---|
PI1000 | 1 | TEST 1000 | 10 |
PI1000 | 2 | TEST 2000 | 20 |
PI1001 | 1 | TEST 3000 | 15 |
PI1002 | 1 | TEST 1000 | 20 |
PI1003 | 1 | TEST 1000 | 15 |
Table SALES
INVONO | DATEINVO | CUSTCODE |
---|---|---|
SI1000 | 25-Jan-23 | 001 |
SI1001 | 29-Jan-23 | 002 |
SI1002 | 29-Jan-24 | 005 |
SI1003 | 30-Jan-24 | 006 |
SI1004 | 29-Jan-24 | 004 |
SI1005 | 30-Jan-24 | 003 |
Table SALESDETAILS
INVONO | NOD | ITEM | QTY |
---|---|---|---|
SI1000 | 1 | TEST 1000 | 10 |
SI1000 | 2 | TEST 2000 | 20 |
SI1001 | 1 | TEST 8000 | 35 |
SI1002 | 1 | TEST 1000 | 27 |
SI1003 | 1 | TEST 1000 | 15 |
SI1004 | 1 | TEST 2000 | 2 |
SI1005 | 1 | TEST 2000 | 2 |
the result of the code above
ITEM | PIQ | SIQ | BLC |
---|---|---|---|
TEST 1000 | 15 | 0 | 15 |
TEST 1000 | 10 | 0 | 25 |
TEST 1000 | 20 | 0 | 45 |
TEST 1000 | 0 | 10 | 35 |
TEST 1000 | 0 | 27 | 8 |
TEST 1000 | 0 | 15 | -7 |
TEST 2000 | 20 | 0 | 13 |
TEST 2000 | 0 | 20 | -7 |
TEST 2000 | 0 | 2 | -9 |
TEST 3000 | 15 | 0 | 6 |
TEST 8000 | 0 | 35 | -29 |
Desired result
ITEM | PIQ | SIQ | BLC |
---|---|---|---|
TEST 1000 | 35 | 42 | -7 |
TEST 2000 | 4 | -4 |
result from answer @TN
result after update answer form @TN
The first issue I see is that your Purchase/PurchaseDetails query is referencing both tables, but is not providing a join condition. This will result in a cross join where every Purchase row joins to every PurchaseDetails row. The same applied to the Sales/SalesDetails query.
I believe this can be corrected using the following Join ... On ...
LINQ syntax.
Dim ps = From p In Purchase
Where p.DATEINVO >= CDate("01/01/2024")
Join pd In PurchaseDetails On pd.INVONO Equals p.INVONO
Select
pd.ITEM,
PIQ = pd.QTY,
SIQ = 0,
BLC = pd.QTY
Order By ITEM
Dim ss = From s In Sales
Where s.DATEINVO >= CDate("01/01/2024")
Join sd In SalesDetails On sd.INVONO Equals s.INVONO
Select
sd.ITEM,
PIQ = 0,
SIQ = sd.QTY,
BLC = -sd.QTY
Order By ITEM
Once you have those two queries defined, you can combine, group, and calculate the resulting sums with a combination of .Concat()
, .GroupBy()
, .Select()
, and .Sum()
. Within the .Select()
, the group-by ITEM
value is accessed via the grouping .Key
property, while the other values are calculated using .Sum()
to iterate through the members of each grouping.
The .Concat()
function is used instead of .Union()
because we do not want to eliminate duplicates. The .Concat()
function unconditionally includes all rows.
The result would be something like:
Dim Card_temp = ps.Concat(ss) _
.GroupBy(Function(w) w.ITEM) _
.Select(Function(g) New ItemCards3 With {
.ITEM = g.Key,
.PIQ = g.Sum(Function(m) m.PIQ),
.SIQ = g.Sum(Function(m) m.SIQ),
.BLC = g.Sum(Function(m) m.BLC)
}) _
.OrderBy(Function(ic) ic.ITEM) _
.ToList()
or
Dim Card_temp = (From w In ps.Concat(ss)
Group By w.ITEM Into g
Select New ItemCards3 With {
.ITEM = g.ITEM,
.PIQ = g.Sum(Function(m) m.PIQ),
.SIQ = g.Sum(Function(m) m.SIQ),
.BLC = g.Sum(Function(m) m.BLC)
}
Order By ITEM
) _
.ToList()
The .OrderBy()
and .ToList()
are present to organize the results.
Note: I am a C# programmer, not a VB programmer, so I prepared the above code snippets in C# and translated to VB with the assistance of a translator tool. Not sure if I have the syntax exactly right - particularly with respect to required vs optional line continuations characters.