SQL ROW_NUMBER() in LINQ Query Syntax - c#

I have this query which I wish to replace with Linq as query syntax:
select
ii.Id, ii.Name as Supplier,
qi.Price1, qi.Price2, qi.Price3,
case when qi.price1 is null then NULL else ROW_NUMBER() OVER(ORDER BY isNull(qi.price1,1000000) ASC) end AS Price1Order,
case when qi.price2 is null then NULL else ROW_NUMBER() OVER(ORDER BY isNull(qi.price2,1000000) ASC) end AS Price2Order,
case when qi.price3 is null then NULL else ROW_NUMBER() OVER(ORDER BY isNull(qi.price3,1000000) ASC) end AS Price3Order
From dbo.InquiryItems ii
left join dbo.quoteItems qi on ii.Id = qi.QuoteItem_InquiryItem
SQL-Query Result:
Id Supplier Price1 Price2 Price3 Price1Order Price2Order Price3Order
1655 Supplier 2 80.00 NULL 40.00 3 NULL 1
1656 Supplier 4 65.00 30.00 42.00 2 1 2
1662 Supplier 1 15.00 35.00 43.00 1 2 3
1670 Supplier 3 250.00 NULL NULL 4 NULL NULL
In C# I need this query as a IQueryable Object. I must filter the query for different parts (one or more) and then group it by Supplier (IdAccount) and SUM the prices. This prices I must rank.
return colQuoteItem = from vQuote in this.vQuoteItemOverviews
where vQuote.IdConstructionStageId == ConstructionStageId
group vQuote by new
{
vQuote.IdAccount
} into g
select new vQuoteItemOverviewSum
{
Id = g.Max(x => x.Id),
Price1Order = null, //Here I need the ROW_NUMBER like in the SQL-Syntax
Price2Order = null, //Here I need the ROW_NUMBER like in the SQL-Syntax
Price3Order = null, //Here I need the ROW_NUMBER like in the SQL-Syntax
price1 = g.Sum(x => x.price1),
price2 = g.Sum(x => x.price2),
price3 = g.Sum(x => x.price3),
}
;

Would this work?
var qi1 = (from qi in quoteItems orderby qi.price1 select new {qi.QuoteItem_InquiryItem}).AsEnumerable().Select((i, index) => new {i.QuoteItem_InquiryItem, Rank = index + 1});
var qi2 = (from qi in quoteItems orderby qi.price2 select new {qi.QuoteItem_InquiryItem}).AsEnumerable().Select((i, index) => new {i.QuoteItem_InquiryItem, Rank = index + 1});
var qi3 = (from qi in quoteItems orderby qi.price3 select new {qi.QuoteItem_InquiryItem}).AsEnumerable().Select((i, index) => new {i.QuoteItem_InquiryItem, Rank = index + 1});
return colQuoteItem = from vQuote in this.vQuoteItemOverviews.AsEnumerable()
where vQuote.IdConstructionStageId == ConstructionStageId
group vQuote by new
{
vQuote.IdAccount
} into g
select new vQuoteItemOverviewSum
{
Id = g.Max(x => x.Id),
Price1Order = qi1.FirstOrDefault(_ => _.IdConstructionStageId == x.Id)?.Rank, //Here i need the ROW_NUMBER like in the SQL-Syntax
Price2Order = qi2.FirstOrDefault(_ => _.IdConstructionStageId == x.Id)?.Rank, //Here i need the ROW_NUMBER like in the SQL-Syntax
Price3Order = qi3.FirstOrDefault(_ => _.IdConstructionStageId == x.Id)?.Rank, //Here i need the ROW_NUMBER like in the SQL-Syntax
price1 = g.Sum(x => x.price1),
price2 = g.Sum(x => x.price2),
price3 = g.Sum(x => x.price3),
}
;

Related

improve this Linq with 2 Selects to same table

