Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Query: Bulk Delete on result of First family in projection #28525

Open
Tracked by #30173
smitpatel opened this issue Jul 26, 2022 · 3 comments
Open
Tracked by #30173

Query: Bulk Delete on result of First family in projection #28525

smitpatel opened this issue Jul 26, 2022 · 3 comments

Comments

@smitpatel
Copy link
Member

    [ConditionalTheory]
    [MemberData(nameof(IsAsyncData))]
    public virtual Task Delete_GroupBy_Where_Select(bool async)
        => AssertDelete(
            async,
            ss => ss.Set<OrderDetail>()
                    .GroupBy(od => od.OrderID)
                    .Where(g => g.Count() > 5)
                    .Select(g => g.First()),
            rowsAffectedCount: 284);

This causes the shaper to be a projection which is entity. Even if we apply projection it causes a subquery in SelectExpression. We can come up with some SQL to represent this (though our current structure is not supported in delete operation and not easy way to convert to subquery)

@smitpatel
Copy link
Member Author

The structure here is -> shaped query has projection binding which is another shaped query with entity projection. So ideally we can translate using subquery form.

@smitpatel
Copy link
Member Author

Work-around

ss.Set<Animal>().Where(e => ss.Set<Animal>().GroupBy(e => e.CountryId)
                                                .Where(g => g.Count() < 3).Select(g => g.First()).Any(i => i == e).ExecuteDelete();

Using subquery form yourself. It should work for non-composite PK scenario.

@MichalLechowski
Copy link

MichalLechowski commented Nov 16, 2022

I probably have a similar issue with bulk delete (EF Core 7, .NET 6).
The case is:

for every unique column pair Key + Name (none is PK column) remove all rows except the latest 10.
Ideal case for a bulk delete, database clean-up job.

So the query is something like:

var query = 
from uniquePairs in _dbContext.Logs.Select(o => new { o.Key, o.Name }).Distinct()
from log in _dbContext.Logs.Where(log => log.Key == uniquePairs .Key && log.Name == uniquePairs .Name)
                                                                   .OrderByDescending(log => log.Timestamp)
                                                                   .Skip(10)
select log;

When executing it with query.ExecuteDeleteAsync(), the result is:

System.ArgumentOutOfRangeException: Index was out of range. Must be non-negative and less than the size of the collection. (Parameter 'index')
at Microsoft.EntityFrameworkCore.Query.SqlExpressions.SelectExpression.GetProjection(ProjectionBindingExpression projectionBindingExpression)

when I try the other way:

var query = 
_dbContext.Logs
.GroupBy(x => new { x.Key, x.Name }, (k, g) => g.OrderByDescending(x => x.Timestamp).Skip(10))
.Select(x => x);

and then query.ExecuteDeleteAsync(), the result is:

.ExecuteDelete()' could not be translated. Additional information: The operation 'ExecuteDelete' requires an entity type which corresponds to the database table to be modified. The current operation is being applied on a non-entity projection. Remove any projection to non-entity types.

Obviously both of these queries work just fine with ToList and I get the exact collection of rows I wanna remove, the point is I don't wanna load them into memory at all.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants