Convert a given SQL query into linq for Entity Framework - c#

Can someone help me to convert the below SQL query to Linq? I tried so many methods none of them are worked.
SELECT PM.Name, SUM(CO.TotalPrice)
FROM CustomerOrder CO, PaymentMethod PM
WHERE CO.PaymentID = PM.PaymentID
AND CO.Status = 'CL'
GROUP BY PM.Name

It's really straightforward. You just need a Navigation Property on CustomerOrder for PaymentMethod, and then you translate the group by and select, something like this:
select PM.Name,SUM(CO.TotalPrice)
from CustomerOrder CO, PaymentMethod PM
where CO.PaymentID=PM.PaymentID
and CO.Status='CL'
group by PM.Name
becomes
from o in db.CustomerOrder
where o.Status == 'CL'
group o by o.PaymentStatus.Name into g
select new { PaymentStatus = g.Key, Total = g.Sum(o => o.TotalPrice) };

Related

How to translate this SQL into LINQ, with correlated subquery single table

The raw SQL uses correlated subquery for single table, any possibility to translate this with single LINQ Query?
SELECT * From dbo.Products AS P1
WHERE SalePrice > (SELECT AVG(SalePrice)
FROM dbo.Products AS P2
WHERE P1.Type = P2.Type GROUP BY Type)
it groups products by type and select the product whose SalePrice is greater than type group average. LINQ data source is a EFCore DbSet.
Try the following query:
var products = context.Products;
var grouped =
from p in products
group p by p.Type into g
select new
{
Type = g.Key,
Average = g.Average(x => x.SalePrice)
};
var query =
from p in products
join g in grouped on p.Type equals g.Type
where p.SalePrice > g.Average
select p;
from product in products.AsEnumerable()
group product by product.Type into typeGroup
from product in typeGroup
where product.SalePrice > typeGroup.Average(product => product.SalePrice)
select product;
Attention: the product in second from beginning SQL is different from the first.
EF Core 6 could not translate this LINQ on IQueryable
So we use AsEnumerable(), and it takes all product data into memory. So maybe the raw SQL one should be better.
Splitting it into two query like another answer will solve the problem and make it more readable.
The reason is IQueryable.GroupBy() can't be the final operator for EF 6, which is planned to be improved with EF Core 7.
To prevent GroupBy() being the final operator. The solution is:
var query =
from product in products
join typeGroup in
from product in products
group product by product.Type into typeGroup
select new { Type = typeGroup.Key, Average = typeGroup.Average(p => p.SalePrice) }
on product.Type equals typeGroup.Type
where product.SalePrice > typeGroup.Average
select product;

SQL query in LINQ to SQL

I am trying to write SQL query in LINQ to SQL but after 4h of trying i gave up.
select B.* from Bid B
join
(
select BidId, max(BidVersion) as maxVersion
from Bid
group by BidId
) X on B.BidId = X.BidId and B.BidVersion = X.maxVersion
i saw some tips on the stackOverflow but they werent helpful.
I am using some VERY bad code like:
List<Bid> bidEntities = new List<Bid>();
var newest = from bid in _dbContext.Bids
group bid by bid.BidId into groups
select new { Id = groups.Key, Vs = groups.Max(b => b.BidVersion) };
foreach (var group in newest)
{
bidEntities.Add(await _dbContext.Bids.Where(b => b.BidId == group.Id && b.BidVersion == group.Vs).SingleOrDefaultAsync());
}
Thank you for any advice.
Something like this should work:
var rows = Bids
.GroupBy(b => b.BidId)
.Select(g => g.OrderByDescending(gg => gg.BidVersion).FirstOrDefault())
With LINQ, it helps sometimes not to think in the 'SQL way', but about what you really want. You want the highest version bid for each BidId

Group By with multiple column

