Perform SELECT statement on another SELECT using LINQ - c#

In my .NET Core app, I have the following in LINQ:
return await db.ApplicationUsers.Where(u=>u.Name.Contains(name) && !u.Deleted && u.AppearInSearch)
.OrderByDescending(u => u.Verified)
.Skip(page * recordsInPage)
.Take(recordsInPage)
.Select(u => new UserSearchResult()
{
Name = u.Name,
Verified = u.Verified,
PhotoURL = u.PhotoURL,
UserID = u.Id,
Subdomain = u.Subdomain
}).ToListAsync();
which translates to the following SQL:
SELECT [t].[Name], [t].[Verified],
[t].[PhotoURL], [t].[Id],
[t].[Subdomain]
FROM
(SELECT [u0].*
FROM [AspNetUsers] AS [u0]
WHERE (((CHARINDEX('khaled', [u0].[Name]) > 0) OR ('khaled' = N''))
AND ([u0].[Deleted] = 0))
AND ([u0].[AppearInSearch] = 1)
ORDER BY [u0].[Verified] DESC
OFFSET 10 ROWS
FETCH NEXT 10 ROWS ONLY ) AS [t]
But due to performance issues, Microsoft support suggested that I only query columns of fixed length (not varchar(max)). I was asked to change the SQL query to:
SELECT [t].[Name], [t].[Verified],
[t].[PhotoURL] , [t].[Id], [t].[Subdomain]
FROM
(Select u0.Name, u0.Verified, u0.PhotoURL, u0.id, u0.Subdomain,
u0.Deleted, u0.AppearInSearch FROM [AspNetUsers] ) As [u0]
WHERE (((CHARINDEX('khaled', [u0].[Name]) > 0) OR ('khaled' = N''))
AND ([u0].[Deleted] = 0))
AND ([u0].[AppearInSearch] = 1)
ORDER BY [u0].[Verified] DESC
OFFSET 10 ROWS FETCH NEXT 10 ROWS ONLY ) AS [t]
Which is a SELECT on another SELECT. Unfortunately I don't know how to do it in LINQ. Can someone please guide me how to make the second SQL query in LINQ?
Thank you

First build the inner select:
var s1 = from u in db.AspNetUsers
select new UserSearchResult
{
Name = u.Name,
Verified = u.Verified,
PhotoURL = u.PhotoURL,
UserID = u.Id,
Subdomain = u.Subdomain
};
then use it in the outer select:
return await (from u1 in s1
where u.Name.Contains(name) && !u.Deleted && u.AppearInSearch
orderby u.Verified
select u1)
.Skip(page * recordsInPage)
.Take(recordsInPage);

When you make the output of select query .ToList(), the output is a list again. Over this list you can apply another .Select(). So, as in your previous query, you can do it as:
return await db.ApplicationUsers.Where(u=>u.Name.Contains(name) && !u.Deleted && u.AppearInSearch)
.OrderByDescending(u => u.Verified)
.Skip(page * recordsInPage)
.Take(recordsInPage)
.Select(u => new UserSearchResult()
{
Name = u.Name,
Verified = u.Verified,
PhotoURL = u.PhotoURL,
UserID = u.Id,
Subdomain = u.Subdomain
})
.ToList()
.Select(<your new query>)
.ToListAsync();

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.

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();

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()
};

LINQ query with sub where clause

I have a SQL query :
SELECT [Paypoint]
,[Department]
,[EmployeeCode]
,[Gender]
,[EmployeeTitle]
,[Initials]
,[Surname]
,[ItemsIssuedDate]
,[ItemsIssuedStockNumber]
FROM [MyTable] AS a
WHERE
(
[ItemsIssuedDate] = ( SELECT max([ItemsIssuedDate])
FROM [MyTable] AS b
WHERE a.[Paypoint] = b.[Paypoint]
AND a.[Department] = b.[Department]
AND a.[EmployeeCode] = b.[EmployeeCode]
AND a.[Gender] = b.[Gender]
AND a.[Surname] = b.[Surname]
)
How would one get the comparitive LINQ query ? I cannot use the SQL query as the Data is already in a DataSet, and now needs to be modified further...
I have attempted, but this does not work :
var query = from a in excelTable
where
(
from c in excelTable
group c by new
{
c.Paypoint,
c.EmployeeCode
} into g
where string.Compare(a.Paypoint, g.Key.Paypoint) == 0 && string.Compare(a.EmployeeCode, g.Key.Paypoint) == 0
select g.Key.Paypoint
)
select a;
var query = from a in MyTable
group a by new {
a.Paypoint,
a.Department,
a.EmployeeCode,
a.Gender,
a.Surname
} into g
select g.OrderByDescending(x => x.ItemsIssuedDate)
//.Select(x => new { required properties })
.First();
You can also select anonymous object with required fields only. Up to you.
Your most direct with the SQL query will be:
var query =
from a in excelTable
let maxIssueDate =
(from b in excelTable
where a.Paypoint == b.Paypoint &&
a.Department == b.Department &&
a.EmployeeCode == b.EmployeeCode &&
a.Gender == b.Gender &&
a.Surname == b.Surname
select b.ItemsIssueDate).Max()
where a.ItemsIssueDate == maxIssueDate
select a;

How can I use over by partition in LINQ?

I'm confused about how to change this query to LINQ
select
CONTENT
from
(
select
CONTENT,
CAM_ID,
max(CAM_ID) over (partition by DOCUMENT_ID) MAX_ID
from
T_CAM_REVISION
where
DOCUMENT_ID = '101'
)
where
CAM_ID = MAX_ID
so I can get a single value of content.
There is no way to do max(...) over (...) in LINQ. Here is an equivalent query:
var maxCamID =
context.T_CAM_REVISION
.Where(rev => rev.DOCUMENT_ID == "101")
.Max(rev => rev.CAM_ID);
var query =
from rev in context.T_CAM_REVISION
where rev.CAM_ID == maxCamID
where rev.DOCUMENT_ID == "101"
select rev.CONTENT;
If you want only a single result:
var result =
context.T_CAM_REVISION
.First(rev => rev.CAM_ID == maxCamID
&& rev.DOCUMENT_ID == "101")
.CONTENT;

Categories

Resources