I have a table:
Table { Id, Date, Number, Bool }
I need to group it by Number, select the row with max Date inside each group, and retrieve Id for each group. In the end I need to filter that to only have records that are !Bool. I am trying to do this with Linq Nhibernate.
This SQL seems to be doing what I want:
select Id from
(select MAX(Date) as Dt, Number as N from Table group by Number) t, Table table
where table.Date = t.Dt and table.Number = t.N and table.Bool = 0
but turns out NHibernate does not allow for subqueries to be in from. How do I write this with Linq Nhibernate?
It's also quite important for it to be efficient, so I would rather avoid having subqueries in select or where if they iterate over the whole set and (N+1) query problem.
The straightforward approach doesn't work either:
Session.Query<Table>().GroupBy(x => x.Number)
.Select(x => x.Where(y => y.Date == x.Max(z => z.Date)))...
Related
The example given in the blog has the following
from e in s.StudentCourseEnrollments where courseIDs.Contains(e.Course.CourseID) select e
The contains logic will not work when we are looking for an exact match. If a student has enrolled for 6 courses (ex : 1,2,3,4,5,6) and the requested list contains 5 (ex: 1,2,3,4,5) the query will return a match when it should not. The other way works well when the student has enrolled in a subset of the requested list.
Below solution works but need help to convert the below sql to LINQ (EF Core 3.0) ?
Create TABLE dbo.Enrollments (StudentId INT NOT NULL, CourseId INT NOT NULL)
insert into dbo.Enrollments values (1,1)
insert into dbo.Enrollments values (1,2)
insert into dbo.Enrollments values (1,3)
insert into dbo.Enrollments values (1,4)
insert into dbo.Enrollments values (1,5)
insert into dbo.Enrollments values (1,6)
DECLARE #TempCourses TABLE
(
CourseId INT
);
INSERT INTO #TempCourses (CourseId) VALUES (1), (2), (3),(4),(5);
SELECT t.StudentId
FROM
(
SELECT StudentId, cnt=COUNT(*)
FROM dbo.Enrollments
GROUP BY StudentId
) kc
INNER JOIN
(
SELECT cnt=COUNT(*)
FROM #TempCourses
) nc ON nc.cnt = kc.cnt
JOIN dbo.Enrollments t ON t.StudentId = kc.StudentId
JOIN #TempCourses n ON n.CourseId = t.CourseId
GROUP BY t.StudentId
HAVING COUNT(*) = MIN(nc.cnt);
drop table dbo.Enrollments
db<>Fiddle
I don't know about the SQL query, but the EF Core 3.0 LINQ query for the same task is something like this:
var matchIds = new[] { 1, 2, 3, 4, 5 }.AsEnumerable();
var query = dbContext.Students
.Where(s => s.Enrollments.All(e => matchIds.Contains(e.CourseId))
&& s.Enrollments.Count() == matchIds.Count());
The main matching job is done with All subquery. Unfortunately that's not enough for the case when related link records are more than the matching ids, so additional counts comparison solves that.
You can achieve it with a simple way like this, live demo here
Let's say that you've got the list of enrollments by this way
var enrollments = from s in dc.Students
from c in s.Courses
select new { StudentID = s.StudentID, CourseID = c.CourseID };
Then get the result by this way
var groupedEnrollment = enrollments.GroupBy(p => p.StudentId)
.Select(g => new
{
StudentId = g.Key,
Courses = g.Select(p => p.CourseId).ToArray()
});
var result = groupedEnrollment.Where(g =>
g.Courses.Length == courses.Length &&
g.Courses.Intersect(courses).Count() == courses.Length);
I'm trying to do the following sql query with linq but I couldn't understand it and it sounds confused.
Can I do it with linq or lambda?Can you help me write this query with linq?
int pp=0;
int typesquery=0;
string query;
if
{
a is change value.
}
I try:
SELECT TOP PERCENT NAME,SURNAME,DEPARTMENTID,DEPARTMENTNAME,ROW_NUMBER() over(PARTITION BY
NAME,SURNAME ORDER BY NAME,SURNAME) AS INF_NUMBER
FROM PERSON AS S
GROUP BY NAME,SURNAME,PERSONNUM,DEPARTMENTID,DEPARTMENTTYPE
THİS Linq query
var query= from x in PERSON
.OrderBy( x => x.NAME, x=>x.SURNAME)
.SELECT (x=>x.NAME,x=>x.SURNAME,x=>x.DEPARTMENTID,x=>x.DEPARTMENTNAME)
but not Couldn't combine batch way.
I have some Ids store in below variable:
List<int> Ids;
Now I want to get records based on above Ids but with same order as it is in above Ids.
For eg: Records are like this in database:
Employee:
Id
1
2
3
4
5
Now if Ids array holds Ids like this : 4,2,5,3,1 then I am trying to get records in this order order only:
Query:
var data = context.Employee.Where(t => Ids.Contains(t.Id)).ToList();
But above query is giving me output like it is in table:
Id
1
2
3
4
5
Expected output :
Id
4
2
5
3
1
Update:I have already tried this below solution but as this is entity framework it didn't work out:
var data = context.Employee.Where(t => Ids.Contains(t.Id))
.OrderBy(d => Ids.IndexOf(d.Id)).ToList();
For above solution to make it working I have to add to list :
var data = context.Employee.Where(t => Ids.Contains(t.Id)).ToList()
.OrderBy(d => Ids.IndexOf(d.Id)).ToList();
But I don't want to load data in memory and then filter out my record.
Since the order in which the data is returned when you do not specify an ORDER BY is not determined, you have to add an ORDER BY to indicate how you want it sorted. Unfortunately you have to order based on objects/values in-memory, and cannot use that to order in your SQL query.
Therefore, the best you can do is to order in-memory once the data is retrieved from the database.
var data = context.Employee
// Add a criteria that we only want the known ids
.Where(t => Ids.Contains(t.Id))
// Anything after this is done in-memory instead of by the database
.AsEnumerable()
// Sort the results, in-memory
.OrderBy(d => Ids.IndexOf(d.Id))
// Materialize into a list
.ToList();
Without stored procedures you can use Union and ?: that are both canonical functions.
I can't immagine other ways.
?:
You can use it to assign a weigth to each id value then order by the weigth. Also, you have to generate ?: using dynamic linq.
What is the equivalent of "CASE WHEN THEN" (T-SQL) with Entity Framework?
Dynamically generate LINQ queries
Union
I think this is the more simple way to obtain it. In this case you can add a Where/Union for each Id.
EDIT 1
About using Union you can use code similar to this
IQueryable<Foo> query = context.Foos.AsQueryable();
List<int> Ids = new List<int>();
Ids.AddRange(new[] {3,2,1});
bool first = true;
foreach (int id in Ids)
{
if (first)
{
query = query.Where(_ => _.FooId == id);
first = false;
}
else
{
query = query.Union(context.Foos.Where(_ => _.FooId == id));
}
}
var results = query.ToList();
This generate the followiong query
SELECT
[Distinct2].[C1] AS [C1]
FROM ( SELECT DISTINCT
[UnionAll2].[C1] AS [C1]
FROM (SELECT
[Distinct1].[C1] AS [C1]
FROM ( SELECT DISTINCT
[UnionAll1].[FooId] AS [C1]
FROM (SELECT
[Extent1].[FooId] AS [FooId]
FROM [Foos] AS [Extent1]
WHERE [Extent1].[FooId] = #p__linq__0
UNION ALL
SELECT
[Extent2].[FooId] AS [FooId]
FROM [Foos] AS [Extent2]
WHERE [Extent2].[FooId] = #p__linq__1) AS [UnionAll1]
) AS [Distinct1]
UNION ALL
SELECT
[Extent3].[FooId] AS [FooId]
FROM [Foos] AS [Extent3]
WHERE [Extent3].[FooId] = #p__linq__2) AS [UnionAll2]
) AS [Distinct2]
p__linq__0 = 3
p__linq__1 = 2
p__linq__2 = 1
EDIT 2
I think the best approach is in memory approach because it has the same network load, EF does not generate the ugly query that could not work on databases different from SQL Server and code is more readable. In your particular application could be that union/where is better. So, generally I would suggest you to try memory approach then, if you have [performance] issues, you can check if union/where is better.
Is there any way to write a linq query to result in :
select Count(Id) from tbl1
because
tbl1.Select(q=>q.Id).Count()
doesn't translate to the result that I want
update :
it returns :
select count(*) from tbl1
Update after answer :
I tested the scenario with more than 21,000,000
Is there any way to write a linq query to result in.
No. First thing is to understad what you need, for sample, in T-SQL, you can use:
COUNT(*) will counts the rows in your table
COUNT(column) will counts the entries in a column - ignoring null values.
If you need to count how many rows you have, just use
var total = tbl1.Count();
If you need to see how many entities you have where a specific column is not null, then use a filter overloads of Count method.
var total = tbl1.Count(x => x.Id != null);
No, it is not possible. There is not difference realted with performance using Count(*) or ´Count(Id), even more if yourId` is the primary key.
I did an experiment with a table here with more than one million tuples. See the executioon plan of both queries. The first one is the select count(*) and second one is select count(id). The id is the primary key (sorry the results are in portuguese-brazil):
Using count(field) in sql counts all non-null values. In linq, you can say:
tbl1.Where(q => q.Id != null).Count();
or simply:
tbl1.Count(q => q.Id != null);
A possibility to get
select Count(Id) from tbl1
would be
tbl1.Where(q => q.Id != null).Select(x => x.Id).Distinct().Count();
The above Where is there to avoid null values. If you want them to also be counted, the Where needs to be eliminated and the Select adjusted to deal with null entries.
Additionally if you don't want to count just distinct values then the Select and Distinct parts can be disregarded.
How to get Total row Count & the records from the below query?
Contracts cont = db.contracts.SqlQuery("SELECT TOP (20) *
FROM (SELECT
ROW_NUMBER() OVER (ORDER BY dbo.Contracts.expDate desc) As RowID,
dbo.Contracts.*,
TotalRows=COUNT(*) OVER()
FROM dbo.Contracts
Where dbo.Contracts.cancelled = 1) as temp
WHERE temp.RowID >" + 20).ToList();
I'm getting the records but don't know how to get the Total row Count.
Can Any body suggest best method to get the Total row Count & the records from the above query?
Your code won't work because you're returning a list of Contracts AND a count, but you're trying to assign it to only a Contracts. You need to project to an anonymous type, or create a custom type to project to that includes both the count and a collection of Contracts.
Why do you insist on using a sql query? This should do the same thing.
var contracts = (from x in db.contacts where x.cancelled == 1
orderby x.expDate descending
select new { Count=x.Count(), Records=x.Skip(20).Take(20) }).ToList();
Unless you want the total rows without the where clause, in which case it would be:
var contracts = (from x in db.contacts orderby x.expDate descending
select new { Count=x.Count(),
Records=x.Where(y => y.canceled == 1).Skip(20).Take(20) }).ToList();