I have data in a table as below
RowId | User | Date
--------------------------
1 A 2015-11-11 08:50:48.243
2 A 2015-11-11 08:51:01.433
3 B 2015-11-11 08:51:05.210
Trying to get the data as below:
User, Date, Count
A 2015-11-11 2
B 2015-11-11 1
Select User,Date,Count(User) from Table1
Group By User,Date
It is returning me 3 rows because of time involved in Date field.
How to get this in SQL and Linq.
Please suggest me.
EDITING:
I am able to get it in SQL
Select User,Cast(Date as Date),Count(User) from Table1
Group By User,Cast(Date as Date)
EDITING:
adding linq query
var details = db.table1.GroupBy( r => new { r.RowId,r.User,r.Date})
.Select(g => new {Name = g.Key, Count = g.Count()}).ToList();
For Linq Query just do the following: (you need to import using System.Data.Entity.SqlServer namespace.
Execute this linq query all calculations are done on the server database. Notice that Table1s represents the DbSet for Table1 and context is your DbContext instance.
var query = from item in context.Table1s
group item by new
{
item.User,
Year = SqlFunctions.DatePart("yyyy", item.Date),
Month = SqlFunctions.DatePart("mm", item.Date),
Day = SqlFunctions.DatePart("dd", item.Date)
} into g
select new { g.Key.User, g.Key.Year, g.Key.Month, g.Key.Day, Count = g.Count() };
Then create the final result like this:
var result = query.ToList().Select(p =>
new
{
p.User,
Date = new DateTime(p.Year.Value, p.Month.Value, p.Day.Value),
p.Count
}).ToList();
Other solution is to create a SQL View that will be used by DbContext to retrive the data you want. The SQL View body must be the SQL your wrote in your question.
EDIT 2 : DbFunctions
Like Cetin Basoz pointed in comments we can use System.Data.Entity.DbFunctions as well. And the code is more cleaner than using SqlFunctions. This will work only with EF 6 and greater. The version using SqlFunctions work with EF 4 and greater.
var query = from item in context.Table1s
group item by new
{
item.User,
Date = DbFunctions.TruncateTime(item.Date)
} into g
select new { g.Key.User, g.Key.Date, Count = g.Count() };
EDIT 1 : this is specific for Cetin Basoz's answer :
As we all know using AsEnumerable is not efficient for doing what is needed.
The second solution he gives us which is :
var grouped = from d in db.MyTable
group d by new {
User = d.User,
Date=d.Date.HasValue ? d.Date.Value.Date : (DateTime?)null} into g
select new {User=g.Key.User, Date=g.Key.Date, Count=g.Count()};
This solution just not work because of this :
The specified type member 'Date' is not supported in LINQ to Entities. Only initializers, entity members, and entity navigation properties are supported.
If the time is the problem, you can first convert it:
select User, CAST(dateColumn AS DATE) as dateConverted
into #tempTable
from myTable
then using a window function or a group by:
select *,
count(user) over (partition by date) as userCount
from #tempTable
This should work in SQL server, don't know about Linq
edit: If the date part is the problem, just select into from your table to a table with the casted date. Then you won't have this problem in Linq.
var grouped = from d in db.MyTable.AsEnumerable()
group d by new {
User = d.User,
Date=d.Date.HasValue ? d.Date.Value.Date : (DateTime?)null} into g
select new {User=g.Key.User, Date=g.Key.Date, Count=g.Count()};
Sooner or later, someone would say that this is not server side grouping and would suffer from performance and they would be right. Without Enumerable it is serverside but at the cost of another call per group, so here is another way:
public class MyResult
{
public string User {get;set;}
public DateTime? Date {get;set;}
public int Count {get;set;}
}
var grouped = db.ExecuteQuery<MyResult>(#"select [User],
Cast([Date] as Date) as [Date],
Count(*) as [Count]
from myTable
group by [user], Cast([Date] as Date)");
EDIT: I don't know why I thought the other way before, this would just work serverside and do it, AsEnumerable() was not needed:
var grouped = from d in db.MyTable
group d by new {
User = d.User,
Date=d.Date.HasValue ? d.Date.Value.Date : (DateTime?)null} into g
select new {User=g.Key.User, Date=g.Key.Date, Count=g.Count()};

How to use CAST,CONVERT and isNULL in LINQ?

I have the query:
SELECT TOP 50 CONVERT(date, o.OrderDate)as OrderDate,ISNULL(rd.SerialNumbers,'') as SerialNumbers,CAST(o.SourceOrderID as varchar(50)) as SourceOrderNumber
From Orders o
Query is edited for question.
var lq= (
from o in db.Orders
select new {,o.Name, o.Company, o.Address, o.Address2, o.City, o.State, o.Country, o.Email, o.Zip, o.Phone, o.ShipName, o.ShipCompany, o.ShipAddress, o.ShipAddress2, o.ShipCity, o.ShipCountry, o.ShipState, o.ShipPhone, o.ShipZip, o.OrderNumber, o.ShippingTotal }
).ToList();
I can make simple joins and select in LINQ but not getting idea how to get selects like one mentioned in query.
I am using EF
Given there is no way to actually perform a string to datetime conversion as part of the DB query using LINQ, it's recommended you use AsEnumerable to switch the context of the list to run the actual conversion in memory
orders.Take(50)
.AsEnumerable()
.Select(x => new {
OrderDate = x.OrderDate.Date,
SerialNumbers = o.SerialNumbers ?? "",
SourceOrderNumber = o.SourceOrderID.ToString()
});
LINQ2SQL is actually bright enough to handle the parse in the code:
var q = from c in Customers
where c.PhoneNumber == "9075556658"
select new
{
SSN = c.SSN,
DOB = DateTime.Parse(c.BirthDate)
};
q.Dump();
I just run that in LinqPad on my own database, and it worked fine.
NOTE: This was tested with Linq2SQL, not Entity Framework, which has a history of not be able to do things Linq2SQL has been doing for years.

stuck with a linq query

am stuck with this linq query, all i need is to optimize the last price calculation, cause i get about a 1000 article, & a lot of sales so its getting slow ...
var result = from article in db.Entities.Articles
select new{
article.ID_ART,
article.Designation,
article.BuyPrice,
article.SellPrice,
LastPrice = (from sale in article.Sales where sale.Date == article.Sales.Max(X => X.Date) select sale.Price).FirstOrDefault()==0?
article.BuyPrice: (from sale in article.Sales where sale.Date == article.Sales.Max(X => X.Date) select sale.Price).FirstOrDefault()
}
var result = from article in db.Entities.Articles
let lastPrice = (from sale in article.Sales
orderby sale.Date descending
select sale.Price).FirstOrDefault()
select new
{
article.ID_ART,
article.Designation,
article.BuyPrice,
article.SellPrice,
LastPrice = lastPrice ==0 ? article.BuyPrice : lastPrice
}
You should either join or Include Sales. I assume since it's a navigation property on article that it's an FK table.
Simply use from article in db.Entities.Articles.Include("Sales")... instead.
That will load sales for reference and prevent it from running a subquery when initializing the anonymous type.

Categories

Resources