How to convert SQL multiple table query to Linq - c#

I tried to convert sql query to linq but couldn't get it correctly. Can someone help me to convert below to linq please?
SELECT stockdiary.datenew, locations.ID AS LOCATIONID, locations.NAME AS LOCATIONNAME,
products.REFERENCE, products.NAME,
products.CATEGORY, categories.NAME AS CATEGORYNAME,
products.SUPPLIER,
SUM(CASE WHEN stockdiary.UNITS <0 THEN stockdiary.UNITS ELSE 0 END) AS UNITSOUT,
SUM(CASE WHEN stockdiary.UNITS <0 THEN stockdiary.UNITS * stockdiary.PRICE ELSE 0 END) AS TOTALOUT,
SUM(CASE WHEN stockdiary.UNITS >=0 THEN stockdiary.UNITS ELSE 0 END) AS UNITSIN,
SUM(CASE WHEN stockdiary.UNITS >=0 THEN stockdiary.UNITS * stockdiary.PRICE ELSE 0 END) AS TOTALIN,
SUM(stockdiary.UNITS) AS UNITSDIFF,
SUM(stockdiary.UNITS * stockdiary.PRICE) AS TOTALDIFF
FROM stockdiary JOIN locations ON stockdiary.LOCATION = locations.ID,
products LEFT OUTER JOIN categories ON products.CATEGORY = categories.ID
WHERE products.ID = stockdiary.PRODUCT
GROUP BY locations.ID, locations.NAME, products.REFERENCE, products.NAME, products.CATEGORY, categories.NAME
ORDER BY locations.ID, categories.NAME, products.NAME
======
below is my linq query which gives me wrong result.
(from sd in Stockdiaries
join loc in Locations on sd.Location equals loc.Id
join prod in Products on sd.Product equals prod.Id
join cat in Categories on prod.Category equals cat.Id
select new
{
Location = loc.Name,
Category = cat.Name,
Reference = prod.Reference,
Product = prod.Name,
UnitsOut = sd.Units < 0 ? sd.Units:0,
TotalOut = sd.Units < 0 ? sd.Units * sd.Price:0,
UnitsIn = sd.Units >= 0 ? sd.Units:0,
TotalIn = sd.Units >= 0 ? sd.Units * sd.Price:0,
UnitsDiff = sd.Units,
TotalDiff = sd.Units * sd.Price
})

You've got some old join syntax in that original sql! This is in addition to the left join. This is abbreviated here:
from stockdiary
join locations on stockdiary.location = locations.id
, products
left join categories on products.category = categories.id
where products.id = stockdiary.product
See if the following approach works out for you. It may not output exactly as you desire it, but it's hopefully close and sufficient for you to work with it after that. Although that being said, I don't have any sample data from you on which to test it, so the only thing I can confirm right now is that it won't error out.
I've got comments within the code describing its components.
var query =
from sd in Stockdiaries
join loc in Locations on sd.Location equals loc.Id
// Your old syntax join should work like an inner join
join prod in Products on sd.Product equals prod.Id
// This is a left join. So you've got to do the 'into' hoop and
// then 'overwrite' the cat table.
join cat in Categories on prod.Category equals cat.Id into pCat
from cat in pCat.DefaultIfEmpty()
// put it all together into one result set
select new {
Location = loc.Name,
Category = cat?.Name, // Because it's a left join, it may be null, hence the '?'
Reference = prod.Reference,
Product = prod.Name,
sd.Units,
sd.Price
} into cnd
// group as appropriate, and remember that in linq
// grouping is a separate operation from aggregation
group cnd by new { cnd.Location, cnd.Reference, cnd.Product, cnd.Category } into g
// aggregate
select new {
g.Key.Location,
g.Key.Reference,
g.Key.Product,
g.Key.Category,
UnitsOut = g.Sum(row => row.Units < 0 ? row.Units : 0),
TotalOut = g.Sum(row => row.Units < 0 ? row.Units * row.Price : 0),
UnitsIn = g.Sum(row => row.Units >= 0 ? row.Units : 0),
TotalIn = g.Sum(row => row.Units >= 0 ? row.Units * row.Price : 0),
UnitsDiff = g.Sum(row => row.Units),
TotalDiff = g.Sum(row => row.Units * row.Price)
};
query.Dump();

