How can I get this result using linq? - c#

I currently have two tables: a table of Users and a table of Votes. The Votes table stores a record that contains the Id of a user.
As a simple example, let's say my Users table has the following fields:
UserId, Name, Age, Gender
And my Votes table has:
UserId, DateCreated
What I want to do is select the top 10 users that have the highest votes.
How can I do this?
Thanks

Context.Users.OrderByDescending(x => x.Votes.Count()).Take(10)

Assuming you've got navigation properties set up between your tables, you could do this:
var results = db.Users.OrderByDescending(u => u.Votes.Count()).Take(10);
This will find the top 10 users with the greatest number of votes.

If you don't have foreign keys/property mapped:
var query = (from u in db.Users
join v in (
from v in db.Votes
group v by v.UserId into grp
select new {
UserId = grp.Key,
Count = grp.Count()
}) on u.Id equals v.UserId into l_vset
from l_v in l_vset.DefaultIfEmpty()
orderby l_v == null ? 0 : l_v.Count descending
select new {
User = u,
Votes = l_v == null ? 0 : l_v.Count
}).Take(10);

Related

group by linq to entity query to get one record having latest timestamp by joining tables

There are two tables and using linq query to get records. From second table, there can be multiple rows corresponding to first table with date timestamp... based on below query, I am getting all records, but is there a way we can get the row from second table which has latest timestamp ?
Table Parent
ID Name
1 M
2 N
3 O
4 P
5 Q
Table Child
Id fkID DateTime
1 2 01/12/2021 09:12:20
2 2 01/12/2021 09:13:20
3 2 01/12/2021 09:14:20
4 2 01/12/2021 09:15:20
5 2 01/12/2021 **09:16:20**
Linq query:
from p in Parent
join c in Child on p.id equals c.fkId into cJoin
from cJoin in cJoin.DefaultIfEmpty()
select new TempResponse
{
Id = p.Id,
Name = p.Name,
Date = c.Date
}
I am getting 10 records using above query but just need 5 records i.e. from child table instead of all 5 records, we need a record that has latest time stamp
**expected output**
1 M
2 N 01/12/2021 09:16:20
this record is 5'th record from child table because this one has latest date time stamp
( latest record )
3 O
4 P
5 Q
Is there any way we can use group by and get the record that has latest time stamp from second table ?
Assuming you have defined navigation properties for the FK, I would use a query like;
dbContext.Child.Where(c => c.Date == c.Parent.Child.Max(c2 => c2.Date))
I believe you can use:
var ans = from p in Parent
join cmax in (
from c in Child
group c by c.fkId into cg
select cg.OrderByDescending(c => c.Date).FirstOrDefault())
on p.Id equals cmax.fkId into cJoin
from c in cJoin.DefaultIfEmpty()
select new TempResponse {
Id = p.Id,
Name = p.Name,
Date = c != null ? c.Date : null
};
Note that the order of results seems to vary on SQL Server unless you add another orderby clause before the select to force an order.

EF Core 3.0 - Convert SQL to LINQ

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

can't get the right output from a "group by"

