Entity Framework Group By with Max Date and count - c#

I have the following SQL
SELECT Tag , COUNT(*) , MAX(CreatedDate)
FROM dbo.tblTags
GROUP BY Tag
Which outputs the following:
+-----------------+------------------+-------------------------+
| Tag | (No column name) | (No column name) |
+-----------------+------------------+-------------------------+
| a great tag | 1 | 2015-04-01 18:30:31.623 |
| not a test | 1 | 2015-04-01 17:46:09.360 |
| test | 5 | 2015-04-01 18:13:17.920 |
| test2 | 1 | 2013-03-07 16:53:54.217 |
+-----------------+------------------+-------------------------+
I'm trying to replicate the output of that query using EntityFramework.
I have the following logic which works:
var GroupedTags = Tags.GroupBy(c => c.Tag)
.Select(g => new
{
name = g.Key,
count = g.Count(),
date = g.OrderByDescending(gt => gt.CreatedDate).FirstOrDefault().CreatedDate
})
.OrderBy(c => c.name);
But takes horribly long to execute compared to the raw SQL query. Any suggestions on how to optimise my approach? It somehow feels wrong.

If you want a max, use the Max() Linq method:
var GroupedTags = Tags.GroupBy(c => c.Tag)
.Select(g => new
{
name = g.Key,
count = g.Count(),
date = g.Max(x => x.CreatedDate)
})
.OrderBy(c => c.name);

Related

How to convert this SQL query to LINQ or Lambda expression in C#?

I have the following simple table:
table : Inventory
+-------+-----------+-----------+
| Id | ProductId | cost |
+-------+-----------+-----------+
| 1 | 1 | 10 |
| 2 | 2 | 55 |
| 3 | 1 | 42 |
| 4 | 3 | 102 |
| 5 | 2 | 110 |
+-------+-----------+-----------+
I have the following SQL query:
SELECT T.Id
FROM Inventory AS T INNER JOIN
(SELECT ProductId
FROM Inventory
GROUP BY ProductId
HAVING (COUNT(*) > 1)) AS S ON T.ProductId = S.ProductId
This works to give me all of the Ids where a duplicate ProductId exists. Using the above table, this query would return Ids { 1,2,3,5 }, which is exactly what I want.
I tried converting this into a Lambda expression, but it continually fails with the join. Can anyone get me started and point me in the right direction to write this expression?
This is what I have tried:
var q = inventory.Join( inventory.GroupBy( o => o.ProductId ).Where( o => o.Count( ) > 1 ), g => g.ProductId, gb => gb.Key, ( g, gb ) => g.Id ).ToList( );
You need to use somthing like this:
var result = Inventory
.GroupBy(x => x.ProductId)
.Where(x => x.Count() > 1)
.SelectMany(x => x.ToList())
.Select(x => x.Id);

How do i write this query in entity-framework

I have a table (Items) with records in this format.
Name | ProductId | Owner
Product1 | 1 | xx
Product2 | 2 | yy
Product1 | 1 | xx
Product3 | 3 | xx
Product3 | 3 | xx
Product3 | 3 | xx
Product4 | 4 | xx
Product2 | 2 | xx
Product5 | 5 | xx
I want to write entity framework query that will return the top 3 products, their names and Count. The result should look like this.
Result
Name: [Product3, Product2, Product1],
Count:[3, 2, 2]
I have checked other answers here on SO but still did not find something close to what i want. Note Name and Count returns a list of the top 3 respectively.
You can try this code
var query = context
.products
.GroupBy(p => p.Name)
.OrderByDescending(g => g.Count())
.Take(3);
products.Select(p => new
{
Name = query.Select(g => g.Key).ToList(),
Count = query.Select(g => g.Count()).ToList(),
})
.FirstOrDefault();
Although I recommend that you get top 3 product with count together from database and then put it in different list, like this :
var products = context.products
.GroupBy(p => p.Name)
.OrderByDescending(g => g.Count())
.Take(3)
.Select(g => new
{
Name = g.Key,
Count = g.Count()
})
.ToList();
List<string> names = products.Select(p => p.Name).ToList();
List<int> counts = products.Select(p => p.Count).ToList();
The following should work:
products.GroupBy(p => p.ProductId)
.OrderByDescending(g => g.Count())
.Take(3);

Get Max() rows per Revision Number in LinQ

