Relevant linq query for the SQL - c#

My query initially was
select *
from Personalization_Mapping
The relevant LINQ query was
List<Personalization_Mapping> list = _appDbContext.Personalization_Mapping.OrderBy(s => s.ID).ToList();
Now I need the relevant unique columns for which, I changed the SQL to
SELECT *
FROM
(SELECT
*,
ROW_NUMBER() OVER(PARTITION BY CustomerID ORDER BY ID DESC) rn
FROM
Personalization_Mapping) a
WHERE
rn = 1
Could any one help me finding the equivalent LINQ query?
Thanks in advance.

var result = _appDbContext.Personalization_Mapping.OrderByDescending(x => x.ID)
.GroupBy(x => x.CustomerID)
.Select(g => new {g, count= g.Count()})
.SelectMany(t => t.g.Select(b => b)
.Zip(Enumerable.Range(1,t.count), (j,i) => new {j.Property1, j.Property2, rn = i}));
Replace Property1, Property2 with actual entities.
Now apply filter for row 1
result.Where(x => x.rn == 1);
Hope this helps.

Related

Linq query for top parents by number of childs, including this number

In my model, there are entities Article and Tag in many-to-many relation through table ArticleTag.
I want to select "trending tags" - tags with most articles in last X days, and I want this count too.
Basically, I need help creating EF Linq query equivalent to this SQL query, with ideal result being Dictionary<Tag, int>
SELECT TOP 50
t.Id, t.Name, count(*)
FROM ArticleTag at
JOIN Article a ON a.Id = at.ArticleId
JOIN Tag t ON t.Id = at.TagId
WHERE a.DateCreated > '2019-10-01'
GROUP BY t.Id, t.Name
ORDER BY count(*) DESC
Can this be done without having ArticleTag as DbSet in DbContext (since it is not really an entity, and I dont need it besides this query).
You have to use navigation properties for this query and do not need to know anything about ArticleTag table.
var query =
from a in ctx.Articles
from t in a.Tags
where a.DateCreated > someDate
group t by new { t.Id, t.Name } into g
orderby g.Count() descending
select new
{
g.Key.Id,
g.Key.Name,
Count = g.Count()
};
var result = query
.Take(50)
.ToDictionary(x => new Tag { Id = x.Id, Name = x.Name }, x => x.Count);

Performance is affected when EF uses ROW_NUMBER() for ordering and paging

I have an Entity view in my database and an Entity class in code that represents the view. To get result for a certain page I am doing this:
var result = await dbContext.Entity
.OrderByDescending(e => e.CreatedOn)
.ThenByDescending(e => e.Id)
.Skip((currentPage - 1) * itemsPerPage)
.Take(itemsPerPage)
.ToListAsync();
For the first page of 50 items (currentPage = 1, itemsPerPage = 50) EF generates the following:
SELECT
[Extent1].[OrderId] AS [Id],
[Extent1].[Number] AS [CreatedOn],
...
FROM [dbo].[Entity] AS [Extent1]
ORDER BY row_number() OVER (ORDER BY [Extent1].[CreatedOn] DESC, [Extent1].[Id] DESC)
OFFSET 0 ROWS FETCH NEXT 50 ROWS ONLY
The problem is that the query is executing quite long. And that's why I tried without row_number() and it was faster, and I got same results:
SELECT
[Extent1].[OrderId] AS [Id],
[Extent1].[Number] AS [CreatedOn],
...
FROM [dbo].[Entity] AS [Extent1]
ORDER BY [Extent1].[CreatedOn] DESC, [Extent1].[Id] DESC
OFFSET 0 ROWS FETCH NEXT 50 ROWS ONLY
So, my questions are:
1) Why is row_number() used here in general?
2) Is there any why force EF not to use it in this query?
UPDATE: Adding clustered index on the table which view is using helped to improve performance. Thanks!
See if this is faster :
var result = await dbContext.Entity
.OrderByDescending(e => e.CreatedOn)
.ThenByDescending(e => e.Id)
.Select((x,i) => new {index = i, item = x})
.GroupBy(x => x.index / itemsPerPage)
.Select(x => x.Select(y => y.item).ToList())
.ToListAsync();

Entity Framework grouping by column from join

