How do I perform group in LINQ inside vb code (dot.net v4.0) with DataTable and sum on the group?
In the sample below I need to add group by GroupName, ProductName and perform sum on QTY. The columns, order and where should remain as in sample, I just need to add the group and sum. The format should remain the same (getting row using e("FieldName")).
Dim ordersTable As DataTable = _dsProd.Tables("tblProductSummary")
Dim query =
(From e In ordersTable
Where (e("Type").ToString() = "1" Or IsDBNull(e("Type")))
Order By e("GroupSortOrder") Ascending, e("ProductName")
Select
GroupName = e("GroupName"),
ProductName = e("ProductName"),
QTY = e("QTY"),
Type= e("Type")
)
Dim query =
(From e In ordersTable
Where (e("Type").ToString() = "1" Or IsDBNull(e("Type")))
Order By e("GroupSortOrder") Ascending, e("ProductName")
Group e By Key = New With {
.ProductName = e("ProductName"),
.GroupName = e("GroupName")
} Into Group
Select New With {
.ProductName = Key.ProductName,
.GroupName = Key.GroupName,
.Sum = Group.Sum(Function(x) x("QTY"))
})
Related
I've got 2 datatables and am trying to summarize the data in them using a left outer join. The join works fine with this code
Dim Journal = From entries In dt.AsEnumerable()
Join inccodes In dtGL.AsEnumerable()
On entries.Field(Of String)("GLCode") Equals inccodes.Field(Of String)("GLCode")
Group By keys = New With {Key .IncomeCode = entries.Field(Of String)("GLCode"), Key .IncomeDesc = .inccodes.Field(Of String)("GLCodeDesc")}
Into ChargeSum = Group, sm = Sum(entries.Field(Of Decimal)("Amount"))
Where sm <> 0
Select New GL_Journal With {.IncomeCode = keys.IncomeCode, .IncomeDesc = keys.IncomeDesc, .LineAmount = sm}
`
However, since I really want a Left Outer Join I want to use Group Join instead of Join.
As soon as I change the Join to Group Join the code in the Group by at ".inccodes.field(Of String)("GLCodeDesc")" has ".inccodes" highlighted with the error "'inccodes' is not a member of 'anonymous type'"
I've reviewed much documentation on Group By and Group Join but there is scant information on them together.
Any ideas? Would I have more options/success with the method syntax?
If i try to reproduce your query using a left outer join, I will do something like this :
Dim dt As New DataTable
dt.Columns.Add("GLCode", GetType(String))
dt.Columns.Add("Amount", GetType(Decimal))
dt.Rows.Add("111", 3251.21)
dt.Rows.Add("222", 125.79)
dt.Rows.Add("999", 10000)
Dim dtGL As New DataTable
dtGL.Columns.Add("GLCode", GetType(String))
dtGL.Columns.Add("GLCodeDesc", GetType(String))
dtGL.Rows.Add("111", "a")
dtGL.Rows.Add("222", "b")
dtGL.Rows.Add("333", "c")
Dim Journal = From entries In dt.AsEnumerable()
Group Join inccodes In dtGL.AsEnumerable()
On entries.Field(Of String)("GLCode") Equals inccodes.Field(Of String)("GLCode")
Into Group
From lj In Group.DefaultIfEmpty()
Group By keys = New With {Key .IncomeCode = entries.Field(Of String)("GLCode"), Key .IncomeDesc = lj?.Field(Of String)("GLCodeDesc")}
Into ChargeSum = Group, sm = Sum(entries.Field(Of Decimal)("Amount"))
Select New With {.IncomeCode = keys.IncomeCode, .IncomeDesc = keys.IncomeDesc, .LineAmount = sm}
I'd like to get item_order and item_id from ITEM_DEF and from ITEM_SYT p4 colums. Also item_ids will be the same. And then sort it in a list order by Ascending.
So the new List will include 3 colums as item_id, item_order and p4.
EDIT: I just need to see how I should declare the List.
Here I have an example which performs well:
Dim ItemSytList As List(Of VLibrary.LINQ.ITEM_SYT) = New List(Of VLibrary.LINQ.ITEM_SYT)
ItemSytList = (From itemSyt As VLibrary.LINQ.ITEM_SYT In Context.ITEM_SYTs _
Join itemDef As VLibrary.LINQ.ITEM_DEF In Context.ITEM_DEFs On itemSyt.item_id Equals itemDef.item_id _
Where itemSyt.p1.ToUpper = "BDDK".ToUpper And itemSyt.p2.ToUpper = TableName.ToUpper _
And itemDef.template_id = TemplateId And If(itemDef.item_close_date.HasValue, itemDef.item_close_date, StartDate) >= StartDate _
And If(itemDef.item_open_date.HasValue, itemDef.item_open_date, EndDate) <= EndDate Select itemSyt).ToList
This code provides to keep ITEM_SYT's colums as it's children.
What you need to do is use an anonymous type. (Use implicit typing with LINQ whenever possible)
Dim ItemSytList =
From itemSyt As VLibrary.LINQ.ITEM_SYT In context.ITEM_SYTs
Join itemDef As VLibrary.LINQ.ITEM_DEF In context.ITEM_DEFs On itemSyt.item_id Equals itemDef.item_id
Where itemSyt.p1.ToUpper = "BDDK".ToUpper And itemSyt.p2.ToUpper = TableName.ToUpper _
And itemDef.template_id = TemplateId And If(itemDef.item_close_date.HasValue, itemDef.item_close_date, StartDate) >= StartDate _
And If(itemDef.item_open_date.HasValue, itemDef.item_open_date, EndDate) <= EndDate
Select New With {
.item_order = itemDef.item_order,
.item_id = itemDef.item_id,
.p4 = itemSyt.p4}
You can access the results like this
Dim firstP4 = ItemSytList.OrderBy(Function(i) i.item_order).First().p4
I have three dropdowns; the second one (Members) populates based on what's in the first (Units), and the third one (Customers) SHOULD populate based on what's in the second; but it doesn't.
Here's the query that works in LINQPad (returns a list of Company names):
select distinct companyname from customers C left join members M on M.MemberNo = C.MemberNo where M.MemberNo = '052' order by companyname
...but does not work in the following code:
'Populate the Members dropdown
Dim selectedUnit As String = DropDownListUnits.Text
Dim membersDT As DataTable
sql = "select distinct shortname, M.memberno from members M left join memberunitproducts mup on M.MemberNo = mup.MemberNo where unit = '" + selectedUnit + "' order by shortname"
retDS = sqlDAL.runSQLDataSet(sql)
membersDT = retDS.Tables(0)
DropDownListMembers.DataSource = membersDT
DropDownListMembers.DataTextField = "shortname"
DropDownListMembers.DataValueField = "memberno"
DropDownListMembers.DataBind()
'Populate the Customers dropdown
Dim selectedMember As String = DropDownListMembers.DataValueField
Dim customersDT As DataTable
sql = "select distinct companyname from customers C left join members M on M.MemberNo = C.MemberNo where M.MemberNo = '" + selectedMember + "' order by companyname"
retDS = sqlDAL.runSQLDataSet(sql)
customersDT = retDS.Tables(0)
DropDownListCustomers.DataSource = customersDT
DropDownListCustomers.DataTextField = "companyname"
DropDownListCustomers.DataValueField = "companyname"
DropDownListCustomers.DataBind()
The third (Customers) dropdown remains unpopulated when this runs - why?
It's probably not needed, but here is the code for the first ("Units") dropdown:
'Populate the Units dropdown
Dim unitsDT As DataTable
sql = "Select distinct unit from masterunits where abs(active) = 1"
retDS = sqlDAL.runSQLDataSet(sql)
unitsDT = retDS.Tables(0)
DropDownListUnits.DataSource = unitsDT
DropDownListUnits.DataTextField = "unit"
DropDownListUnits.DataValueField = "unit"
DropDownListUnits.DataBind()
What am I missing or doing wrong?
I had to change this:
Dim selectedMember As String = DropDownListMembers.DataValueField
...to this:
Dim selectedMember As String = DropDownListMembers.SelectedItem.Value
i have a linq query like this i dont want to put the Datatype as in some case i don't know what the datatype returned from database
Dim orders As DataTable = ds3.Tables(0)
Dim query = From row In orders
Group row By S_type = row.Field(Of Decimal)("S_type") Into MonthGroup = Group
Select New With {
Key S_type,
.TotalLocal = MonthGroup.Sum(Function(r) r.Field(Of Double)("TotalLocal"))
}
I am trying to perform this query on two DataTables in a DataSet
SELECT Totals.accCategory, Totals.ID, Totals.Account, Sum(Totals.Jan) AS Jan FROM (SELECT * FROM Allocated UNION SELECT * FROM Spent) AS Totals GROUP BY Totals.accCategory, Totals.ID, Totals.Account
As they are generated in code (in memory) into the DataSet I need to use LINQ thus:
Dim t = (From totals In (allocated.AsEnumerable.Union(spent.AsEnumerable)) _
Group totals By accCategory = totals.Item("accCategory"), ID = totals.Item("ID"), Account = totals.Item("Account") _
Into g = Group _
Select New With {Key .accCategory = accCategory, Key .ID = ID, Key .Account = Account, Key .Jan = g.Sum(Function(totals) Totals.Item("Jan"))}).ToList
Which fails as there are some instances where there are no records to sum. The Access query returns an empty cell - which is what I want. I can make the LINQ statement work by using If(IsDbNull(totals.Item("Jan")),0,totals.Item("Jan")) but then I get 0.00 if the total is zero (which is correct) but also if there are no items to sum (which I don't want)
I have tried Select New With {Key .accCategory = accCategory, Key .ID = ID, Key .Account = Account, Key .Jan = g.Sum(Function(totals) DirectCast(totals.Item("Jan"), Nullable(Of Decimal)))}).ToList which doesn't work either.
How can I make .Jan a Nullable(Of Decimal) and accept DBNull as a value??
Thanks
Andy
Got it!
Dim t = (From totals In (allocated.AsEnumerable.Union(spent.AsEnumerable)) _
Group totals By accCategory = totals.Item("accCategory"), ID = totals.Item("ID"), Account = totals.Item("Account") _
Into g = Group _
Select New With {Key .accCategory = accCategory, Key .ID = ID, Key .Account = Account, Key .Jan = If(g.AsQueryable.Any(Function(totals) totals.Field(Of Nullable(Of Decimal))("Jan").HasValue), g.AsQueryable.Sum(Function(totals) totals.Field(Of Nullable(Of Decimal))("Jan")), Nothing)}).ToList