Help with LINQ-SQL GroupBy - c#

I'm trying to convert this T-SQL to a LINQ-SQL query:
-- top 3 pros for city
select top 3 description, ispro, COUNT(*) as numberofvotes
from tblProCon
where IdPrimaryCity = #IdPrimaryCity
and IsPro = 1
group by IdPrimaryCity, IsPro, description
union
-- top 3 cons for city
select top 3 description, ispro, COUNT(*) as numberofvotes
from tblProCon
where IdPrimaryCity = #IdPrimaryCity
and IsPro = 0
group by IdPrimaryCity, IsPro, description
order by ispro, numberofvotes desc
Here's what i have so far:
// Construct base query
var query = (from p in db.tblProCons
where p.IdPrimaryCity == idPrimaryCity
group p by new { p.IdPrimaryCity, p.IsPro, p.Description } into g
select new { Description = g.Key, IsPro = g.Any(x => x.IsPro), NumberOfAgrees = g.Count() });
// Split queries based on pro/con, and apply TOP(3)
var pros = query.Where(x => x.IsPro).Take(3);
var cons = query.Where(x => !x.IsPro).Take(3);
result = pros
.Union(cons) // Union pro/cons
.OrderByDescending(x => x.IsPro) // Order #1 - Pro/Con
.ThenByDescending(x => x.NumberOfAgrees) // Order #2 - Number of Agree's
.Select(x => new ProCon // project into cut-down POCO
{
Description = x.Description,
IsPro = x.IsPro
}).ToList();
But she ain't working. :(
x.Description is complaining "Cannot convert source type {IdPrimaryCity:int, IsPro:bool, Description:string} to target type string".
All i want to end up with is a List<ProCon>, having the description (string), and flag indicating if it's a pro or con.
What am i doing wrong?

Nevermind, i got it, the "group" projection was all wrong.
Here's the working solution:
// Construct base query
var query = (from p in db.tblProCons
where p.IdPrimaryCity == idPrimaryCity
group p by new { p.IdPrimaryCity, p.IsPro, p.Description } into g
select new { ProCon = g.Key, NumberOfAgrees = g.Count() });
// Split queries based on pro/con, and apply TOP(3)
var pros = query.Where(x => x.ProCon.IsPro).Take(3);
var cons = query.Where(x => !x.ProCon.IsPro).Take(3);
result = pros
.Union(cons) // Union pro/cons
.OrderByDescending(x => x.ProCon.IsPro) // Order #1 - Pro/Con
.ThenByDescending(x => x.NumberOfAgrees) // Order #2 - Number of Agree's
.Select(x => new ProCon // project into cut-down POCO
{
Description = x.ProCon.Description,
IsPro = x.ProCon.IsPro
}).ToList();

Related

I didn't true use order by in linq

Hi i develop web app with c#. I have sql query and i convert to linq but it's not working true because of order by
My sql query
Select TOP 3 HastalikIsmi From Hastaliklar group by HastalikIsmi order by Count(*) desc
My linq
public List<HastalikDto> GetHastalikDto()
{
using (SirketDBContext context = new SirketDBContext())
{
var result = from hastalik in context.Hastaliklar
group hastalik by hastalik.HastalikIsmi into isim
select new HastalikDto { HastalikIsmi = isim.Key };
return result.OrderBy(h => h.HastalikIsmi).Take(3).ToList();
}
}
Here's how you can do the order by on the count of each group and take the 3 with the highest count.
var result = context.Hastaliklar
.GroupBy(x => x.HastalikIsmi)
.OrderByDescending(grp => grp.Count())
.Select(grp => grp.Key)
.Take(3)
.ToList();

Order by and group by and sum using SQL

