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

EF Core 2.0.2 generates multiple query for a subquery #11677

Closed
zulander1 opened this issue Apr 14, 2018 · 10 comments
Closed

EF Core 2.0.2 generates multiple query for a subquery #11677

zulander1 opened this issue Apr 14, 2018 · 10 comments
Labels
closed-no-further-action The issue is closed and no further action is planned.

Comments

@zulander1
Copy link

zulander1 commented Apr 14, 2018

I am not sure if it's a bug not but I am getting some major performance issue. EF Core is generating multiple query for a simple statement.

This is the query:

        var query = (from task in db.Tasks
                     let LastStatus = db.TaskStatus.Where(x => x.TaskId == task.Id && x.Deleted == false).OrderByDescending(x => x.CreatedDate).Select(x => x.Status.Name).First()
                     select new
                     {
                         task.Id,
                         LastStatus
                     }).ToList();

Expected resuts:

SELECT [t0].[Id], (
    SELECT TOP (1) [t2].[Name]
    FROM [TaskStatus] AS [t1]
    INNER JOIN [Status] AS [t2] ON [t2].[Id] = [t1].[StatusId]
    WHERE ([t1].[TaskId] = [t0].[Id]) AND (NOT ([t1].[Deleted] = 1))
    ORDER BY [t1].[CreatedDate] DESC
    ) AS [LastStatus]
FROM [Tasks] AS [t0]

However EF is generating:

SELECT TOP(1) [x.Status0].[Name]
FROM [TaskStatus] AS [x0]
INNER JOIN [Status] AS [x.Status0] ON [x0].[StatusId] = [x.Status0].[Id]
WHERE ([x0].[TaskId] = @_outer_Id) AND ([x0].[Deleted] = 0)
ORDER BY [x0].[CreatedDate] DESC
Microsoft.EntityFrameworkCore.Database.Command:Information: Executed DbCommand (0ms) [Parameters=[@_outer_Id='?'], CommandType='Text', CommandTimeout='30']
SELECT TOP(1) [x.Status0].[Name]
FROM [TaskStatus] AS [x0]
INNER JOIN [Status] AS [x.Status0] ON [x0].[StatusId] = [x.Status0].[Id]
WHERE ([x0].[TaskId] = @_outer_Id) AND ([x0].[Deleted] = 0)
ORDER BY [x0].[CreatedDate] DESC

........

https://i.stack.imgur.com/22H8w.png

https://stackoverflow.com/questions/49834550/ef-core-2-0-2-generates-multiple-query-for-a-subquery

@zulander1
Copy link
Author

zulander1 commented Apr 14, 2018

I have also changed the query to:

let LastStatus = task.TaskStatus.Where(x => x.Deleted == false).OrderByDescending(x => x.CreatedDate).Select(x => x.Status.Name).First()

same results

Tested with EF core 2.1.0-preview2-final still the same issue

@maumar
Copy link
Contributor

maumar commented Apr 14, 2018

Try using FirstOrDefault instead, EFCore is unable to mimic throwing behavior of First if it gets fully translated to sql, so it’s evaluated on the client. There are some issues with using “let” keyword, so you can also try without it - this should produce a single efficient query.

@zulander1
Copy link
Author

It's working with the FirstOrDefault() what do you mean by “let” keyword, so you can also try without it

@maumar
Copy link
Contributor

maumar commented Apr 14, 2018

Defining “let LastStatus = ...” so you can reuse it later in the query. There are known problem when the sub query defined using let appears more than once later

@maumar maumar closed this as completed Apr 14, 2018
@maumar maumar added the closed-no-further-action The issue is closed and no further action is planned. label Apr 14, 2018
@powermetal63
Copy link

Using the model from Many-to-many sample and 2.1.0-preview2-final, the following query still generates N + 1 queries though:

var query = db.Posts.Select(post => new
{
    Post = post,
    LastTag = post.PostTags.OrderByDescending(pt => pt.TagId).Select(pt => pt.Tag).FirstOrDefault(),
}).ToList();

@maumar
Copy link
Contributor

maumar commented Apr 16, 2018

@powermetal63 this is a known issue tracked by #10001

@powermetal63
Copy link

Well, it just looks the same as the one from original poster and they claim that their issue is fixed by removing the let operator and using FirstOrDefault instead of First.

Anyway, as I understand now, it's not supposed to be addressed in the initial 2.1. Good to know in case similar questions arrive at StackOverflow. Thanks.

@maumar
Copy link
Contributor

maumar commented Apr 17, 2018

@powermetal63 this one is different because the subquery returns more than one column. In SQL you can inline a subquery that returns a single scalar (like it was in the first example), but if the subquery returns an entity (i.e. multiple columns) it can't be done. For those cases we split it into two queries and patch the results on the client, but the optimization currently doesn't apply if there are any result operators in the subquery.

@powermetal63
Copy link

Makes sense. But...

var query = db.Tags.Select(tag => new
{
    Tag = tag,
    LastPost = tag.PostTags.OrderByDescending(pt => pt.PostId).Select(pt => pt.Post.Content).FirstOrDefault(),
})
.ToList();

still does N + 1 queries even if I select a single value. Just FYI :)

@smitpatel
Copy link
Contributor

@powermetal63 - We are tracking it at #11186

@ajcvickers ajcvickers reopened this Oct 16, 2022
@ajcvickers ajcvickers closed this as not planned Won't fix, can't repro, duplicate, stale Oct 16, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
closed-no-further-action The issue is closed and no further action is planned.
Projects
None yet
Development

No branches or pull requests

5 participants