Translate SQL to lambda LINQ with GroupBy and Average - c#

I spend a few hours trying to translate simple SQL to lambda LINQ
SELECT ID, AVG(Score) FROM myTable
GROUP BY ID
Any idea?

from t in myTable
group t by new {
t.ID
} into g
select new {
Average = g.Average(p => p.Score),
g.Key.ID
}
or Lambda
myTable.GroupBy(t => new {ID = t.ID})
.Select (g => new {
Average = g.Average (p => p.Score),
ID = g.Key.ID
})

The equivalent in Linq-to-Objects would be something like the below.
var results = from row in myTable
group row by row.Id into rows
select new
{
Id = rows.Key,
AverageScore = rows.Average(row => row.Score)
};
It's only slightly different for an ORM like entity framework. Namely, you would need to go through the data context or an appropriate DbSet/ObjectSet.

var _result = from a in myTable
group a by a.ID into g
select new
{
ID = g.Key.ID,
AverageResult = g.Average(x => x.Score)
}

Related

Join where lambda query in EF Core 3.1

I have a problem to write in lambda query this sql query:
select c.Id, c.Name, c.SomeNumber, count(*) from TableA a
inner join TableB b
on a.Id = b.aId
inner join TableC c
on c.BId = b.Id
where b.Status = N'Approved'
and c.Scope = N'Scope1'
group by a.Id, a.Name, a.SomeNumber
Can you guys help me with this one ? I want to write lambda query to execute this in code. I'm using EF Core 3.1
This is what I ended up so far:
var query = await _dbContext.TableA.Where(a => a.TableB.Any(b => b.Status.Equals("Approved")
&& b.TableC.Any(c => c.Scope.Equals("Scope1"))))
.GroupBy(g => new { Id = g.Id, Name = g.Name, SomeNumber = g.SomeNumber })
.Select(s => new { Id = s.Key.Id, Name = s.Key.Name, SomeNumber = s.Key.SomeNumber, Count = s.Count() })
.GroupBy(g => g.Id).Select(s => new {Id = s.Key, Count = s.Count()}).ToListAsync();
Well, this is corrected query. I have used Query syntax which is more readable when query has lot of joins or SelectMany.
var query =
from a in _dbContext.TableA
from b in a.TableB
from c in b.TableC
where b.Status == "Approved" && c.Scope == "Scope1"
group a by new { a.Id, a.Name, a.SomeNumber } into g
select new
{
g.Key.Id,
g.Key.Name,
g.Key.SomeNumber,
Count = g.Count()
}
var result = await query.ToListAsync();
It's maybe easier to start from the many end and work up through the navigation properties
tableC
.Where(c => c.Scope == "Scope1" && c.BEntity.Status == "Approved")
.GroupBy(c => new
{
c.BEntity.AEntity.Id,
c.BEntity.AEntity.Name,
c.BEntity.AEntity.SomeNumber
})
.Select(g => new { g.Key.Id, g.Key.Name, g.Key.SomeNumber, Ct = g.Count()})
EF knows how to do joins when you navigate around the object tree in the where. By starting at the many and and working up to the 1 end of the relationship it means you don't have to get complex with asking "do any of the children of this parent have a status of ..."

How to shorten the LINQ query to calculate the sum for parent table from child tables

Currently I have this LINQ query which calculate the totalcount of a parent table (CafeTables) from child tables (CafeTableDetails). These code works. But somehow, I believe these code can the shorten.
var selectedTable = db.CafeTables.Where(c => c.TableNo.Equals(userName)).SingleOrDefault();
var selectedTableDetailsRaw = db.CafeTableDetails.
Where(cd => cd.CafeTableId == selectedTable.Id);
selectedTable.TotalOrders = selectedTableDetailsRaw.Count();
I think you can try to use linq JOIN and GroupBy to make it.
var result = db.CafeTables.Where(c => c.TableNo == userName)
.Join(db.CafeTableDetails.GroupBy(x=>x.CafeTableId)
.Select(g => new { CafeTableId = g.Key, cnt = g.Count() }),
st => st.Id,
cd => cd.CafeTableId,
(st,cd) => new
{
st.Id,
cd.cnt
//..... your expect property
});