What I am trying to do is get the top 10 most sold Vegetables by grouping them by an Id passed by parameter in a function and ordering them by the sum of their Quantity. I don't know how to use SUM or (total) quite yet but I thought I'd post it here seeking help. If you need me offering you anything else I will be ready.
This is my code:
TheVegLinQDataContext db = new TheVegLinQDataContext();
var query =db.OrderDetails.GroupBy(p => p.VegID)
.Select(g => g.OrderByDescending(p => p.Quantity)
.FirstOrDefault()).Take(10);
And this is an image of my database diagram
Group orders by Vegetable ID, then from each group select data you want and total quantity:
var query = db.OrderDetails
.GroupBy(od => od.VegID)
.Select(g => new {
VegID = g.Key,
Vegetable = g.First().Vegetable, // if you have navigation property
Total = g.Sum(od => od.Quantity)
})
.OrderByDescending(x => x.Total)
.Select(x => x.Vegetable) // remove if you want totals
.Take(10);
Since this is not clear that you are passing what type of id as function parameter, I'm assuming you are passing orderId as parameter.
First apply where conditions then group the result set after that order by Total sold Quantity then apply Take
LINQ query
var result = (from a in orderdetails
where a.OrderId == orderId //apply where condition as per your needs
group a by new { a.VegId } into group1
select new
{
group1.Key.VegId,
TotalQuantity = group1.Sum(x => x.Quantity),
group1.FirstOrDefault().Vegitable
}).OrderByDescending(a => a.TotalQuantity).Take(10);
Lamda (Method) Syntax
var result1 = orderdetails
//.Where(a => a.OrderId == 1) or just remove where if you don't need to filter
.GroupBy(x => x.VegId)
.Select(x => new
{
VegId = x.Key,
x.FirstOrDefault().Vegitable,
TotalQuantity = x.Sum(a => a.Quantity)
}).OrderByDescending(x => x.TotalQuantity).Take(10);

How to write linq query for this sql statement

How would you write a linq query with the following SQL statement. I've tried several methods referenced on stackoverflow but they either don't work with the EF version I'm using (EF core 3.5.1) or the DBMS (SQL Server).
select a.ProductID, a.DateTimeStamp, a.LastPrice
from Products a
where a.DateTimeStamp = (select max(DateTimeStamp) from Products where a.ProductID = ProductID)
For reference, a couple that I've tried (both get run-time errors).
var results = _context.Products
.GroupBy(s => s.ProductID)
.Select(s => s.OrderByDescending(x => x.DateTimeStamp).FirstOrDefault());
var results = _context.Products
.GroupBy(x => new { x.ProductID, x.DateTimeStamp })
.SelectMany(y => y.OrderByDescending(z => z.DateTimeStamp).Take(1))
Thanks!
I understand you would like to have a list of the latest prices of each products?
First of all I prefer to use group by option even over 1st query
select a.ProductID, a.DateTimeStamp, a.LastPrice
from Products a
where a.DateTimeStamp IN (select max(DateTimeStamp) from Products group by ProductID)
Later Linq:
var maxDateTimeStamps = _context.Products
.GroupBy(s => s.ProductID)
.Select(s => s.Max(x => x.DateTimeStamp)).ToArray();
var results = _context.Products.Where(s=>maxDateTimeStamps.Contains(s.DateTimeStamp));
-- all assuming that max datetime stamps are unique
I've managed to do it with the following which replicates the correlated sub query in the original post (other than using TOP and order by instead of the Max aggregate), though I feel like there must be a more elegant way to do this.
var results = from x
in _context.Products
where x.DateTimeStamp == (from y
in _context.Products
where y.ProductID == x.ProductID
orderby y.DateTimeStamp descending
select y.DateTimeStamp
).FirstOrDefault()
select x;
I prefer to break up these queries into IQueryable parts, do you can debug each "step".
Something like this:
IQueryable<ProductOrmEntity> pocoPerParentMaxUpdateDates =
entityDbContext.Products
//.Where(itm => itm.x == 1)/*if you need where */
.GroupBy(i => i.ProductID)
.Select(g => new ProductOrmEntity
{
ProductID = g.Key,
DateTimeStamp = g.Max(row => row.DateTimeStamp)
});
//// next line for debugging..do not leave in for production code
var temppocoPerParentMaxUpdateDates = pocoPerParentMaxUpdateDates.ToListAsync(CancellationToken.None);
IQueryable<ProductOrmEntity> filteredChildren =
from itm
in entityDbContext.Products
join pocoMaxUpdateDatePerParent in pocoPerParentMaxUpdateDates
on new { a = itm.DateTimeStamp, b = itm.ProductID }
equals
new { a = pocoMaxUpdateDatePerParent.DateTimeStamp, b = pocoMaxUpdateDatePerParent.ProductID }
// where
;
IEnumerable<ProductOrmEntity> hereIsWhatIWantItems = filteredChildren.ToListAsync(CancellationToken.None);
That last step, I am putting in an anonymous object. You can put the data in a "new ProductOrmEntity() { ProductID = pocoMaxUpdateDatePerParent.ProductID }...or you can get the FULL ProductOrmEntity object. Your original code, I don't know if getting all columns of the Product object is what you want, or only some of the columns of the object.

