linq - dynamic group by different functions of date - c#

below my simple sql query
SELECT source, date ,COUNT (*)
FROM query
WHERE source in ('a value')
GROUP BY source, date
it took me a while to understand and implement it in linq notation (also thanks to hints from this site) by finally I did it
var selekt = dt.AsEnumerable();
var alldata = from ad in selekt
where ad["source"].ToString().Equals(this.comboBox1.Text)
group ad by new
{
err = ad["err"],
date = ad["date"]
}into g
orderby g.Key.err ascending, g.Key.channel descending
select new
{
Name = g.Key.err,
Date = g.Key.date,
C = g.Count()
};
It is more or less what I need, it also contains "dynamic" element in where clause.
The last point I can't get it is dynamic group clause.
Depends on values on the form I want to group this query by one of three functions of date: day/week/month. I suppose the most difficult part could be group by week.
Anyway, every hint regarding this topis are welcome

Related

C# linq query data table take method

I'm not able to call take method from below linq query
var data = from tb in table.AsEnumerable()
join ch in channelTable.AsEnumerable() on syskey equals (DateTime)ch["syskey"]
orderby Convert.ToDouble(tb[(string.Format("Channel{0}_data",Convert.ToInt32(ch["Channel_No"])))]) descending
select new
{
Channel_No = ch["Channel_No"],
Channel_data = tb[string.Format("Channel{0}_data", Convert.ToInt32(ch["Channel_No"]))],
};
How can I add Take(5) from above linq query?
As simply as:
var top5 = data.Take(5);
You can do it in the same statement, too, but it's a bit ugly:
var data = (from tb in table.AsEnumerable()
join ch in channelTable.AsEnumerable() on syskey equals (DateTime)ch["syskey"]
orderby Convert.ToDouble(tb[(string.Format("Channel{0}_data",Convert.ToInt32(ch["Channel_No"])))]) descending
select new
{
Channel_No = ch["Channel_No"],
Channel_data = tb[string.Format("Channel{0}_data", Convert.ToInt32(ch["Channel_No"]))],
}).Take(5);
Note that ordering by a string here probably doesn't do what you want, unless your values are all single digit - would you really want channel3, channel2, channel10, channel1?
It's a pretty odd query though - especially as your join doesn't use anything from the first table...

Most efficient way to get MAX value of joined table in LINQ query using C#

I am trying to find the most efficient way to get the most recent record in a joined table in LINQ.
This query may handle thousands of records so I didn't want to perform a subquery.
I need everything from items, but only the most recent date from the "Notes" table, whose field name is SubmittedDate.
var items = (from t1 in db.Items
from t2
in db.Notes
.Where(o => (t1.Item_ID == o.Item_ID))
select new ItemModel
{
Name = t1.Name,
MostRecentUpdate = t2.SubmittedDate <== Need max value in model
});
Any help would be greatly appreciated.
It looks like you probably just want a group join:
var items = from t1 in db.Items
join t2 in db.Notes on t1.Item_ID equals t2.Item_ID into notes
select new ItemModel
{
Name = t1.Name,
MostRecentUpdate = notes.Max(x => (DateTime?) x.SubmittedDate)
};
Now MostRecentUpdate should be null if there are no non-null dates in the matching Notes rows. At least, that's what the LINQ to Objects behaviour would be, so fingers crossed the abstraction holds...

Problem with grid when trying to use groupby in LINQ

When trying to use GROUPBY I get an error saying a field 'date1' is not found on selected resource.
var query = (from a in db.Dates
from b in db.Facts
where a.Count_Key == b.Date_key
select new{
a.Date1,
a.Month,
b.Fact_key
});
var query2 = query.GroupBy(x => x.Month );
Grid1.DataSource = query2;
Grid1.DataBind();
So, when I try to bind with query it works perfectly, but query2 yields the error
field date1 not found on selected datasource.
How can I fix this?
The group by returns columns with different names than the original select.
You can do the following to see it "for educational purpose".
create a new Grid2 and use it with automatic columnmapping. Bind Query2 to this Grid2 and you will see the result.
Because you grouped by month, now you have only month field as a key, and collection of grouped items. If you want more fields on data source you need to use aggregate function on date1 field.
For example like this:
var query2 = (from q in query
group q by q.Month into g
select new
{
Month = g.Key,
Date = g.Select(gg=>gg.Date1).Max //or you can use first here etc.
}).ToList()
hope this helps

Quick linq count question

I have the following query that returns the login count per day from a given date.
var sot = from uts in DataContext.UserTrackingStatistics
let startDate = new DateTime(2009, 10, 01)
where uts.LastLogin >= startDate
group uts by uts.LastLogin.Date into myGroup
orderby myGroup.Key.Date
select new { Count = myGroup.Count() , myGroup.Key.Date};
I would like this to say the count was 0 for a given day rather than not return anything. How could I do that within this query?
You can't do it just with LINQ-to-SQL, as you'd have to use a union on your query with data that doesn't actually exist, which LINQ-to-SQL can't do.
To do this, you'll need to fill in the gaps client-side. I'm not in front of VS at the moment, but a general approach would be this:
Define your date range (since you mention no end date in your code and we're talking about login date, I'm assuming that the end date would be the current date.
Use Enumerable.Range to create a list of numbers ranging from 0 to the number of days within your date range, then use Select to transform that list into a list of dates. Select your results using an anonymous type and use the same properties as your L2S statement; this way, the compiler will reuse the same type
Combine your lists together using an outer join (not the most obvious syntax in LINQ, unfortunately) on the Date property
Order your results by date
This will now show 0 for the gaps.
I'll try to post a code sample below, but note that I can't compile where I am, so it may require tweaking.
var allDates = Enumerable.Range(0, (DateTime.Today - startDate).TotalDays)
.Select(i => new { Count = 0, Date = startDate.AddDays(i) });
var fullResults = from d in allDates
join r in results on d.Date == r.Date
from oj in r.DefaultIfEmpty()
select new { Count = oj == null ? 0 : oj.Count, Date = d.Date };

WIll LINQ cache a value if a select for it presents twice?

I have a LINQ query to a DataTable:
var list = from row in table.AsEnumerable()
group row by row.Field<byte>("ID") into g
select new
{
ID = g.Key,
Name = (from c in g
select c.Field<string>("name")).First(),
Localized = (from c in g
select myDic[c.Field<string>("name"))].First();
};
where ID is a primary column, Name - data from query and Localized - a value from a dictionary where key - that data from query (Name).
Will LINQ cache that data from query for the second select or I have to do this in another way?
And another question: if I'll put dictionary creation in select, will it been crated every time?
LINQ does not do any kind of query analysis/optimizations (see note below). Instead use the C# "let" clause (which maps to the SelectMany method) to capture that value once for reuse in the select statement. Here is your revised query:
var list = from row in table.AsEnumerable()
group row by row.Field<byte>("ID") into g
let name = (from c in g
select c.Field<string>("name")).First()
select new
{
ID = g.Key,
Name = name,
Localized = myDic[name]
};
NOTE: IQueryable providers can technically translate LINQ expressions into more optimal ones if they wanted to, but that's a subject of another discussion and you're using pure "LINQ to Objects" here anyway.
LINQ does not cache data. Therefore, it will perform the dictionary lookup for every element of every group.

Categories