Convert SQL query using 'count' clause, to LinQ

My SQL Server query (below) works perfectly fine, and I'm trying to convert it to LinQ in C#.
SQL Query:
SELECT addressline3, city, COUNT(*) as 'InstitutionNumber'
FROM institutionenquiries
WHERE CITY = 'AHMEDABAD'
GROUP BY addressline3, city
ORDER BY city;
Desired output is:
I'm able to draw the LinQ query in below format, which gives me correct output
(except for COUNT(*) as 'InstitutionNumber').
LINQ Query:
var obj = (from u in dbContext.InstitutionEnquiry
where u.City == data.Trim().ToLower()
select new {
AddressLine3 = u.AddressLine3.Trim().ToLower(),
City = u.City.Trim().ToLower(),
InstitutionNumber = (from a in dbContext.InstitutionEnquiry
where a.City == data.Trim().ToLower()
select a).Count()
}).ToList();
This gives me count for 'InstitutionNumber' = 3 for all.
I'm not able to get the count aspect correctly. I've referred to this post's answer and it gives me only count, but I'm not able to nest it within my LinQ query.
Any helpful inputs will be appreciated.
The first observation is that since SQL has a GROUP BY, LINQ should have a GroupBy, too.
The second observation is that since non-aggregate columns in SQL are group by columns, the results you select in LINQ would come from the group's key.
Now we can write the query:
var res = dbContext.InstitutionEnquiry
.Where(u => u.City == data.Trim().ToLower())
.GroupBy(u => new {
AddressLine3 = u.AddressLine3.Trim().ToLower(),
City = u.City.Trim().ToLower()
}).Select(g => new {
g.Key.AddressLine3,
g.Key.City,
Count = g.Count()
}).ToList();
I think you can use this LinQ :
var obj = dbContext.InstitutionEnquiry
// WHERE CITY = 'AHMEDABAD'
.Where(w => w.City == "AHMEDABAD") // => Note
// GROUP BY addressline3, city
.GroupBy(g => new {g.AddressLine3, g.City})
// SELECT addressline3, city, COUNT(*) as 'InstitutionNumber'
.Select(c => new {c.Key.AddressLine3, c.Key.City, InstitutionNumber = c.Count()})
// ORDER BY city
.OrderBy(o=> o.City)
.ToList();
Note: For ignoring case sensitivity You can use :
.Where(string.Equals(w.City.Trim(), data.Trim(), StringComparison.CurrentCultureIgnoreCase))
LINQ :
var obj = (from p in dbContext.InstitutionEnquiry
group p by new
{
p.addressline3,
p.city
} into grp
select new
{
grp.Key.addressline3,
grp.Key.city,
InstitutionNumber = grp.Count(),
}).ToList();
Equivalent Lambda expression :
var obj = dbContext.InstitutionEnquiry
.GroupBy(p => new { p.addressline3, p.city})
.Select(p => new { p.Key.addressline3, p.Key.city, InstitutionNumber = p.Count() })
.ToList();

C# lambda-> All rows Select Add Row_Number