#pwilcox and all other friends,
Here is the final linq query which gave me the absolute result! I added orderby expression in addition to the above query.
(from sd in Stockdiaries
join loc in Locations on sd.Location equals loc.Id
join prod in Products on sd.Product equals prod.Id
join cat in Categories on prod.Category equals cat.Id into pCat
from cat in pCat.DefaultIfEmpty()
select new {
Location = loc.Name,
Category = cat.Name,
Reference = prod.Reference,
Product = prod.Name,
sd.Units,
sd.Price
} into cnd
group cnd by new {cnd.Location,cnd.Reference, cnd.Product, cnd.Category} into g
orderby g.Key.Location, g.Key.Category, g.Key.Product
select new {
g.Key.Location,
g.Key.Reference,
g.Key.Product,
g.Key.Category,
UnitsOut = g.Sum(row => row.Units < 0 ? row.Units : 0),
TotalOut = g.Sum(row => row.Units < 0 ? row.Units * row.Price : 0),
UnitsIn = g.Sum(row => row.Units >= 0 ? row.Units : 0),
TotalIn = g.Sum(row => row.Units >= 0 ? row.Units * row.Price : 0),
UnitsDiff = g.Sum(row => row.Units),
TotalDiff = g.Sum(row => row.Units * row.Price)
})

Related

Performance issue in linq query

I have created a linq query to join tables and to do aggregate function and it takes more than a minute and it is affecting performance when executing the query in database it takes 18 seconds Kindly help me to improve the performance of Linq query.
Code:
List<int> oStatus = new List<int> { (int)STATUS.PAID, (int)STATUS.PARTIALY_PAID, (int)STATUS.OPEN, (int)STATUS.COMPLETED };
List<int> oLocationids = (
from loc in oTransactionContext.Location
join lum in oTransactionContext.LocationUserMap on loc.LocationId equals lum.LocationId
where lum.UserId == iUserID && loc.LocationName != "Local Purchase" && loc.LocationId !=0 &&loc.IsActive==true
select lum.LocationId
).ToList();
var oPT_Det = (
from inv in oTransactionContext.Invoice
join loc in oTransactionContext.Location on inv.LocationId equals loc.LocationId
where (inv.ClientId == (iClientID) && oLocationids.Contains(inv.LocationId.GetValueOrDefault()) &&
(inv.EndTime > DateTime.UtcNow.AddMonths(-6))
&& inv.IsActive == (true) && oStatus.Contains(inv.Status.GetValueOrDefault()))
group new { inv, loc } by new { inv.LocationId, loc.LocationName } into groupResult
select new OverAllSales
{
Location = Helper.CommonHelper.ParseString(groupResult.Key.LocationName),
InvoiceAmount = Helper.CommonHelper.ParseDecimal(groupResult.Sum(f => f.inv.TotalInvoiceAmount)),
}
).ToList();
Postgresql:
SELECT
COALESCE(SUM(inv.total_invoice_amount), 0) AS value1,
loc.location_name AS metric
FROM location loc
LEFT JOIN location_user_map LUM ON LUM.location_id = loc.location_id
LEFT OUTER JOIN invoice inv ON inv.client_id =2 AND inv.location_id = loc.location_id
AND inv.status IN (SELECT status_id FROM status WHERE status IN ('Paid', 'Partialy Paid', 'Open', 'Completed'))
WHERE loc.location_name NOT IN ('Local Purchase')
AND loc.location_id != 0
AND LUM.user_id IN ($user_ids)
AND inv.is_active = TRUE
AND CAST(inv.end_time AS date) > CURRENT_DATE - INTERVAL '2' MONTH
GROUP BY loc.location_name
ORDER BY value1 DESC
This is direct translation of the SQL. Should generate similar SQL query.
var startDate = DateTime.Date.AddMonths(-2);
int clientId = ...
int userId = ...
var oStatus = new List<int> { (int)STATUS.PAID, (int)STATUS.PARTIALY_PAID, (int)STATUS.OPEN, (int)STATUS.COMPLETED };
var query =
from loc in oTransactionContext.Location
join lum in oTransactionContext.LocationUserMap on loc.LocationId equals lum.LocationId
from inv in oTransactionContext.Invoice
.Where(inv => inv.ClientId == clientId && inv.LocationId = loc.LocationId
&& oStatus.Contains(inv.Status))
where
loc.LocationName != "Local Purchase"
&& loc.LocationId !=0
&& lum.UserId == userId
&& inv.IsActive == true
&& inv.end_time >= startDate
group inv by loc.LocationName into g
select new
{
Location = g.Key,
InvoiceAmount = g.Sum(x => x.total_invoice_amount) ?? 0
};
var oPT_Det = query.ToList();

