Linq - Group by multiple tables - c#

Using Linq to Sql how do i group the following 2 tables.
Orders Table:
CustomerID | Name |Date
1 | order1 | 2010-01-01
2 | order2 | 2010-01-01
2 | order3 | 2010-04-01
Calls Table:
CustomerID | Name |Date
1 | call1 | 2010-01-01
3 | call2 | 2010-06-01
2 | call3 | 2010-05-01
I want to group the two tables by date , Result:
Date | Orders | Calls
2010-01-01 | 2 | 1
2010-04-01 | 1 | 0
2010-05-01 | 0 | 1
2010-06-01 | 0 | 1
i know how to group a single table ,
from o in Orders
group o by o.Date.Date into og
select new {Date = og.Key,Orders= og.Count()};
how do i group both?
thx!

Since both tables seem to have a similar structure I'd recommend projecting both into an equivalent form and then group on the concatenation of those two sets.
var orders = from o in Orders
select new { IsOrder = true, o.Date };
var calls = from c in Calls
select new { IsOrder = false, c.Date };
var result = from x in orders.Concat(calls)
group x by x.Date into og
select new {Date = og.Key, Orders= og.Count(o=>o.IsOrder), Calls = og.Count(c=>!c.IsTrue)};
Due to the lazy nature of Linq2Sql this might actually be reduced to a single query. In the interest of performance I would make sure this is not a query from hell.

You can use the Union method:
var result =
(from c in Calls group c by c.Date into cg select new {Date = cg.Key, Calls = cg.Count(), Orders = 0})
.Union(from o in Orders group o by o.Date into og select new {Date = og.Key, Calls = 0, Orders = og.Count()})
.GroupBy(x => x.Date)
.Select(g => new {Date = g.Key, Calls = g.Max(r => r.Calls), Orders = g.Max(r => r.Orders)});
foreach (var row in result)
{
Trace.WriteLine(row);
}
This is very similar to the SQL you would write (a union of the two tables, and then an outer query to merge the results into a row)

Related

how to group by in LINQ and not select all columns