I have this result set:
Company_ID | State | City | Rev |
Company 1 | S-A | C-1 | 1 |
Company 2 | S-B | C-2 | 1 |
Company 1 | S-A | C-3 | 2 |
The result set that I wanted is
Company_ID | State | City | Rev |
Company 1 | S-A | C-3 | 2 |
Company 2 | S-B | C-2 | 1 |
Here's the query that I tried:
var list = (from coy in TBL_COMPANY
where coy.IsActive == "1"
select coy).ToList()
.Select(coy => new
{
coy.Company_ID,
coy.State,
coy.City,
coy.Rev
});
var companyList = list.GroupBy (grp => new
{
grp.Company_ID,
grp.State,
grp.City
})
.Select(cpy => new
{
CompanyID = cpy.Key.Company_ID,
State = cpy.Key.State,
City = cpy.Key.City,
Rev = cpy.Max(c => c.Rev)
}).ToList();
However, I am not able to get the result set I wanted. My suspicion there is because of the City because it is included. However, I need these columns (except for the Rev). Is there any way to do this?
The required result doesn't fit you grouping. There are 3 different City items which should lead to 3 result items.
I'd say Jon Skeet's comment about Grouping only by Company_ID is correct.
Code (group by Company_ID and pick the items with the highest Rev):
var result = TBL_COMPANY.Where(x => x.IsActive == "1")
.GroupBy(x => x.Company_ID)
.Select(x => x.OrderByDescending(y => y.Rev).First())
.Select(x=> new {x.Company_ID, x.State, x.City, x.Rev})
.ToList();

Entity Framework select one of each group by date

I have a table like this (Table name: Posts):
+----+--------------------------+-------+------------+
| id | content | type | date |
+----+--------------------------+-------+------------+
| 0 | Some text | TypeA | 2013-04-01 |
| 1 | Some older text | TypeA | 2012-03-01 |
| 2 | Some even older texttext | TypeA | 2011-01-01 |
| 3 | A dog | TypeB | 2013-04-01 |
| 4 | And older dog | TypeB | 2012-03-01 |
| 5 | An even older dog | TypeB | 2011-01-01 |
+----+--------------------------+-------+------------+
Using a LINQ expression I want to find the newest content of each type, so the result should be
Some text | TypeA
A dog | TypeB
I have tried a few things but no point in pointing out non-working expressions.
If you want to get the whole Posts. You can try this:
var query = Posts.GroupBy(p => p.Type)
.Select(g => g.OrderByDescending(p => p.Date)
.FirstOrDefault()
)
Or using temporary result and predicate
var tmp = posts.GroupBy(x => x.type).Select(x => new {x.Key, date = x.Max(g => g.date)).ToArray();
var filter = PredicateBuilder.False<Post>();
foreach (var item in tmp)
{
filter = filter.Or(x => x.type == item.Key && x.date == item.date);
}
var newestPosts = posts.Where(filter);
I suppose you can group your Posts rows by type and then select first content from descending ordered by date collection of that type
from row in Posts
group row by row.type
into g
select new
{
Content = (from row2 in g orderby row2.date descending select row2.content).FirstOrDefault(),
Type = g.Key
}
From memory, something like this should do it
var data = context.Posts.GroupBy(p => p.Type)
.Select(g => new {
Type = g.Key,
Date = g.OrderByDescending(p => p.Date)
.FirstOrDefault()
}
This would give you a new anonymous type, but you can always map it to a class

Calculating difference between different columns in different rows

I have a table that records what happens to a vehicle during a visit. For each visit, there is are multiple rows to denote what has been done. The table looks like this
VisitID | ActionID | StartTime | EndTime
0 | 0 | 1/1/2013 | 1/2/2013
1 | 0 | 1/2/2013 | 1/4/2013
1 | 1 | 1/4/2013 | 1/7/2013
2 | 0 | 1/4/2013 | 1/5/2013
2 | 1 | 1/5/2013 | 1/6/2013
2 | 2 | 1/6/2013 | 1/7/2013
I wish to construct a LINQ query capable of getting the amount of time a visit took. TotalTime is calculated by first finding the first and last (lowest and highest) ActionID, then last.EndTime - first.StartTime. Expected results:
VisitID | TotalTime
0 | 1
1 | 5
2 | 3
I can generate my expected results by doing
var first = db.Visits.Where(v => v.ActionID == 0)
var last = db.Visits.GroupBy(x => x.VisitID).Select(g => g.OrderByDescending(x => x.ActionID).First())
first.Join(last, f => f.VisitID, l => l.VisitID, (f, l) new{ VisitID = Key, TotalTime = l.EndTime - f.StartTime});
I really don't like the hack I used to get the last ActionID, and I would really like to be able to do this within 1 LINQ statement. What do I need to do to achieve this?
I think this should work...
var result = db.Visits.GroupBy(v => v.VisitID)
.Select(g => new
{
VisitId = g.Key,
TotalTime = g.Max(v => v.EndTime).Subtract(g.Min(v => v.StartTime)).Days
});
Edit: This assumes the actionid doesn't matter so much as the max and min start dates. Here is a different solution where the actionid's are ordered and the first and last Visits are used to calculate the time difference.
var result2 = db.Visits.GroupBy(v => v.VisitID)
.Select(g => new
{
VisitId = g.Key,
TotalTime =
g.OrderBy(v => v.ActionID).Last().EndTime.Subtract(g.OrderBy(v => v.ActionID).First().StartTime).Days
});
db.Visits.GroupBy(v => v.VisitID)
.Select(g => new { VisitId = g.Key,
Days = g.Select(x => x.EndTime.ToOADate()).Sum()
-g.Select(x => x.StartTime.ToOADate()).Sum() });
Used a little hack to add all starts days and end days for each visit and get the difference.

Categories

Resources