Show related records according to the Id from another Table

In my ASP.NET MVC application, I have created an HTML Table using the view model.
For this, I have written a query that shows only the data that ReOrderQty is !=0 and AvaQty is less than ReOrderQty.
List < RecognizedPartsViewModel > Reco = new List < RecognizedPartsViewModel > ();
var rData = (from i in db.InventoryMain
join p in db.PartCategory on i.PartCatogary equals p.Id
where i.ReOrderQty != 0 && i.AvaQty <= i.ReOrderQty && i.PartCatogary != 0
select new RecognizedPartsViewModel {
Id = i.Id,
PartNo = i.PartNo,
Description = i.PartDescription,
Model = i.PartModel,
AvaQty = i.AvaQty,
ReOrderQty = i.ReOrderQty,
PartCato = i.PartCatogary,
ABCD = i.A_B_C_D_Category
}).ToList();
So as so far, table data is showing according to the query.
There is another table where I store Orders according to the PartId. So I want to show that data on another column in the same HTML Table.
I can get those details by joining the join ord in db.OrderTable on i.Id equals ord.PartNo_Id
but when it does results only show the PartNumbers that only contains on the OrderTable only.
This is how I modified it as I mention.
List < RecognizedPartsViewModel > Reco = new List < RecognizedPartsViewModel > ();
var rData = (from i in db.InventoryMain
join p in db.PartCategory on i.PartCatogary equals p.Id
join ord in db.OrderTable on i.Id equals ord.PartNo_Id
where i.ReOrderQty != 0 && i.AvaQty <= i.ReOrderQty && i.PartCatogary != 0
select new RecognizedPartsViewModel {
Id = i.Id,
PartNo = i.PartNo,
Description = i.PartDescription,
Model = i.PartModel,
AvaQty = i.AvaQty,
ReOrderQty = i.ReOrderQty,
PartCato = i.PartCatogary,
ABCD = i.A_B_C_D_Category,
PastOrders = "Order Qty: " + ord.OrderQty
}).ToList();
So, when this does like I was said earlier not show every record, it shows only the record in the ordertable.
So how I can show those tables within the same view without losing my main requirement?
That would be
left outer join
var rData = (from i in db.InventoryMain
join p in db.PartCategory on i.PartCatogary equals p.Id
join ord in db.OrderTable on i.Id equals ord.PartNo_Id into leftjoin
from order in leftjoin.DefaultIfEmpty()
where i.ReOrderQty != 0 && i.AvaQty <= i.ReOrderQty && i.PartCatogary != 0
select new RecognizedPartsViewModel {
Id = i.Id,
PartNo = i.PartNo,
Description = i.PartDescription,
Model = i.PartModel,
AvaQty = i.AvaQty,
ReOrderQty = i.ReOrderQty,
PartCato = i.PartCatogary,
ABCD = i.A_B_C_D_Category,
PastOrders = "Order Qty: " + order?.OrderQty ?? string.Empty
}).ToList();
Reference: Perform left outer joins

LINQ with multiple columns and operations