Trying to get the list of product Id's with amount of feedbacks each of them has
Need to count only feedbacks that were given to the product as belonging to specific category (see the SQL script: categoryId == 50). Product can belong to multiple categories.
productId, cnt
14, 0
16, 0
15, 1
09, 2
10, 2
EDIT:
I came up with the LINQ to SQL below, to recreate the logic I expressed trough SQL script below. But the result is not the same. Can't get what's different with LINQ's logic from SQL script's?
LINQ to Sql:
var result =
(
from pcl in db.productCategoryLookup
join p in db.products on pcl.productId equals p.productId
join f in db.feedbacks on p.productId equals f.feedbackId into bb
from g in bb.DefaultIfEmpty()
where (pcl.categoryId == 50)
group p by p.productId into grp
select new
{
productId = grp.Key,
cnt = grp.Count()
} into res1
orderby res1.cnt
select new
{
producetId = res1.productId,
cnt = res1.cnt
}
)
.Take(5)
.ToList();
SQL Script:
SELECT TOP 5
p.productId,
COUNT(f.feedbackId)
FROM ProductCategoryLookup pcl
INNER JOIN Product p
ON p.productId = pcl.productId
LEFT JOIN Feedbacks f
ON f.productId = p.productId
WHERE
pcl.categoryId = 50
GROUP BY
p.productId
ORDER BY
COUNT(f.feedbackId)
Tables:
**Products** table
productId PK
productName string
**ProductCategoryLookup** table. Connects products with Category.
One product can have multiple categories and the feedback goes
for given product in given category.
productId FK
categoryId FK
. . .
**Feedbacks** table. Each product+category pair gets zero or more feedbacks.
feedbackId PK
productId FK
categoryId FK
. . .
**Category** table.
categoryId pk
name
To the answer from AD.Net I added join after 'from' for the productCategoryLookup, and where clause. Works now! Thanks.
(from p in context.Products
select new {Product = p, Count = p.Feedbacks.Any() ? p.Feedbacks.Count() : 0})
.OrderBy(p=>p.Count)
.Take(5)
.Select(p=>p.Product)
.ToList()
Try following query. It may help you.
db.Products.Select(n => new { n.ProductID, count = n.Feedbacks.Count})
.OrderBy(m=>m.count).Take(5).ToList();

Fluent NHibernate: create mapping that should let me create a query with two inner joins to the same table

I have an Items table with ItemID as primary key
I have a StoreItems table which has composite primary keys StoreID and ItemID. The ItemID uses a foreign key reference to the Items table.
I need to create a query which looks like this (goal is to select items which exist in two stores)
select * from Items i
inner join storeitems s1 on s1.ItemID = i.ItemID and s1.StoreID = myfirststoreid
inner join storeitems s2 on s2.ItemID = i.ItemID and s2.StoreID = mysecondstoreid
How can I do the fluent mapping to achieve this?
untested. it selects the ids first then gets the items. It might be possible to combine into one statement:
var itemsIdsInBothStores =
from i in session.Query<Item>()
from s in i.Stores
where s.Id == myfirststoreid || s.Id == mysecondstoreid
group i by i.Id into g
where g.Count() = 2
select g.Key;
var itemsInBothStores = ´session.QueryOver<Item>().WhereRestrictionOn(i => i.Id).In(itemsIdsInBothStores.ToList());

C# Linq finding value

I want to return the depart number that is not found Employee Table by comparing Department table.
Person Table
ID name salary job commision DeptID
--------------------------------------------------------------
P001 Jon 2000 Manager NULL 1
P002 Skeet 1000 Salesman 2000 1
P003 James 2340 Developer NULL 2
P004 greed 4500 Developer NULL 2
P005 Joel 1330 Salesman 1200 1
P006 Deol 5000 Architect NULL 2
Department Table
DeptID DeptName
1 Management
2 Software
3 ERP
SQL
select DeptId from dept
where deptId not in (select deptid from person)
When i try to execute the below code
LINQ
var qry = from n in context.Persons
where n.DeptID !=
(from m in context.Depts select m.DeptId)
select new { DeptID = n.DeptID };
I receive the following error
Operator '!=' cannot be applied to operands of type 'int?' and 'System.Linq.IQueryable'
var qry = from n in context.Persons
where n.DeptID !=
(from m in context.Depts select m.DeptId).FirstOrDefault()
select new { DeptID = n.DeptID };
You are trying to compare DeptID with a collection 1 or more department Ids. Even if there would only logically be one result for a DeptID, syntactically you need to specify that you want the first hit.
Suggested rephrasing:
var q = from m in context.Depts
where
!context.Persons.Select(p => p.DeptID).Contains(m.DeptID)
select new { DeptID = m.DeptID };
It sounds that your DeptID field in SQL is set to allow nulls. In that case you'd probably want something along the lines of this:
var qry = from n in context.Persons
where n.DeptID.Value !=
(from m in context.Depts select m.DeptId)
select new { DeptID = n.DeptID.Value };
I think it should be something like that. I tried to get a list of DeptID's first and then implement a NOT IN with contains :
var deptIDs = context.Persons
.Where( p => !context.Depts
.Select(d => new {DeptID = d.DeptID})
.Contains( p.DeptID )
)
.Select( p => new { DeptID = n.DeptID } );

Categories

Resources