I have the next query:
select VisitLines.ProcedureId, COUNT(DISTINCT VisitLines.VisitId) as nt
from Visits
LEFT JOIN VisitLines ON Visits.Id = VisitLines.VisitId
WHERE Visits.VisitStatusId = 1 AND Visits.IsActive = 1 AND VisitLines.IsActive = 1
GROUP BY VisitLines.ProcedureId
Main question: Does ability exists to grouping by column from join using linq ? I'm wondering how to do it using 'collection' column.
Is it possible to force EF to generate COUNT(DISTINCT column) ? IQueryable.GroupBy.Select(x => x.Select(n => n.Number).Distinct().Count()) generate query with few subqueries which much slower then COUNT(DISTINCT )
I found. Need to use SelectMany with second parameter resultSelector:
dbContext.Visits.Where(x => x.IsActive)
.SelectMany(x => x.VisitLines, (v, vl) => new
{
v.Id,
vl.ProcedureId
})
.GroupBy(x => x.ProcedureId)
.Select(x => new
{
Id = x.Key,
VisitCount = x.Count()
}).ToArray();
It generates the desired SQL, but with exception that I need distinct count by visit.
And if I change VisitCount = x.Distinct().Count() then EF generates a query with few subqueries again. But the main issue resolved

Delete all but top 10 for every type Entity Framework

Let's say that I have a table like:
Id Name Category CreatedDate
1 test test 10-10-2015
2 test1 test1 10-10-2015
...
Now, I would like to delete all rows and leave only the top 10 from all categories (by top 10 I mean the 10 newest according to createdDate).
Using raw SQL, it would be like:
DELETE FROM [Product]
WHERE id NOT IN
(
SELECT id FROM
(
SELECT id, RANK() OVER(PARTITION BY Category ORDER BY createdDate DESC) num
FROM [Product]
) X
WHERE num <= 10
How is this done when using the DbContext in Entity Framework?
// GET all products
var list = ctx.Products.ToList();
// GROUP by category, ORDER by date descending, SKIP 10 rows by category
var groupByListToRemove = list.GroupBy(x => x.Category)
.Select(x => x.OrderByDescending(y => y.CreatedDate)
.Skip(10).ToList());
// SELECT all data to remove
var listToRemove = groupByListToRemove.SelectMany(x => x);
// Have fun!
ctx.Products.RemoveRange(listToRemove);
Guessing it will take a whil if you have a lot of data but.
var oldItems = efContext.Products
.GroupBy(x => x.Category,
(c,p) => p.OrderByDescending(x => p.createdDate).Skip(10))
.SelectMany(p => p);
efContext.Products.RemoveRange(oldItems);
Will do the trick
(Written in notepad)

Sql is fast but when converted to linq it's slow

This is fast
SELECT Foo,
count(*)
FROM
(SELECT Foo
FROM MyTable
GROUP BY Foo,
Bar,
Baz) AS Subquery
GROUP BY Foo
This is fast
var query = from fooGrp in
(from rv in _myRepository.AsQueryable()
group rv by new {rv.Foo, rv.Bar, rv.Baz}
into grp
select grp)
group fooGrp by fooGrp.Key.Foo
into grp2
select new {grp2.Key, Count = grp2.Count()};
query.ToDictionary(x => x.Key, x => x.Count);
This is slow, really slow!
_myRepository.AsQueryable()
.GroupBy(x => new { x.Foo, x.Bar, x.Baz })
.GroupBy(x => x.Key.Foo)
.ToDictionary(x => x.Key, x => x.Count());
I don't understand :(
What is the difference between the two linq expressions? They both return the expected result set.
The generated SQL for the first expression (fast) is:
SELECT
1 AS [C1],
[GroupBy1].[K1] AS [Foo],
[GroupBy1].[A1] AS [C2]
FROM ( SELECT
[Distinct1].[Foo] AS [K1],
COUNT(1) AS [A1]
FROM ( SELECT DISTINCT
[Extent1].[Foo] AS [Foo],
[Extent1].[Bar] AS [Bar],
[Extent1].[Baz] AS [Baz]
FROM [dbo].[MyTable] AS [Extent1]
) AS [Distinct1]
GROUP BY [Distinct1].[Foo]
) AS [GroupBy1]
The generated SQL for the second expression (slow) is:
Is so long that it exceeds the character limit of this post, so cannot post it :/
So turns out that the LINQ expressions are not identical.
The correct linq expression is:
_myRepository.AsQueryable()
.GroupBy(x => new {x.Foo, x.Bar, x.Baz})
.GroupBy(x => x.Key.Foo)
.Select(x => new {x.Key, Count = x.Count()})
.ToDictionary(x => x.Key, x => x.Count);
I was missing a select, which I didn't expect because in normal SQL you can only select on columns that are in the group by clause. But LINQ does all kinds of magic to get the rest of the columns included, unless you limit it in the select.

Categories