I have a table:
DataTable store_temp = new DataTable();
store_temp.Columns.Add("patn");
store_temp.Columns.Add("rf");
store_temp.Columns.Add("name");
store_temp.Columns.Add("conv");
store_temp.Columns.Add("conv_type");
store_temp.Columns.Add("recorddate");
store_temp.Columns.Add("executiondate");
My C# code :
int i = 0;
var rowsgroups = (from row in store_temp.AsEnumerable().GroupBy(row =>
row.Field<string>("patn"))
.OrderBy((g => g.OrderByDescending(y => y.Field<string("executiondate")).ThenByDescending(y =>
y.Field<string>("rf"))))
select new
{
patn = row.ElementAt(i),
rf_num = ++i,
}).ToArray();
I want the lambda experession, which is equivalent to:
select patn, rf,
> row_number() over( partition by patn order by executiondate,rf )
as rf_num,
name, conv,conv_type, recorddate, executiondate
from store_temp2
But, lambda syntax ... var rowsgroups has just a one row..
I want to show all rows in store_temp.
What should I do to fix the query?
row_number() over(partition by patn order by executiondate, rf)
means in LINQ you need to group by patn, then order each group by executiondate, rf, then use the indexed Select overload to get row numbering inside the group, and finally flatten the result with SelectMany.
With that being said, the equivalent LINQ query could be something like this:
var result = store_temp.AsEnumerable()
.GroupBy(e => e.Field<string>("patn"), (key, elements) => elements
.OrderBy(e => e.Field<string>("executiondate"))
.ThenBy(e => e.Field<string>("rf"))
.Select((e, i) => new
{
patn = key,
rf = e.Field<string>("rf"),
rf_num = i + 1,
name = e.Field<string>("name"),
conv = e.Field<string>("conv"),
conv_type = e.Field<string>("conv_type"),
recorddate = e.Field<string>("recorddate"),
executiondate = e.Field<string>("executiondate")
}))
.SelectMany(elements => elements)
.ToArray();
Try something like this
select new
{
rowNum = store_temp.Rows.IndexOf(row),
patn = row.ElementAt(i),
rf_num = ++i,
}).ToArray();
I don't think you required any groupby as per your required sql
var i=0;
var rowsgroups = (from row in store_temp.AsEnumerable()
orderby row.Field<string>("executiondate") descending,
row.Field<string>("rf") descending
select new
{
patn = row.Field<string>("patn"),
rf_num = ++i,
name = row.Field<string>("name"),
conv = row.Field<string>("conv"),
conv_type = row.Field<string>("conv_type"),
recorddate = row.Field<string>("recorddate"),
executiondate = row.Field<string>("executiondate")
}).ToArray();

SQL query to LINQ conversion with nested select statements

I want to convert the following query to LINQ:
SELECT LV.* FROM LowerVehicles LV
INNER JOIN (Select VSerial,MAX(updatedOn) MaxUpdatedOn from LowerVehicles group by vserial) LVG
ON LV.VSerial = LVG.VSerial AND LV.updatedOn = LVG.MaxUpdatedOn
Not knowing your entities classes, here is an approximation. You can use query syntax or fluent syntax. Sometimes one is preferable over the other, and in the case of joins and grouping I prefer to use query syntax.
QUERY SYNTAX
var query = from LV in LowerVehicles
join LVG in (
from r in LowerVehicles
group r by r.vserial into g
select new {VSerial = g.Key, MaxUpdatedOn = g.Max(t => t.updatedOn)})
on LV.VSerial equals LVG.Vserial
and LV.updatedOn equals LVG.MaxUpdatedOn
select LV;
FLUENT SYNTAX
var lvg = LowerVehicles.GroupBy(t => t.vserial)
.Select(g => new {
VSerial = g.Key,
MaxUpdatedOn = g.Max(t => t.updatedOn)
});
var query = LowerVehicles.Join(
lvg,
a => new { a.VSerial, a.updatedOn },
b => new { b.VSerial, b.MaxUpdatedOn },
(a, b) => new { LV = a, LVG = b}
)
.Select(t=> t.LV);
Something like this?
Something.LowerVehicles
.Join(something.LowerVehicles.Select(y => new { y.VSerial, updatedOn = y.updatedOn.Max() }).GroupBy(z => z.VSerial),
x => new { x.VSerial, x.updatedOn },
lvg => new { lvg.VSerial, lvg.updatedOn },
(x, y) => x)

Categories