i'm trying to do this on LINQ:
select p.ProductID, p.ArticleCode, SUM((case h.OperationType when 0 then 1 else -1 end) * h.[Count])
from Products p
inner join StockItems s on p.ArticleCode = s.ArticleCode
inner join StockHistorical h on s.ArticleID = h.ArticleID
where h.[Date] < '23/08/2013 11:30:00'
group by p.ProductID, p.ArticleCode
I have this:
var products = (from p in e.Products
join s in e.StockItems on p.ArticleCode equals s.ArticleCode
join h in e.StockHistoricals on s.ArticleID equals h.ArticleID
where h.Date < DateTime.Parse("23/08/2013 11:30:00")
group p by p.ProductID into StockResult
select new { });
Anyone know how can i do the
SUM((case h.OperationType when 0 then 1 else -1 end) * h.[Count])
with LINQ?
Thanks!
I forgot to say that the problem is the "group by", because i can't access the OperationType property in the StockResult group.
Solved! The key is the:
let combined = new
{
Product = p,
HistoryCount = (h.OperationType == 0 ? 1 : -1) * h.Count
}
...
let combined = new
{
Product = p,
HistoryCount = (h.OperationType == 0 ? 1 : -1) * h.Count
}
group combined by combined.Product.ProductID into StockResult
select new
{
ProductID = StockResult.Key,
Total = StockResult.Sum(x => x.HistoryCount)
}
It's not a direct translation, but I'd go with:
(h.Count(his => his.OperationType != 0)
- h.Count(his => his.OperationType == 0))
* h.Count()
It should be functionally equivalent, and seems to better represent what you're trying to do.
You are trying to do something like this?
I am not sure this is going to work.
select new
{
S=Sum(((h.OperationType)!=0?-1:1)*h.Count)
}

Writing case statements using LINQ (with GROUP BY)

