When using UNION with owned entity types a LEFT JOIN to same table is generated
- Dominant language
- C#
- Stars
- 14.8k
- Forks
- 3.4k
- PR merge metrics
- PR metrics pending
Description
Hi,
this bug is related to #18299 which has already been fixed with EF 5.0. I found the following set-up where an overly complicated SQL is produced.
Please see the _very_ simplified example where a `Project` owns one optional `Address`. When we `UNION` two `Project` queries, a `LEFT JOIN` is performed for the `Address` owned entity type. When just selecting projects without a `UNION`, no left join is produced as expected (see output below):
```C#
using Microsoft.EntityFrameworkCore;
using var context = new DataContext();
var query1 = context.Projects;
var query2 = context.Projects;
Console.WriteLine("query1.Union(query2):\n=====================");
Console.WriteLine(query1.Union(query2).ToQueryString());
Console.WriteLine();
Console.WriteLine("query1:\n======");
Console.WriteLine(query1.ToQueryString());
public class Project
{
public Guid Id { get; set; }
public string? Name { get; set; }
public Address? Address { get; set; }
}
public record Address(
string Street,
string ZipCode,
string City,
string IsoCountryCode)
{
}
public class DataContext : DbContext
{
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(@"Server=.\;Database=EFCoreDemo;Trusted_Connection=True;MultipleActiveResultSets=true");
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity(e => e.OwnsOne(e => e.Address));
}
public DbSet Projects => Set();
}
```
The following output is produced:
```
query1.Union(query2):
=====================
SELECT [t].[Id], [t].[Name], [t0].[Id], [t0].[Address_City], [t0].[Address_IsoCountryCode], [t0].[Address_Street], [t0].[Address_ZipCode]
FROM (
SELECT [p].[Id], [p].[Name]
FROM [Projects] AS [p]
UNION
SELECT [p0].[Id], [p0].[Name]
FROM [Projects] AS [p0]
) AS [t]
LEFT JOIN (
SELECT [p1].[Id], [p1].[Address_City], [p1].[Address_IsoCountryCode], [p1].[Address_Street], [p1].[Address_ZipCode]
FROM [Projects] AS [p1]
INNER JOIN [Projects] AS [p2] ON [p1].[Id] = [p2].[Id]
WHERE ((([p1].[Address_City] IS NOT NULL) AND ([p1].[Address_IsoCountryCode] IS NOT NULL)) AND ([p1].[Address_Street] IS NOT NULL)) AND ([p1].[Address_ZipCode] IS NOT NULL)
) AS [t0] ON [t].[Id] = CASE
WHEN ((([t0].[Address_City] IS NOT NULL) AND ([t0].[Address_IsoCountryCode] IS NOT NULL)) AND ([t0].[Address_Street] IS NOT NULL)) AND ([t0].[Address_ZipCode] IS NOT NULL) THEN [t0].[Id]
END
query1:
======
SELECT [p].[Id], [p].[Name], [p].[Address_City], [p].[Address_IsoCountryCode], [p].[Address_Street], [p].[Address_ZipCode]
FROM [Projects] AS [p]
```
Why is a left join produced in the `UNION` example? I would have expected that the `Address` columns are selected within the inner `FROM (...) AS [t]` query and no left join is needed.
The left join can cause major performance issues as already discusssed in #18299. Please note that this is just a very simple example and the two UNION queries we actually use are more complicated.
### Additional info
To emphasise the performance penantly we encounter with the `LEFT JOIN` in our actual query (which I cannot share here): We see a _major_ difference in logical reads:
* 6 logical reads when the `LEFT JOIN` is removed and the owned entity type columns are directly selected in the two UNION statements
* _27746 (!)_ logical reads when using the `LEFT JOIN`.
The table has ~90k entries.
### Include provider and version information
EF Core version: 6.0.9
Database provider: Microsoft.EntityFrameworkCore.SqlServer
Target framework: .NET 6.0
Operating system: Windows
Contributor guide
Assessment
This issue has not been assessed yet.