-
Notifications
You must be signed in to change notification settings - Fork 3.3k
TPT: Avoid connecting with derived tables when not selecting any properties from them #21773
New issue
Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? # to your account
Comments
@dotnet/efcore - Can someone post example of pri0 query which was problematic in EF6 and we wanted to improve. |
DevDiv work items 7221, 137333 (I don't remember how to access these) |
From 2814 public class EntityBase
{
public Guid Id { get; set; }
[MaxLength(50)]
public string Name { get; set; }
public List<Tag> Tags { get; set; }
}
[Table("DerivedEntities1")]
public class DerivedEntity1 : EntityBase
{
[MaxLength(50)]
public string Text { get; set; }
}
[Table("DerviedEntities2")]
public class DerivedEntity2 : EntityBase
{
public int Score { get; set; }
} If I query for base entities and project only plain properties (Name, for example) - everything is fine, derived classes are not joined to the query, since no information is needed from them var entities = dataContext.Entities
.Select(e => new { Name = e.Name })
.ToList(); SELECT
1 AS [C1],
[Extent1].[Name] AS [Name]
FROM [dbo].[EntityHeaders] AS [Extent1] However, if I need to fetch a collection for the base class, tables for all derived classes appear in the query. var entities = dataContext.Entities
.Include(e => e.Tags)
.Select(e => new { Name = e.Name, Tags = e.Tags })
.ToList(); SELECT
[Project1].[Id1] AS [Id],
[Project1].[Id2] AS [Id1],
[Project1].[Id] AS [Id2],
[Project1].[C1] AS [C1],
[Project1].[Name] AS [Name],
[Project1].[C2] AS [C2],
[Project1].[Id3] AS [Id3],
[Project1].[Value] AS [Value],
[Project1].[EntityBase_Id] AS [EntityBase_Id]
FROM ( SELECT
[Extent1].[Id] AS [Id],
[Extent1].[Name] AS [Name],
[Extent2].[Id] AS [Id1],
[Extent3].[Id] AS [Id2],
1 AS [C1],
[Extent4].[Id] AS [Id3],
[Extent4].[Value] AS [Value],
[Extent4].[EntityBase_Id] AS [EntityBase_Id],
CASE WHEN ([Extent4].[Id] IS NULL) THEN CAST(NULL AS int) ELSE 1 END AS [C2]
FROM [dbo].[EntityHeaders] AS [Extent1]
LEFT OUTER JOIN [dbo].[DerivedEntities2] AS [Extent2] ON [Extent1].[Id] = [Extent2].[Id]
LEFT OUTER JOIN [dbo].[DerivedEntities1] AS [Extent3] ON [Extent1].[Id] = [Extent3].[Id]
LEFT OUTER JOIN [dbo].[Tags] AS [Extent4] ON [Extent1].[Id] = [Extent4].[EntityBase_Id]
) AS [Project1]
ORDER BY [Project1].[Id1] ASC, [Project1].[Id2] ASC, [Project1].[Id] ASC, [Project1].[C2] ASC Is there any possibility to avoid this? |
Query generation bug. Not directly related to TPT2296
In 6.1 the following SQL is produced:
However in 6.1.1 query is much more complex:
|
1218
If a query is issued that only queries properties in the base:
The sql generated still performs a join with the union all of the derived type tables:
|
Not relevant to TPT2256 var result = context.ChantierInfos statement : this query on the same join generates an INNER JOIN : why ? (only two relations , if i set three , i will get a LEFT OUTER JOIN) all the relationships are n to 1 using (var context = new DataContext()) var result = context.AgenceInfos Thanks in advance for your feedback Christophe It is hard to tell without looking at the model, but for relationships that are actually n:0..1, e.g. where the foreign key is nullable and therefore the relationship is optional we produce LEFT OUTER JOINs and that is by design. In other words, when you use the Include method in a query, it means that you want to include any existing entities that can be reached by traversing the provided path of navigation properties, and it does not imply that you want to filter the results of the query to include only the entities that contain other entities through the path. I will just make sure that the behavior with non-optional relationships is what we expect before I close. Feel free to provide a repro if you think that is the case. Thanks, I checked as well the mapping and the Db foreign key, and i realy have a many to one relationchip (fk not null , property .IsRequired() in the mapping looks like : Summary : if you consider the properties A,B,C,D B.include(x=>x.C.D) will produce the right result A-B : many to one , B ->C : Many to 1, C->D : Many to one I downloaded the latstet EF6 source and will begin to investigate. Thanks for your feedbacks and idees Christophe Because this issue produce very bad performances on my customer production site (very impacting issue for us) FYI, Emil has been investigating this and we now understand why this is happening: the transformation rule that promotes LEFT OUTER JOIN to INNER JOIN is only applied a couple of times, so the third join and successive joins won't be promoted. Since applying those transformation rules more times could regress the time it takes to translate the query to SQL, we need to be very careful. Could you provide some data about the performance impact? E.g. with your customer database and typical data, how long exactly do the query with the LEFT OUTER JOIN and the one with the INNER JOIN take to execute? Thanks, Thanks a lot for your feedback. |
Resolves #21773 - For top level SelectExpression in client code, we preserve whole projection. - We search for all the referenced column for each table in SelectExpression - If there are no references outside and if it is LeftJoinExpression for TPT (which is remembered through a state in SelectExpression), then we remove the table altogether. - If the table is nested SelectExpression (direct or through a join) then we take referenced columns for that table and prune projection of inner SelectExpression and apply process recursively to prune TPT pattern inside subquery.
Resolves #21773 - For top level SelectExpression in client code, we preserve whole projection. - We search for all the referenced column for each table in SelectExpression - If there are no references outside and if it is LeftJoinExpression for TPT (which is remembered through a state in SelectExpression), then we remove the table altogether. - If the table is nested SelectExpression (direct or through a join) then we take referenced columns for that table and prune projection of inner SelectExpression and apply process recursively to prune TPT pattern inside subquery. - Removes derived tables when projecting out only base properties of TPT - Removes derived tables when joining with collection on base and not selecting any of the derived properties of TPT
Resolves #21773 - For top level SelectExpression in client code, we preserve whole projection. - We search for all the referenced column for each table in SelectExpression - If there are no references outside and if it is LeftJoinExpression for TPT (which is remembered through a state in SelectExpression), then we remove the table altogether. - If the table is nested SelectExpression (direct or through a join) then we take referenced columns for that table and prune projection of inner SelectExpression and apply process recursively to prune TPT pattern inside subquery. - Removes derived tables when projecting out only base properties of TPT - Removes derived tables when joining with collection on base and not selecting any of the derived properties of TPT
#21992) Resolves #21773 - For top level SelectExpression in client code, we preserve whole projection. - We search for all the referenced column for each table in SelectExpression - If there are no references outside and if it is LeftJoinExpression for TPT (which is remembered through a state in SelectExpression), then we remove the table altogether. - If the table is nested SelectExpression (direct or through a join) then we take referenced columns for that table and prune projection of inner SelectExpression and apply process recursively to prune TPT pattern inside subquery. - Removes derived tables when projecting out only base properties of TPT - Removes derived tables when joining with collection on base and not selecting any of the derived properties of TPT
May be a lazy injection of columns. Also can be useful for owned types sharing tables.
Child of #18923
The text was updated successfully, but these errors were encountered: