Entity Framework grouping by column from join - c#

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

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

EF Core LINQ query failing due to limitation?

I am trying to do quite a simple group by, and sum, with EF Core 3.0
However am getting a strange error:
System.InvalidOperationException: 'Processing of the LINQ expression
'AsQueryable((Unhandled parameter:
y).TransactionLines)' by 'NavigationExpandingExpressionVisitor'
failed. This may indicate either a bug or a limitation in EF Core.
var creditBalances = await context.Transaction
.Include(x => x.TransactionLines)
.Include(x=>x.CreditAccount)
.Where(x => x.CreditAccount.UserAccount.Id == userAccount.Id)
.GroupBy(x => new
{
x.CreditAccount.ExternalId
})
.Select(x => new
{
x.Key.ExternalId,
amount = x.Sum(y => y.TransactionLines.Sum(z => z.Amount))
})
.ToListAsync();
I'm battling to see where an issue can arise, so not even sure where to start. I am trying to get a sum of all the transaction amounts (Which is a Sum of all the TransactionLines for each transaction - i.e. A Transaction amount is made of the lines associated to it).
I then sum up all the transactions, grouping by then CreditAccount ID.
The line, Unhandled parameter: y is worrying. Maybe my grouping and summing is out.
So start at the TransactionLines level and this is as simple as:
var q = from c in context.TransactionLines
where c.Transaction.CreditAccount.UserAccount.Id == userAccount.Id
group c by c.Transaction.CreditAccount.ExternalId into g
select new
{
ExternalId = g.Key,
Amount = g.Sum(x => x.Amount)
};
var creditBalances = await q.ToListAsync();
( You don't need any Include() since you're not returning an Entity with related data. You're projecting a custom data shape. )
Which translates to:
SELECT [c].[ExternalId], SUM([t].[Amount]) AS [Amount]
FROM [TransactionLines] AS [t]
LEFT JOIN [Transaction] AS [t0] ON [t].[TransactionId] = [t0].[Id]
LEFT JOIN [CreditAccounts] AS [c] ON [t0].[CreditAccountId] = [c].[Id]
LEFT JOIN [UserAccount] AS [u] ON [c].[UserAccountId] = [u].[Id]
WHERE [u].[Id] = #__userAccount_Id_0
GROUP BY [c].[ExternalId]

Linq Query Group By Multiple Columns Having Count > 1

I have a query I know how to do in SQL but struggling to figure out the LINQ query. Here is the SQL.
SELECT ordNo, tranNo, COUNT(distinct custNo)
FROM orders
GROUP BY ordNo, tranNo
HAVING COUNT(distinct custNo) > 1
I don't feel like this is the same question that I see you marked as a duplicate. The linked question only groups on a single property. I've lost track of the Linq queries I've tried but here is one.
var countList = from o in orders
group o by new {o.orderNo, o.tranNo, o.custNo}
into grp
where grp.Key.custNo.Distinct().Count() > 1
select grp;
I tried the suggestion below but like someone commented you can't access the custNo property.
Just spitballing since I don't know the table structure.
context.orders
.GroupBy(o => new { o.ordNo, o.tranNo, o.custNo })
.Where(o => o.custNo.Distinct().Count() > 1)
.Select(o => new {
ordNo = o.ordNo,
tranNo = o.tranNo
});

Relevant linq query for the SQL

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.

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