-
Notifications
You must be signed in to change notification settings - Fork 3.2k
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
Suboptimal SQL generation for query with optional navigation, its collection navigation and lateral join #27072
Comments
Can you please reopen this in https://github.com/dotnet/efcore? This is a general SQL generation question rather than anything specific to Npgsql. |
isn't this already in https://github.com/dotnet/efcore? |
Oh apologies, I misread the repo name, going a bit too quickly here. |
cc: @maumar |
when nav expansion is rewriting the collection navigation we add a null check for the outer Id being not null
but we get rid of it (as a redundant null check) when we translate to JOIN/APPLY. So when null semantics rewriter processes the query it applies the SELECT [o].[Id], [t].[Id], [t0].[ChildId], [t0].[ParentId]
FROM [Ones] AS [o]
LEFT JOIN [Twos] AS [t] ON [o].[TwoId] = [t].[Id]
OUTER APPLY (
SELECT [t1].[Id] AS [ChildId], [t].[Id] AS [ParentId]
FROM [Twos] AS [t1]
WHERE [t].[Id] IS NOT NULL AND [t].[Id] = [t1].[ParentId]
) AS [t0]
ORDER BY [o].[Id], [t].[Id] suggestion: we should keep the null check if we can't convert from APPLY to JOIN |
note: this is data corruption, but not a regression (ef5 had the bug already). @ajcvickers @smitpatel thoughts on patching this? |
It should be easy fix by just remembering the earlier predicate rather than recreating it from join predicate extraction. Due to low risk and data corruption, we should patch this. Let's discuss in team meeting. |
…igation, its collection navigation and lateral join When expanding collection navigation we added a check to filter out rows for which parent value is null and only afterwards match inner and outer keys. This could happen when chaining collection navigation after optional reference. Problem was that during translation we try to convert the collection subquery to a join and if that succeeds we don't need the null check anymore - joins key comparison doesn't match nulls. However, we also remove the null check when we are not able to convert to JOIN and use APPLY instead. Fix is to extract two versions of join predicate (with and without the null checks) and then use the one that we need, once we know whether JOIN or APPLY will be used.
…igation, its collection navigation and lateral join When expanding collection navigation we added a check to filter out rows for which parent value is null and only afterwards match inner and outer keys. This could happen when chaining collection navigation after optional reference. Problem was that during translation we try to convert the collection subquery to a join and if that succeeds we don't need the null check anymore - joins key comparison doesn't match nulls. However, we also remove the null check when we are not able to convert to JOIN and use APPLY instead. Fix is to save the original predicate before we try to extract the join predicate and then for APPLY case re-apply the original predicate and only use the extracted join predicate for the JOIN case.
…igation, its collection navigation and lateral join When expanding collection navigation we added a check to filter out rows for which parent value is null and only afterwards match inner and outer keys. This could happen when chaining collection navigation after optional reference. Problem was that during translation we try to convert the collection subquery to a join and if that succeeds we don't need the null check anymore - joins key comparison doesn't match nulls. However, we also remove the null check when we are not able to convert to JOIN and use APPLY instead. Fix is to save the original predicate before we try to extract the join predicate and then for APPLY case re-apply the original predicate and only use the extracted join predicate for the JOIN case. Fixes #27072
Consider the following code:
The generated SQL is:
It is very suboptimal, since due to the unnecessary
OR ((t."Id" IS NULL) AND (t1."ParentId" IS NULL))
clause in the inner query, it will iterate over allEntityTwo
havingParentId = null
for everyEntityOne
withTwoId = null
, creating a new output row on every iteration.It's possible to get rid of the iteration in the database with a workaround like:
provider and version information
EF Core version: 6.0.1
Database provider: Npgsql.EntityFrameworkCore.PostgreSQL 6.0.2
Target framework: .NET 6
The text was updated successfully, but these errors were encountered: