Linq: Group by Time Interval - vb.net

I've the following Datatable:
[Date];[Time];[ProcessID];[ID]
24.09.2012;08:18:00;4000;1
24.09.2012;08:18:00;4000;2
24.09.2012;08:19:00;4001;3
24.09.2012;08:23:00;4002;4
24.09.2012;08:32:00;4003;5
...
As result I would like to have a grouping by a 15-minute Time-Interval to count the ProcessID´s:
[Interval];[ProcessID Count]
8:15:00 - 8:29:00;3
8:30:00 - 8:45:00;1
How to achieve this goal?

Here's the solution against Linq-to-SQL (on SQL Server) with the DateTime being in a single field (a LINQpad query):
Const BaseDate As Date = #9/24/2012#
Const PeriodMinutes As Integer = 15
Dim p = From t In TestData _
Group By q=CInt(Math.Floor((t.SomeDate - BaseDate).TotalMinutes/PeriodMinutes)) Into Group _
Select s=BaseDate.AddMinutes(q*PeriodMinutes), _
e=BaseDate.AddMinutes((q+1)*PeriodMinutes), _
ids=Aggregate g In Group Select g.ProcessID Distinct Into Count()
p.Dump
Using different types and other "little" adjustments could produce simpler SQL backend code.
For your actual database, you need to adjust the Group By, although it now depends upon what your Time column is converted to through Linq-to-SQL and/or your database's driver.
As it is a TimeSpan the Group By becomes something like:
Group By d=t.[Date], q=CInt(Math.Floor(t.[Time].TotalMinutes/PeriodMinutes)) Into Group _
Select s=d.AddMinutes(q*PeriodMinutes), _
e=d.AddMinutes((q+1)*PeriodMinutes), _
ids=Aggregate g In Group Select g.ProcessID Distinct Into Count()

This should work (however, not tested):
Dim groupedByQuarter =
From r In table
Let day = r.Field(Of Date)("Date").Date
Let time = r.Field(Of TimeSpan)("Time")
Let quarter = time.Add(TimeSpan.FromMinutes((-1) * time.Minutes Mod 15))
Group r By Key = New With {Key .day = day, Key .quarter = quarter} Into TimeGroup = Group
Select New With {
.Interval = String.Format("{0} - {1}",
Key.quarter,
Key.quarter.Add(TimeSpan.FromMinutes(14))),
.ProcessCount = TimeGroup.Select(Function(r) r.Field(Of Int32)("ProcessID")).Distinct().Count()
}

Related

LİNQ SQL Creating a New List Using Join VB.NET

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

Is there any way to avoid putting the datatype in LINQ query

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"))
}

How can I convert this SQL Script to LINQ? And keep the Count()?

I have made and tested my a MS SQL script that works perfectly. I've been slowly transitioning to LINQ and I am having trouble converting my script into VB.Net and LINQ.
SELECT DATEPART(dd,Generated) AS Day, count(ID) as Total
FROM Alarm
WHERE Generated >=CONVERT(VARCHAR(10),DATEADD(MONTH, DATEDIFF(MONTH,0,GETDATE()),0),120)
GROUP BY DATEPART(dd,Generated)
Basically, I'm asking the table to give me the total number of rows for the month grouped per day. It would be awesome to have it place a 0 for the future days of the month. Any advice on how to convert count into LINQ would be great.
My code:
Dim ChartData = (From a In db.Alarms _
Where a.Generated >= FirstDayOfMonth(DateTimeNow) And a.Generated <= LastDayOfMonth(DateTimeNow) _
Group By Day = a.Generated Into Grp = Group, Count() _
Select Grp)
'Get the first day of the month
Public Function FirstDayOfMonth(ByVal sourceDate As DateTime) As DateTime
Return New DateTime(sourceDate.Year, sourceDate.Month, 1)
End Function
'Get the last day of the month
Public Function LastDayOfMonth(ByVal sourceDate As DateTime) As DateTime
Dim lastDay As DateTime = New DateTime(sourceDate.Year, sourceDate.Month, 1)
Return lastDay.AddMonths(1).AddDays(-1)
End Function
Got it working, here is the code.
Dim ChartData = (From a In db.Alarms _
Where a.Generated >= FirstDayOfMonth(DateTimeNow) And a.Generated <= LastDayOfMonth(DateTimeNow) _
Group a By CalendarDate = a.Generated.Value.Date Into g = Group _
Select New With {CalendarDate, .AlarmCount = g.Count()})
You can do something like this. (I think dd means day...`)
var myDate = new DateTime(); //calculate your date...
Alarms
.Where(x => x.Generated >= myDate)
.GroupBy(x => x.Generated.Day)
.Select (x => new {
Day = x.Key,
Total = x.Count()
}
You might not be able to group by x.Generated.Day in sql, if so, then add .AsEnumerable() after the Where() to do the grouping in memory.

how to perform groupby in linq on DataTable inside vb code?

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"))
})

How can I preserve Null (DBNull) in Sum in LINQ queries

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