dotnet / dotnet/efcore

Join condition that isn't an outer/inner column comparison causes a subquery pushdown

Open
#33,745 2 comments 2 reactions 0 assignees View on GitHub
area-perf area-query consider-for-next-release customer-reported
Dominant language
C#
Stars
14.8k
Forks
3.4k
PR merge metrics
PR metrics pending

Description

Redirected here from npgsql/efcore.pg#3176

1. Declarative-style equijoin
```csharp
from foo in dbContext.Set()
join bar in dbContext.Set() on
new { foo.Id, State = "Blah" } equals new { Id = bar.FooId, bar.State } into barGroupJoin
from subBar in barGroupJoin.DefaultIfEmpty()
select new { /* use subBar instead of bar */ }
```

2. Method-style equijoin
```csharp
from foo in dbContext.Set()
from bar in dbContext.Set().Where(x => x.FooId == foo.Id && x.State == "Blah").DefaultIfEmpty()
select new { /* use bar */ }
```

I generally prefer **2** because it's arguably just as easy to read, doesn't leave a variable that goes unused, etc.
But one thing I notice is that the former style will generate something like
```sql
LEFT JOIN mySchema."Bars" AS B ON B."FooId" = F."Id" AND B."State" = 'Blah'
```
while the latter generates something like
```sql
LEFT JOIN (
SELECT …
FROM mySchema."Bars" AS B
WHERE B."State" = 'Blah'
) as C ON C."FooId" = F."Id"
```

Is this intentional, and if so why?

### Include provider and version information

EF Core version: 8.0.4
Database provider: Npgsql.EntityFrameworkCore.PostgreSQL
Target framework: .NET 8
Operating system: MacOS Sonoma 14.4.1
IDE: Rider

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.