I'm really struggling with LINQ. I've written my query out in SQL (in effect it's a pivot), and trying to write an equivalent statement in LINQ. Any pointers forward would be much appreciated. See below for code:
SELECT b.URL,
SUM(CASE WHEN (a.ExtFlag = 0) THEN 1 ELSE 0 END) AS IntLinks,
SUM(CASE WHEN (a.ResponseCode >= 400 AND a.ExtFlag = 0) THEN 1 ELSE 0 END) AS IntBroken,
SUM(CASE WHEN (a.ExtFlag = 1) THEN 1 ELSE 0 END) AS ExtLinks,
SUM(CASE WHEN (a.ResponseCode >= 400 AND a.ExtFlag = 1) THEN 1 ELSE 0 END) AS ExtBroken
FROM Link a
INNER JOIN Host b
ON a.HostID = b.ID
GROUP BY b.URL
ORDER BY b.URL
Many Thanks.
I think this should do what you want - it's at least worth a try:
var query = from link in db.Links
join host in db.Hosts on link.HostID equals host.ID
group link by host.Url into links
select new
{
Url = links.Url,
IntLinks = links.Count(link => link.ExtFlag == 0),
IntBroken = links.Count(link => link.ExtFlag == 0 &&
link.ResponseCode >= 400),
ExtLinks = links.Count(link => link.ExtFlag == 1),
ExtBroken = links.Count(link => link.ExtFlag == 1 &&
link.ResponseCode >= 400),
};
This is how to do it:
from a in db.Links
group a by a.Host.Url into g
select new
{
Url = g.Key,
IntLinks = (
from x in g
select x.ExtFlag == 0 ? 1 : 0)
.Sum()
};
You can use the ?: operator for your CASE.

SQL into LINQ to Entities

I have a big problem.
I'm for last 5 year SQL-boy, but now I need to convert my SQL query into LINQ to entity C# format.
Because I'm new in LINQ (complex statements) right now I need fast help.
Thank's in advance.
P.S. also I need some advices, some start point to start rapidly to learn LINQ to entities.
Here is my SQL (direct from my app(#endDate,#endDate and #glChartID remain as parameters also in my c# app)):
SELECT budget.accountid,
budget.perioddate,
budget.lastyear,
budget.thisyear,
budget.budget,
budget.operplan,
budget.forecast,
glchart.accounttype,
glchart.headertype
FROM budget INNER JOIN glchart ON budget.accountid = glchart.id
WHERE budget.accountid = #glChartID AND budget.perioddate BETWEEN #startDate and #endDate AND glchart.headertype NOT LIKE 'Header%'
UNION
SELECT glchart.id,
budget.perioddate,
SUM(ISNULL(budget.lastyear, 0)),
SUM(ISNULL(budget.thisyear, 0)),
SUM(ISNULL(budget.budget, 0)),
SUM(ISNULL(budget.operplan, 0)),
SUM(ISNULL(budget.forecast, 0)),
glchart.accounttype,
glchart.headertype
FROM budget INNER JOIN glchart ON budget.accountid = glchart.id
WHERE budget.accountid
IN
(SELECT g.id FROM glchart g
WHERE g.code >= glchart.code AND g.code <
CASE
WHEN glchart. headerlevel = 1 AND
(SELECT MAX(g3.code)
FROM glchart g3
WHERE g3.headerlevel = 1
) = glchart.code
THEN
(SELECT MAX(g2.code)
FROM glchart g2
WHERE g2.code >= g.code)
ELSE
(SELECT MIN(g2.code)
FROM glchart g2
WHERE g2.code > glchart.code AND
g2.headerlevel = glchart. headerlevel) END ) AND
glchart.id = #glChartID AND
budget.perioddate BETWEEN #startDate AND #endDate AND
glchart.headertype LIKE 'Header%'
GROUP BY glchart.id, budget.perioddate, glchart.accounttype, glchart.headertype
Until today, I managed (thanks to DOK)how to do it and this is how my LINQ is look like right now:
var query = ((ObjectQuery<Budget>)(
from budgets in this.ObjectContext.Budgets
join glcharts in this.ObjectContext.GLCharts on new { AccountID = budgets.AccountID } equals new { AccountID = glcharts.ID }
where
(!(from glC in this.ObjectContext.GLCharts
where Convert.ToInt16(glC.Code) >= Convert.ToInt16(glcharts.Code) && glC.Code != (Convert.ToInt64(glcharts.HeaderLevel) == 1 &&
(from g3 in this.ObjectContext.GLCharts
where Convert.ToInt64(g3.HeaderLevel) == 1
select new {g3.Code}).Max(p => p.Code) == glcharts.Code ?
(from g2 in this.ObjectContext.GLCharts
where Convert.ToInt16(g2.Code) >= Convert.ToInt16(glC.Code)
select new {g2.Code}).Max(p => p.Code) :
(from g2 in this.ObjectContext.GLCharts
where Convert.ToInt16(g2.Code) > Convert.ToInt16(glcharts.Code) && g2.HeaderLevel == glcharts.HeaderLevel
select new {g2.Code}).Min(p => p.Code))
select new {glC.ID}
).Contains(new { budgets.AccountID }) &&
glcharts.ID == 2376 && budgets.PeriodDate >= StartDate &&
budgets.PeriodDate <= EndDate &&
glcharts.HeaderType.StartsWith("Header"))
).Contains(new { budgets.AccountID }) && glcharts.ID == 2376 && budgets.PeriodDate >= StartDate && budgets.PeriodDate <= EndDate && glcharts.HeaderType.StartsWith("Header")
group new {glc = glcharts, b = budgets}
by new {
glcharts.ID,
budgets.PeriodDate,
glcharts.AccountType,
glcharts.HeaderType
} into g
select new {
AccountID = (System.Int32?)g.Key.ID,
PeriodDate = (System.DateTime?)g.Key.PeriodDate,
LastYear = g.Sum(p => ((System.Decimal?)p.t.LastYear ?? (System.Decimal?)0)),
ThisYear = g.Sum(p => ((System.Decimal?)p.t.ThisYear ?? (System.Decimal?)0)),
Budget = g.Sum(p => ((int?)p.t.Budget1 ?? (int?)0)),
OperPlan = g.Sum(p => ((System.Decimal?)p.t.OperPlan ?? (System.Decimal?)0)),
Forecast = g.Sum(p => ((System.Decimal?)p.t.Forecast ?? (System.Decimal?)0)),
AccountType = g.Key.AccountType,
HeaderType = g.Key.HeaderType
}));
return query;
But in THIS LINE: .Contains(new { budgets.AccountID }) I'm getting next error :
Error 8'System.Linq.IQueryable' does not contain a definition for 'Contains' and the best extension method overload 'System.Linq.ParallelEnumerable.Contains(System.Linq.ParallelQuery, TSource)' has some invalid arguments
Does anybody have an idea where I'm wrong?
Thanks to everyone.
You might find some help in this excellent reference site.
That will lead you to, for example, two examples for UNION.
If you really must start out at this level of difficulty, you might consider breaking your SQL down into pieces and getting them working bit by bit. Do the first SELECT without the JOIN or WHERE, then add those one at a time. Then do the second SELECT the same way. Then add the UNION.
By the time you get this one worked out, SQL-boy, you will definitely be LINQ-man!

Categories