LINQ group by with multiple counts

I am having trouble doing multiple counts on a single table in a LINQ query. I am using NHibernate, LINQ to NHibernate and C#.
query is a populated list. I have a table that has a boolean called FullRef. I want to do a LINQ query to give a count of occurances of FullRef = false and FullRef = true on each TrackId. TrackId gets a new row for each time he gets a track.Source == "UserRef".
In the following query I get the correct number count (from the FullRefTrueCount) of FullRef = true, but it gives an unknown wrong number on the FullRefFalseCount.
var query2 = from track in query
where track.Source == "UserRef"
group track by new { TrackId = track.TrackId, FullRef = track.FullRef } into d
select new FullReferrer
{
Customer = d.Key.TrackId,
FullRefFalseCount = d.Where(x => x.FullRef == false).Count(),
FullRefTrueCount = d.Where(x => x.FullRef == true).Count()
};
Anyone have any idea on how to fix it? I am pretty certain the .Where() clause is ignored and the "group by" is screwing me over.
If I could somehow
group track by new { TrackId = track.TrackId, FullRefTrue = track.FullRef, FullRefFalse = !track.FullRef }"
it would work. Is there some way to do this?
you should group by trackId only, if you want results by trackId...
var query2 = query
.Where(m => m.Source == "UserRef")
.GroupBy(m => m.TrackId)
.Select(g => new FullReferrer {
Customer = g.Key,
FullRefFalseCount = g.Count(x => !x.FullRef),
FullRefTrueCount = g.Count(x => x.FullRef)
});

How to project LINQ-to-SQL query into custom object with a GroupBy and OrderBy

I'm trying to convert this query so that it will output to a custom DTO type object. I want to get only pages with the highest revision number for the int[] that I pass in.
return from page in db.Pages
where intItemIdArray.Contains(page.pageId)
group page by page.pageId into g
orderby g.Max(x => x.pageId)
select g.OrderByDescending(t => t.revision).First();
But when I try to replace
select g.OrderByDescending(t => t.revision).First();
With something like
select (new JPage {pageid = g.pageId, title = g.title, etc})
.OrderByDescending(t => t.revision)
.First();
It doesn't work, can anyone help me out?
This is what I have gone with currently, which I don't like, but it is working perfectly, and I don't need to optimize beyond this currently.
It would be great if someone could improve this.
var pages = from page in db.Pages
where intItemIdArray.Contains(page.pageId)
group page by page.pageId into g
orderby g.Max(x => x.pageId)
select g.OrderByDescending(t => t.revision).First();
return pages.Select(x => new JPage() {
pageId = x.pageId,
pageKey = x.pageKey,
title = x.title,
body = x.body,
isFolder = x.isFolder.ToString(),
leftNode = x.leftNode,
rightNode = x.rightNode,
revision = x.revision,
sort = x.sort,
createdBy = x.createdBy.ToString(),
createdDate = Utility.DateTimeToUnixTimeStamp(x.createdDate).ToString(),
modifiedDate = Utility.DateTimeToUnixTimeStamp(x.modifiedDate).ToString(),
pageVariationId = x.pagesVariationId,
parentId = x.parentId
})
.AsQueryable();
I'd suggest that you order before you select; i.e. instead of
select (new JPage {pageid = g.pageId, title = g.title, etc}
.OrderByDescending(t => t.revision).First();
you should try
.OrderByDescending(t => t.revision)
.Select(new JPage {pageid = g.pageId, title = g.title, etc})
.First();
You can't order by 'revision' if it doesn't exist in the result of the previous 'select'
This should be a slight improvement
var pages = from page in db.Pages
where intItemIdArray.Contains(page.pageId)
group page by page.pageId into g
select g.First(a => a.revision == g.Max(b => b.revision));

Categories