Hi i have this query that populates CompletedWords and CompletedRows with Childs that have Status Completed , the thing is it goes 2 times too the segments table instead of 1:
var query = _context.Submodules.Where(t => t.Id == id)
.Select(e => new Submodules{
Id = e.Id,
Name = e.Name,
Status = e.Status,
Token = e.Token,
ModuleId = e.ModuleId,
Gender = e.Gender,
TotalRows = e.TotalRows,
TotalWords = e.TotalWords,
CompletedWords = e.Segments.Where(a => a.Status == Abr.Recorded).Sum(y=> y.Wordcount),
CompletedRows = e.Segments.Where(a => a.Status == Abr.Recorded).Count()
}).ToList();
that translates to:
SELECT t.ID, t.name, t.status, t.token, t.moduleID,
t.gender, t.total_rows AS TotalRows, t.total_words AS TotalWords,
(
SELECT SUM(a.wordcount)
FROM segments AS a
WHERE (a.status = 1) AND (t.ID = a.submoduleID)
) AS CompletedWords, (
SELECT COUNT(*)
FROM segments AS a0
WHERE (a0.status = 1) AND (t.ID = a0.submoduleID)
) AS CompletedRows
FROM submodules AS t
WHERE t.ID = #__id_0
as you can see to populate the CompletedWords and Rows ,
it runs 2 selects where the Status==1 , just that one its Sum , the other is Count() , how can i merge them into .1 select .
Please advice
I wouldn't worry too much about the SQL that EF generates unless you explicitly see a performance issue. Database engines are pretty adept at self-optimizing.
To answer your question though, this should work:
var query = _context.Submodules.Where(t => t.Id == id)
.Select(e => new {
Id = e.Id,
Name = e.Name,
Status = e.Status,
Token = e.Token,
ModuleId = e.ModuleId,
Gender = e.Gender,
TotalRows = e.TotalRows,
TotalWords = e.TotalWords,
ComletedSegments = e.Segments
.Where(a => a.Status == Abr.Recorded)
.Select(y => new { y.Wordcount })
.ToList()
}).ToList()
.Select(e => new Submodules{
Id = e.Id,
Name = e.Name,
Status = e.Status,
Token = e.Token,
ModuleId = e.ModuleId,
Gender = e.Gender,
TotalRows = e.TotalRows,
TotalWords = e.TotalWords,
CompletedWords = e.Sum(y=> y.Wordcount),
CompletedRows = e.Count()
}).ToList();
The first query selects an anonymous type to select the word count from applicable completed segments. The ToList() materializes this and executes the query. For the segments you could leave off the .Select() though this reduces the data selected to just the column we care about.
The second .Select() populates the view model by summing and counting the segments.

Why can't I select data from my LINQ sub query join?

I am trying to get data from 2 tables using a left join to a nested query. This allows me to get data from Item table but not the cart(nested query) table:
var q = from s in db.Items
join sub in (from c in db.Carts
where c.CartID == 1
group c by c.ItemID into g
select new
{
ItemID = g.Key,
Qty = g.Select(s => s.Qty)
}) on s.ItemID equals sub.ItemID into a
select new ItemViewModel
{
CategoryID = s.CategoryID,
Description = s.Description,
Price = s.Price,
**This being the issue------>>>>>>>** //Qty = a.Select(j => j.Qty),
ItemID = s.ItemID,
ItemName = s.ItemName
};
viewModel = q.ToList();
The query i am trying to acheive is:
select Items.*, Cart.Qty
from Items
left join (select ItemID, Qty from carts where CartID = 1 ) Cart
on Items.ItemID = Cart.ItemID
You can use GroupJoin with SelectMany for LEFT JOIN SQL Query and get the desired output.
var result = db.Items.GroupJoin(db.Carts.Where(x => x.CartID == 1), item => item.ItemID, cart => cart.ItemID,
(item, cart) => new { item, cart })
.SelectMany(x => x.cart.DefaultIfEmpty(), (it, ca) =>
{
return new ItemViewModel
{
ItemName = it.item.ItemName,
Price = it.item.Price,
ItemID = it.item.ItemID,
// ... .... ....
// Fill the required columns from it.Item property..
Qty = ca != null ? ca.Qty : 0
};
}).ToList();
EDIT: The LINQ version with SelectMany.
var result = from s in db.Items
join sub in (from c in db.Carts
where c.CartID == 1
select c)
on s.ItemID equals sub.ItemID into joined
from row in joined.DefaultIfEmpty()
select new ItemViewModel
{
CategoryID = s.CategoryID,
Description = s.Description,
Price = s.Price,
Qty = row != null ? row.Qty : 0,
ItemID = s.ItemID,
ItemName = s.ItemName
};
The C# Fiddle with sample data.
If I'm understanding correctly, and assuming that ItemViewModel.Qty property is just an int, the simplest form of the query you want is:
var q = from item in items
join cart in
(from cart in carts where cart.CartID == 1 select cart)
on item.ItemID equals cart.ItemID into itemCarts
select new ItemViewModel
{
ItemID = item.ItemID,
Qty = itemCarts.Sum(cart => cart.Qty)
};
If you want to only slightly modify/fix your query:
var q = from s in db.Items
join sub in (from c in db.Carts
where c.CartID == 1
group c by c.ItemID into g
select new
{
ItemID = g.Key,
Qty = g.Sum(s => s.Qty)
// or Qty = g.Select(s => s.Qty)
// and below: Qty = a.SelectMany(x => x.Qty).Sum()
})
on s.ItemID equals sub.ItemID into a
select new ItemViewModel
{
CategoryID = s.CategoryID,
Description = s.Description,
Price = s.Price,
Qty = a.Sum(x => x.Qty),
ItemID = s.ItemID,
ItemName = s.ItemName
};

Convert SQL(Row_Number) Query into Linq in MVC?