I am trying to write a LINQ query which is complex for me now since I am a newbie to LINQ.
I have a table like below...
UserId | CompanyId | ProblemDescription | CreatedTime | TimeSpentMins
-----------------------------------------------------------------------
1 | 95 | Sysmtem is crashed | 2016-01-01 15:23 | 25
1 | 95 | Total is incorrect | 2016-01-01 15:45 | 45
I want to write a LINQ query that will do the job below. CreatedTime has date and time but I want to group it by only date.
SELECT UserId, CompanyId,CreateTime Sum(TimeSpentMins)
FROM TransactionLogs
GROUP BY UserId, CompanyId, Convert(DATE,CreatedTime)
How can I write this LINQ? I wanted to put my code below but I got nothing :(
Simply use the GroupBy extension method and use EntityFunctions.TruncateTime method to get only the date part:-
var result = db.TransactionLogs
.GroupBy(x => new
{
CreateTime = EntityFunctions.TruncateTime(x.CreatedTime),
UserId,
CompanyId
})
.Select(x => new
{
UserId = x.Key.UserId,
CompanyId = x.Key.CompanyId,
CreateTime = x.Key.CreateTime,
TotalTimeSpentMins = x.Sum(z => z.TimeSpentMins)
});
try this one:
var result = db.TransactionLogs
.GroupBy(_ => new {
_.UserId, _.CompanyId, DbFunctions.TruncateTime(_.CreatedTime)})
.Select(_ => new {
_.UserId, _.CompanyId, DbFunctions.TruncateTime(_.CreatedTime),
Total = _.Sum(t => t.TimeSpentMins)});

Distinct Count x and Grouping by Date y

I'm trying to get from the following data in an SQL database
date | user |
(DateTime) | (string)|
--------------------------------------------------
2013-06-03 13:24:54.013 | 3 |
2013-06-04 13:25:54.013 | 5 |
2013-06-04 13:26:54.013 | 3 |
2013-06-04 13:27:54.013 | 3 |
a list in the form
date | DistinctCountUser
---------------------------------
2013-06-03 | 1
2013-06-04 | 2
I've tried several ways to do this with linq but always end up with a) not the result I expected or b) a linq exception.
var result = input.GroupBy(x=>x.date.Date,(key,x)=> new {
date = key,
DistinctCountUser = x.Select(e=>e.user).Distinct().Count()
});
If you are using Entity Framework, then you should use EntityFunctions.TruncateTime to get date part of date time field:
from x in context.TableName
group x by EntityFunctions.TruncateTime(x.date) into g
select new {
date = g.Key,
DistinctCountUser = g.Select(x => x.user).Distinct().Count()
}
Otherwise use #KingKong answer
Here is how to use query expression when grouping in Linq. Query Expressions may be easier to read in some cases and I find grouping to be one of them.
from thing in things
group thing by thing.date.Date into g
select new {
Date = g.Key,
DistinctCountUser = g.Select(x => x.user).Distinct().Count()
}

How do I get a value adjacent to a Max(value) using Linq?

I have a table:
Group | BasalArea | SpeciesName
1 | 3.6 | Palustris
1 | 45.0 | MSO
2 | 4.2 | Oak
2 | 2.0 | MSO
...
From this table, I would like to get the species name with the highest basal area grouped by the Group field, which would look like this:
Group | BasalArea | SpeciesName
1 | 45.0 | MSO
2 | 4.2 | Oak
Using SQL, I can get the highest basal area:
SELECT Group, Max(BasalArea)
FROM TABLE
GROUP BY Group
I can't figure out how to also get the species name without doing some looping. Is this possible? What are the strategies for handling ties?
This is simpler in LINQ2SQL than in SQL:
var res = source.MyTable
.GroupBy(item => item.Group)
.Select(g => g.OrderByDescending(item => item.BasalArea).First())
.ToList();
This will return the list of items with largest values of BasalArea in its Group, together with SpeciesName.
In SQL you would need to join back to the original table, like this:
SELECT * FROM TABLE b
JOIN (
SELECT Group, Max(BasalArea) as BasalArea
FROM TABLE
GROUP BY Group
) t on t.Group = b.Group AND t.BasalArea = b.BasalArea
Try this:
var froup = categories.GroupBy(g => new {g.CategoryType})
.Select(g => g.OrderByDescending(i => i.CategoryID).First())
.ToArray();
What sasblinkenlight said would be the LINQ. Out of curiosity, here is a potential SQL solution.
SELECT grouped.Group, raw.SpeciesName, grouped.MaBasalArea
FROM (
SELECT Group, MAX(BasalArea) as MaxBasalArea
FROM TABLE
GROUP BY Group
) grouped
INNER JOIN TABLE raw ON grouped.MaxBasalArea = raw.BasalArea AND grouped.Group = raw.Group

LINQ join and group

I'm new to LINQ, and I'm trying to convert this SQL query into its LINQ equivalent:
select S.*
from Singles S
join (
select max(SingleId) as SingleId
from Single
group by ArtistId) S2 on S2.SingleId = S.SingleId
order by Released desc
The table looks like this:
-----------
| Singles |
|-----------|
| SingleID |
| ArtistId |
| Released |
| Title |
| ..... |
-----------
and so on...
And contains for example these items:
SingleID ArtistID Released Title
1 1 2011-05-10 Title1
2 1 2011-05-10 Title2
3 2 2011-05-10 Title3
4 3 2011-05-10 Title4
5 4 2011-05-10 Title5
6 2 2011-05-10 Title6
7 3 2011-05-10 Title7
8 5 2011-05-10 Title8
9 6 2011-05-10 Title9
So I'm trying to get the latest singles, but only one per artist. Could anyone help me? :)
Maybe there's even a better way to write the query?
Update:
To answer the questions posted in the comments:
We're using Microsoft SQL Server, and LINQ to NHibernate.
Here's a sample that we're using right now, that returns the latest singles, without grouping by artistid:
public Single[] GetLatest()
{
IQueryable<Single> q;
q = from s in _sess.Query<Single>()
where s.State == State.Released
orderby s.Released descending
select s;
return q.Take(20).ToArray();
}
How about this:
var firstSingles = Singles.GroupBy(x => x.ArtistId)
.Select(g => g.OrderByDescending(x => x.Released).First())
.ToList();
Something like this should work.
var query = from s in db.Singles
group s by s.ArtistID into sg
let firstSingle = sg.OrderByDescending(r => r.SingleID).FirstOrDefault()
select new
{
ArtistID = sg.Key,
SingleID = firstSingle.SingleID,
Released = firstSingle.Released,
Title = firstSingle.Title,
}
singles
.OrderByDescending(s => s.SingleID)
.GroupBy(s => s.SingerID, (id, s) => new
{
SingleID = id,
Title = s.First().Title
});

Get distinct columns with order by date

I have these following two tables:
Job Title | PostDate | CompanyId
Assitant | 12/15/10 | 10
Manager | 12/1/10 | 11
Developer | 12/31/10 | 10
Assitant | 12/1/10 | 13
PM | 11/29/10 | 12
CompanyId | Name
10 | Google
11 | Yahoo
12 | Microsoft
13 | Oracle
Now i would like to get 3 different companies with the jobs sorted by post date. The result table would be following:
Job Title | PostDate | CompanyName
Developer | 12/31/10 | Google
Manager | 12/1/10 | Yahoo
Assitant | 12/1/10 | Oracle
How can I achieve that using a linq query? Any help will be appreciated...
I think that would be something like:
var query = from company in db.Companies
join job in db.Jobs on company.CompanyId equals job.CompanyId
group job by company into jobsByCompany
let lastJob = jobsByCompany.OrderByDescending(x => x.PostDate)
.First()
orderby lastJob.PostDate descending
select new
{
JobTitle = lastJob.JobTitle,
PostDate = lastJob.PostDate,
CompanyName = jobsByCompany.Key.Name
};
It's a little odd to do a join and then a group - we could do a GroupJoin instead, but then discard empty options:
var query = from company in db.Companies
join job in db.Jobs on company.CompanyId equals job.CompanyId
into jobsByCompany // Make this a group join
let lastJob = jobsByCompany.OrderByDescending(x => x.PostDate)
.FirstOrDefault()
where lastJob != null
orderby lastJob.PostDate descending
select new
{
JobTitle = lastJob.JobTitle,
PostDate = lastJob.PostDate,
CompanyName = company.Name
};
EDIT: Note that doesn't just take the top three results. Use query = query.Take(3); to just get the first three results.

Categories