How to convert below SQL Query to Linq expression in C#(MVC)?
with cte
as
(
select *,rank() over(partition by LineClass order by LineClassId) as rownum
from MT_LineClass where ProjectId =1
)
select * from cte where rownum=1
var res = from tbl in MT_LineClass
group tbl.lineclassid by tbl.lineclass into g
select new { LineClass = g.Key, LineClassIds = g.ToList() };
You can use other than ToList operations to get the id (eq First, Max etc)
var lst = (from t in context.MT_LineClass
where t.ProjectId == projId
group t by t.LineClass into g
select new Mt_Lineclass
{
lineId = g.Select(t => t.LineClassId).FirstOrDefault(),
Lineclass = g.Select(t => t.LineClass).FirstOrDefault()
}).ToList();

Get additional attributes from group key

I have another problem with LINQ, So what I'm trying to do is create a list of best salesman. My table of sold_items, looks like this
[ID]
[Name]
[Price]
[Sold_date]
[Salesman_ID]
and the table of Salesmans
[ID]
[Login]
[First_name]
[Surname]
I want to sum all the prices, that each individual salesman sold and also have an option to group it by date for example, the best of this month, or this week
var query1 = from s in context.Archive
//join us in context.Users
//on s.Salesman.ID equals us.ID
group s by s.Salesman.ID
into g
orderby g.Sum(o => o.Price) descending
select new
{
Salesman_ID = g.Key,
Sale = g.Sum(o => o.Price),
};
that works fine, but I only get salesman_ID, how to add User First_name and Surname from Salesmans table?
You could do something like this:
var query1 =
from s in context.Archive
group s by s.Salesman into g
orderby g.Sum(o => o.Price) descending
select new
{
Salesman_ID = g.Key.ID,
Salesman_First_name = g.Key.First_name,
Salesman_Surname = g.Key.Surname,
Sale = g.Sum(o => o.Price),
};
And to limit it my a specific date, use something like this:
var query1 =
from s in context.Archive
where s.Sold_date >= minDate && s.Sold_date <= maxDate
group s by s.Salesman into g
orderby g.Sum(o => o.Price) descending
select new
{
Salesman_ID = g.Key.ID,
Salesman_First_name = g.Key.First_name,
Salesman_Surname = g.Key.Surname,
Sale = g.Sum(o => o.Price),
};
You can group by multiple columns (all the columns you want to retrieve)
var query1 = from s in context.Archive
//join us in context.Users
//on s.Salesman.ID equals us.ID
group s by new // group by multiple columns
{
s.Salesman.Id,
s.first_name,
s.surname,
} into g
orderby g.Sum(o => o.Price) descending
select new
{
Salesman_ID = g.Key,
FirstName = g.first_name,
Surname = g.surname,
Sale = g.Sum(o => o.Price),
};
Change the current Line to this.
select new { Salesman_ID = g.Key, Salesman_First_Name = g.Key.First_name };

Not counting null values from a linq LEFT OUTER JOIN query

I have this sql query that does exactly what i want but i need it in linq. It returns a few AVC rows and counts how many PersonAVCPermission that has status 1 linked to it
SELECT a.Id, a.Name, a.Address, COUNT(p.AVCID) AS Count
FROM AVC AS a
LEFT OUTER JOIN
(
SELECT PersonAVCPermission.AVCId
FROM PersonAVCPermission
WHERE PersonAVCPermission.Status = 1
) AS p
ON a.Id = p.AVCId
GROUP BY a.Id, a.Name, a.Address
I have this query in linq and it does the same thing except when there are no PersonAVCPermission it still counts 1
var yellows = odc.PersonAVCPermissions.Where(o => o.Status == (int)AVCStatus.Yellow);
var q = from a in odc.AVCs
from p in yellows.Where(o => o.AVCId == a.Id).DefaultIfEmpty()
group a by new { a.Id, a.Name, a.Address } into agroup
select new AVCListItem
{
Id = agroup.Key.Id,
Name = agroup.Key.Name,
Address = agroup.Key.Address,
Count = agroup.Count(o => o.Id != null)
};
Im guessing that with DefaultIfEmpty() it places null rows in the list that then gets counted so i try to exclude them with (o => o.Id != null) but it still counts everything as at least one
If i dont use DefaultIfEmpty() it skips the rows with count 0 completely
How can i exclude them or am i doing it completely wrong?
How about using .Any() and a Let?
var yellows = odc.PersonAVCPermissions.Where(o => o.Status == (int)AVCStatus.Yellow);
var q = from a in odc.AVCs
let Y = (from p in yellows.Where(o => o.AVCId == a.Id) select p).Any()
where Y == true
group a by new { a.Id, a.Name, a.Address } into agroup
select new AVCListItem
{
Id = agroup.Key.Id,
Name = agroup.Key.Name,
Address = agroup.Key.Address,
Count = agroup.Count(o => o.Id != null)
};
You don't need the join, nor the grouping:
var q = from a in odc.AVCs
select new AVCListItem
{
Id = a.Id,
Name = a.Name,
Address = a.Address,
Count = yellows.Where(o => o.AVCId == a.Id).Count()
};

Categories

Resources