diff --git a/src/EFCore.Relational/Query/Internal/QueryExpressionReplacingExpressionVisitor.cs b/src/EFCore.Relational/Query/Internal/QueryExpressionReplacingExpressionVisitor.cs new file mode 100644 index 00000000000..97154a73b72 --- /dev/null +++ b/src/EFCore.Relational/Query/Internal/QueryExpressionReplacingExpressionVisitor.cs @@ -0,0 +1,51 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics.CodeAnalysis; +using System.Linq.Expressions; + +namespace Microsoft.EntityFrameworkCore.Query.Internal +{ + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + public class QueryExpressionReplacingExpressionVisitor : ExpressionVisitor + { + private readonly Expression _oldQuery; + private readonly Expression _newQuery; + + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + public QueryExpressionReplacingExpressionVisitor(Expression oldQuery, Expression newQuery) + { + _oldQuery = oldQuery; + _newQuery = newQuery; + } + + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + [return: NotNullIfNotNull("expression")] + public override Expression? Visit(Expression? expression) + { + return expression is ProjectionBindingExpression projectionBindingExpression + && ReferenceEquals(projectionBindingExpression.QueryExpression, _oldQuery) + ? projectionBindingExpression.ProjectionMember != null + ? new ProjectionBindingExpression( + _newQuery, projectionBindingExpression.ProjectionMember!, projectionBindingExpression.Type) + : new ProjectionBindingExpression( + _newQuery, projectionBindingExpression.Index!.Value, projectionBindingExpression.Type) + : base.Visit(expression); + } + } +} diff --git a/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.cs b/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.cs index a690b2a13c8..a86be13667b 100644 --- a/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.cs +++ b/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Linq.Expressions; using System.Reflection; @@ -464,29 +463,7 @@ private static ShapedQueryExpression CreateShapedQueryExpression(IEntityType ent source = TranslateSelect(source, elementSelector); } - if (translatedKey is NewExpression newExpression - && newExpression.Arguments.Count == 0) - { - selectExpression.ApplyGrouping(_sqlExpressionFactory.ApplyDefaultTypeMapping(_sqlExpressionFactory.Constant(1))); - } - else - { - translatedKey = selectExpression.ApplyGrouping(translatedKey); - } - var clonedSelectExpression = selectExpression.Clone(); - // If the grouping key is empty then there may not be any group by terms. - var correlationPredicate = selectExpression.GroupBy.Zip(clonedSelectExpression.GroupBy) - .Select(e => _sqlExpressionFactory.Equal(e.First, e.Second)) - .Aggregate((l, r) => _sqlExpressionFactory.AndAlso(l, r)); - clonedSelectExpression.ClearGroupBy(); - clonedSelectExpression.ApplyPredicate(correlationPredicate); - - var groupByShaper = new GroupByShaperExpression( - translatedKey, - new ShapedQueryExpression( - clonedSelectExpression, - new QueryExpressionReplacingExpressionVisitor(selectExpression, clonedSelectExpression).Visit(source.ShaperExpression))); - + var groupByShaper = selectExpression.ApplyGrouping(translatedKey, source.ShaperExpression, _sqlExpressionFactory); if (resultSelector == null) { return source.UpdateShaperExpression(groupByShaper); @@ -1697,30 +1674,5 @@ static void PopulatePredicateTerms(SqlExpression predicate, List } } } - - private sealed class QueryExpressionReplacingExpressionVisitor : ExpressionVisitor - { - private readonly Expression _oldQuery; - private readonly Expression _newQuery; - - public QueryExpressionReplacingExpressionVisitor(Expression oldQuery, Expression newQuery) - { - _oldQuery = oldQuery; - _newQuery = newQuery; - } - - [return: NotNullIfNotNull("expression")] - public override Expression? Visit(Expression? expression) - { - return expression is ProjectionBindingExpression projectionBindingExpression - && ReferenceEquals(projectionBindingExpression.QueryExpression, _oldQuery) - ? projectionBindingExpression.ProjectionMember != null - ? new ProjectionBindingExpression( - _newQuery, projectionBindingExpression.ProjectionMember!, projectionBindingExpression.Type) - : new ProjectionBindingExpression( - _newQuery, projectionBindingExpression.Index!.Value, projectionBindingExpression.Type) - : base.Visit(expression); - } - } } } diff --git a/src/EFCore.Relational/Query/SqlExpressions/SelectExpression.cs b/src/EFCore.Relational/Query/SqlExpressions/SelectExpression.cs index 676ef9ce4fb..3970f041e61 100644 --- a/src/EFCore.Relational/Query/SqlExpressions/SelectExpression.cs +++ b/src/EFCore.Relational/Query/SqlExpressions/SelectExpression.cs @@ -11,6 +11,7 @@ using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Metadata.Internal; +using Microsoft.EntityFrameworkCore.Query.Internal; using Microsoft.EntityFrameworkCore.Utilities; namespace Microsoft.EntityFrameworkCore.Query.SqlExpressions @@ -1126,7 +1127,7 @@ public void ApplyPredicate(SqlExpression sqlExpression) /// Applies grouping from given key selector. /// /// An key selector expression for the GROUP BY. - public Expression ApplyGrouping(Expression keySelector) + public void ApplyGrouping(Expression keySelector) { Check.NotNull(keySelector, nameof(keySelector)); @@ -1134,7 +1135,7 @@ public Expression ApplyGrouping(Expression keySelector) var groupByTerms = new List(); var groupByAliases = new List(); - AppendGroupBy(keySelector, groupByTerms, groupByAliases, "Key"); + PopulateGroupByTerms(keySelector, groupByTerms, groupByAliases, "Key"); if (groupByTerms.Any(e => e is SqlConstantExpression || e is SqlParameterExpression || e is ScalarSubqueryExpression)) { @@ -1163,19 +1164,84 @@ public Expression ApplyGrouping(Expression keySelector) _identifier.AddRange(_groupBy.Select(e => ((ColumnExpression)e, e.TypeMapping!.KeyComparer))); } } - - return keySelector; } /// - /// Clears existing group by terms. + /// Applies grouping from given key selector and generate to shape results. /// - public void ClearGroupBy() + /// An key selector expression for the GROUP BY. + /// The shaper expression for current query. + /// The sql expression factory to use. + /// A which represents the result of the grouping operation. + public GroupByShaperExpression ApplyGrouping(Expression keySelector, Expression shaperExpression, ISqlExpressionFactory sqlExpressionFactory) { - _groupBy.Clear(); + Check.NotNull(keySelector, nameof(keySelector)); + + ClearOrdering(); + + var keySelectorToAdd = keySelector; + var emptyKey = keySelector is NewExpression newExpression + && newExpression.Arguments.Count == 0; + if (emptyKey) + { + keySelectorToAdd = sqlExpressionFactory.ApplyDefaultTypeMapping(sqlExpressionFactory.Constant(1)); + } + + var groupByTerms = new List(); + var groupByAliases = new List(); + PopulateGroupByTerms(keySelectorToAdd, groupByTerms, groupByAliases, "Key"); + + if (groupByTerms.Any(e => e is SqlConstantExpression || e is SqlParameterExpression || e is ScalarSubqueryExpression)) + { + // EmptyKey will always hit this path. + var sqlRemappingVisitor = PushdownIntoSubqueryInternal(); + var newGroupByTerms = new List(groupByTerms.Count); + var subquery = (SelectExpression)_tables[0]; + var subqueryTableReference = _tableReferences[0]; + for (var i = 0; i < groupByTerms.Count; i++) + { + var item = groupByTerms[i]; + var newItem = subquery._projection.Any(e => e.Expression.Equals(item)) + ? sqlRemappingVisitor.Remap(item) + : subquery.GenerateOuterColumn(subqueryTableReference, item, groupByAliases[i] ?? "Key"); + newGroupByTerms.Add(newItem); + } + if (!emptyKey) + { + // If non-empty key then we need to regenerate the key selector + keySelector = new ReplacingExpressionVisitor(groupByTerms, newGroupByTerms).Visit(keySelector); + } + groupByTerms = newGroupByTerms; + } + + _groupBy.AddRange(groupByTerms); + + // We generate the cloned expression before changing identifier for this SelectExpression + // because we are going to erase grouping for cloned expression. + var clonedSelectExpression = Clone(); + var correlationPredicate = groupByTerms.Zip(clonedSelectExpression._groupBy) + .Select(e => sqlExpressionFactory.Equal(e.First, e.Second)) + .Aggregate((l, r) => sqlExpressionFactory.AndAlso(l, r)); + clonedSelectExpression._groupBy.Clear(); + clonedSelectExpression.ApplyPredicate(correlationPredicate); + + if (!_identifier.All(e => _groupBy.Contains(e.Column))) + { + _identifier.Clear(); + if (_groupBy.All(e => e is ColumnExpression)) + { + _identifier.AddRange(_groupBy.Select(e => ((ColumnExpression)e, e.TypeMapping!.KeyComparer))); + } + } + + return new GroupByShaperExpression( + keySelector, + new ShapedQueryExpression( + clonedSelectExpression, + new QueryExpressionReplacingExpressionVisitor(this, clonedSelectExpression).Visit(shaperExpression))); } - private void AppendGroupBy(Expression keySelector, List groupByTerms, List groupByAliases, string? name) + private void PopulateGroupByTerms(Expression keySelector, List groupByTerms, List groupByAliases, string? name) { Check.NotNull(keySelector, nameof(keySelector)); @@ -1189,23 +1255,23 @@ private void AppendGroupBy(Expression keySelector, List groupByTe case NewExpression newExpression: for (var i = 0; i < newExpression.Arguments.Count; i++) { - AppendGroupBy(newExpression.Arguments[i], groupByTerms, groupByAliases, newExpression.Members?[i].Name); + PopulateGroupByTerms(newExpression.Arguments[i], groupByTerms, groupByAliases, newExpression.Members?[i].Name); } break; case MemberInitExpression memberInitExpression: - AppendGroupBy(memberInitExpression.NewExpression, groupByTerms, groupByAliases, null); + PopulateGroupByTerms(memberInitExpression.NewExpression, groupByTerms, groupByAliases, null); foreach (var argument in memberInitExpression.Bindings) { var memberAssignment = (MemberAssignment)argument; - AppendGroupBy(memberAssignment.Expression, groupByTerms, groupByAliases, memberAssignment.Member.Name); + PopulateGroupByTerms(memberAssignment.Expression, groupByTerms, groupByAliases, memberAssignment.Member.Name); } break; case UnaryExpression unaryExpression when unaryExpression.NodeType == ExpressionType.Convert || unaryExpression.NodeType == ExpressionType.ConvertChecked: - AppendGroupBy(unaryExpression.Operand, groupByTerms, groupByAliases, name); + PopulateGroupByTerms(unaryExpression.Operand, groupByTerms, groupByAliases, name); break; default: diff --git a/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.ExpressionVisitors.cs b/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.ExpressionVisitors.cs index d1977373693..cb2ed4fda42 100644 --- a/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.ExpressionVisitors.cs +++ b/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.ExpressionVisitors.cs @@ -1195,7 +1195,9 @@ public GroupingElementReplacingExpressionVisitor( protected override Expression VisitMethodCall(MethodCallExpression methodCallExpression) { if (methodCallExpression.Method.IsGenericMethod - && methodCallExpression.Method.GetGenericMethodDefinition() == QueryableMethods.AsQueryable + && (methodCallExpression.Method.GetGenericMethodDefinition() == QueryableMethods.AsQueryable + || methodCallExpression.Method.GetGenericMethodDefinition() == EnumerableMethods.ToList + || methodCallExpression.Method.GetGenericMethodDefinition() == EnumerableMethods.ToArray) && methodCallExpression.Arguments[0] == _parameterExpression) { var currentTree = _cloningExpressionVisitor.Clone(_navigationExpansionExpression.CurrentTree); diff --git a/test/EFCore.Specification.Tests/Query/NorthwindGroupByQueryTestBase.cs b/test/EFCore.Specification.Tests/Query/NorthwindGroupByQueryTestBase.cs index b814c7bd6f5..1618e573add 100644 --- a/test/EFCore.Specification.Tests/Query/NorthwindGroupByQueryTestBase.cs +++ b/test/EFCore.Specification.Tests/Query/NorthwindGroupByQueryTestBase.cs @@ -436,6 +436,17 @@ from c in grouping.DefaultIfEmpty() g => new { Value = g.Key + g.Key, Average = g.Average(o => o.OrderID) })); } + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task GroupBy_with_aggregate_through_navigation_property(bool async) + { + return AssertQuery( + async, + ss => ss.Set().GroupBy(c => c.EmployeeID).Select( + g => new { max = g.Max(i => i.Customer.Region) }), + elementSorter: e => e.max); + } + #endregion #region GroupByAnonymousAggregate @@ -1909,7 +1920,7 @@ from o in ss.Set().GroupBy(o => o.CustomerID) .Where(g => g.Count() > 5) .Select(g => new { CustomerID = g.Key, LastOrderID = g.Max(o => o.OrderID) }) .Where(c1 => c.CustomerID == c1.CustomerID) - select c, + select c, entryCount: 63); } @@ -2833,17 +2844,6 @@ public virtual Task GroupBy_Distinct(bool async) ss => ss.Set().GroupBy(o => o.CustomerID).Distinct().Select(g => g.Key))); } - [ConditionalTheory] - [MemberData(nameof(IsAsyncData))] - public virtual Task GroupBy_with_aggregate_through_navigation_property(bool async) - { - return AssertQuery( - async, - ss => ss.Set().GroupBy(c => c.EmployeeID).Select( - g => new { max = g.Max(i => i.Customer.Region) }), - elementSorter: e => e.max); - } - #endregion #region GroupBySelectFirst @@ -2882,6 +2882,78 @@ public virtual Task GroupBy_Shadow3(bool async) .Select(g => EF.Property(g.First(), "Title"))); } + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task GroupBy_select_grouping_list(bool async) + { + return AssertQuery( + async, + ss => ss.Set() + .GroupBy(e => e.City) + .Select(g => new { g.Key, List = g.ToList() }), + elementSorter: e => e.Key, + elementAsserter: (e, a) => + { + AssertEqual(e.Key, a.Key); + AssertCollection(e.List, a.List); + }, + entryCount: 91); + } + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task GroupBy_select_grouping_array(bool async) + { + return AssertQuery( + async, + ss => ss.Set() + .GroupBy(e => e.City) + .Select(g => new { g.Key, List = g.ToArray() }), + elementSorter: e => e.Key, + elementAsserter: (e, a) => + { + AssertEqual(e.Key, a.Key); + AssertCollection(e.List, a.List); + }, + entryCount: 91); + } + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task GroupBy_select_grouping_composed_list(bool async) + { + return AssertQuery( + async, + ss => ss.Set() + .GroupBy(e => e.City) + .Select(g => new { g.Key, List = g.Where(c => c.CustomerID.StartsWith("A")).ToList() }), + elementSorter: e => e.Key, + elementAsserter: (e, a) => + { + AssertEqual(e.Key, a.Key); + AssertCollection(e.List, a.List); + }, + entryCount: 4); + } + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task GroupBy_select_grouping_composed_list_2(bool async) + { + return AssertQuery( + async, + ss => ss.Set() + .GroupBy(e => e.City) + .Select(g => new { g.Key, List = g.OrderBy(c => c.CustomerID).ToList() }), + elementSorter: e => e.Key, + elementAsserter: (e, a) => + { + AssertEqual(e.Key, a.Key); + AssertCollection(e.List, a.List); + }, + entryCount: 91); + } + #endregion #region GroupByEntityType diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsCollectionsQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsCollectionsQuerySqlServerTest.cs index 85e4874710e..356ebcdac45 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsCollectionsQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsCollectionsQuerySqlServerTest.cs @@ -661,7 +661,7 @@ FROM [LevelOne] AS [l0] WHERE [t1].[row] <= 1 ) AS [t0] ON [t].[Name] = [t0].[Name] LEFT JOIN [LevelTwo] AS [l1] ON [t0].[Id] = [l1].[OneToMany_Optional_Inverse2Id] -ORDER BY [t].[Name], [t0].[Name]"); +ORDER BY [t].[Name], [t0].[Id]"); } public override async Task Include_collection_with_groupby_in_subquery_and_filter_before_groupby(bool async) @@ -686,7 +686,7 @@ WHERE [l0].[Id] > 3 WHERE [t1].[row] <= 1 ) AS [t0] ON [t].[Name] = [t0].[Name] LEFT JOIN [LevelTwo] AS [l1] ON [t0].[Id] = [l1].[OneToMany_Optional_Inverse2Id] -ORDER BY [t].[Name], [t0].[Name]"); +ORDER BY [t].[Name], [t0].[Id]"); } public override async Task Include_collection_with_groupby_in_subquery_and_filter_after_groupby(bool async) @@ -710,7 +710,7 @@ FROM [LevelOne] AS [l0] WHERE [t1].[row] <= 1 ) AS [t0] ON [t].[Name] = [t0].[Name] LEFT JOIN [LevelTwo] AS [l1] ON [t0].[Id] = [l1].[OneToMany_Optional_Inverse2Id] -ORDER BY [t].[Name], [t0].[Name]"); +ORDER BY [t].[Name], [t0].[Id]"); } public override async Task Include_reference_collection_order_by_reference_navigation(bool async) @@ -2000,16 +2000,16 @@ public override async Task Skip_Take_on_grouping_element_into_non_entity(bool as await base.Skip_Take_on_grouping_element_into_non_entity(async); AssertSql( - @"SELECT [t].[Date], [t0].[Name], [t0].[Date] + @"SELECT [t].[Date], [t0].[Name], [t0].[Id] FROM ( SELECT [l].[Date] FROM [LevelOne] AS [l] GROUP BY [l].[Date] ) AS [t] LEFT JOIN ( - SELECT [t1].[Name], [t1].[Date] + SELECT [t1].[Name], [t1].[Id], [t1].[Date] FROM ( - SELECT [l0].[Name], [l0].[Date], ROW_NUMBER() OVER(PARTITION BY [l0].[Date] ORDER BY [l0].[Name]) AS [row] + SELECT [l0].[Name], [l0].[Id], [l0].[Date], ROW_NUMBER() OVER(PARTITION BY [l0].[Date] ORDER BY [l0].[Name]) AS [row] FROM [LevelOne] AS [l0] ) AS [t1] WHERE (1 < [t1].[row]) AND ([t1].[row] <= 6) @@ -2039,7 +2039,7 @@ OFFSET 1 ROWS FETCH NEXT 5 ROWS ONLY ) AS [t1] LEFT JOIN [LevelTwo] AS [l0] ON [t1].[Id] = [l0].[OneToMany_Optional_Inverse2Id] ) AS [t0] -ORDER BY [t].[Date], [t0].[Name], [t0].[Date]"); +ORDER BY [t].[Date], [t0].[Name], [t0].[Id]"); } public override async Task Skip_Take_on_grouping_element_with_reference_include(bool async) @@ -2064,7 +2064,7 @@ OFFSET 1 ROWS FETCH NEXT 5 ROWS ONLY ) AS [t1] LEFT JOIN [LevelTwo] AS [l0] ON [t1].[Id] = [l0].[Level1_Optional_Id] ) AS [t0] -ORDER BY [t].[Date], [t0].[Name], [t0].[Date]"); +ORDER BY [t].[Date], [t0].[Name], [t0].[Id]"); } public override async Task Skip_Take_on_grouping_element_inside_collection_projection(bool async) diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsCollectionsSplitQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsCollectionsSplitQuerySqlServerTest.cs index 58a87f74eec..509624571d8 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsCollectionsSplitQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsCollectionsSplitQuerySqlServerTest.cs @@ -2228,9 +2228,9 @@ FROM [LevelOne] AS [l0] ) AS [t1] WHERE [t1].[row] <= 1 ) AS [t0] ON [t].[Name] = [t0].[Name] -ORDER BY [t].[Name], [t0].[Name]", +ORDER BY [t].[Name], [t0].[Id]", // - @"SELECT [l1].[Id], [l1].[Date], [l1].[Level1_Optional_Id], [l1].[Level1_Required_Id], [l1].[Name], [l1].[OneToMany_Optional_Inverse2Id], [l1].[OneToMany_Optional_Self_Inverse2Id], [l1].[OneToMany_Required_Inverse2Id], [l1].[OneToMany_Required_Self_Inverse2Id], [l1].[OneToOne_Optional_PK_Inverse2Id], [l1].[OneToOne_Optional_Self2Id], [t].[Name], [t0].[Name] + @"SELECT [l1].[Id], [l1].[Date], [l1].[Level1_Optional_Id], [l1].[Level1_Required_Id], [l1].[Name], [l1].[OneToMany_Optional_Inverse2Id], [l1].[OneToMany_Optional_Self_Inverse2Id], [l1].[OneToMany_Required_Inverse2Id], [l1].[OneToMany_Required_Self_Inverse2Id], [l1].[OneToOne_Optional_PK_Inverse2Id], [l1].[OneToOne_Optional_Self2Id], [t].[Name], [t0].[Id] FROM ( SELECT [l].[Name] FROM [LevelOne] AS [l] @@ -2245,7 +2245,7 @@ FROM [LevelOne] AS [l0] WHERE [t1].[row] <= 1 ) AS [t0] ON [t].[Name] = [t0].[Name] INNER JOIN [LevelTwo] AS [l1] ON [t0].[Id] = [l1].[OneToMany_Optional_Inverse2Id] -ORDER BY [t].[Name], [t0].[Name]"); +ORDER BY [t].[Name], [t0].[Id]"); } public override async Task Include_collection_with_groupby_in_subquery_and_filter_before_groupby(bool async) @@ -2269,9 +2269,9 @@ WHERE [l0].[Id] > 3 ) AS [t1] WHERE [t1].[row] <= 1 ) AS [t0] ON [t].[Name] = [t0].[Name] -ORDER BY [t].[Name], [t0].[Name]", +ORDER BY [t].[Name], [t0].[Id]", // - @"SELECT [l1].[Id], [l1].[Date], [l1].[Level1_Optional_Id], [l1].[Level1_Required_Id], [l1].[Name], [l1].[OneToMany_Optional_Inverse2Id], [l1].[OneToMany_Optional_Self_Inverse2Id], [l1].[OneToMany_Required_Inverse2Id], [l1].[OneToMany_Required_Self_Inverse2Id], [l1].[OneToOne_Optional_PK_Inverse2Id], [l1].[OneToOne_Optional_Self2Id], [t].[Name], [t0].[Name] + @"SELECT [l1].[Id], [l1].[Date], [l1].[Level1_Optional_Id], [l1].[Level1_Required_Id], [l1].[Name], [l1].[OneToMany_Optional_Inverse2Id], [l1].[OneToMany_Optional_Self_Inverse2Id], [l1].[OneToMany_Required_Inverse2Id], [l1].[OneToMany_Required_Self_Inverse2Id], [l1].[OneToOne_Optional_PK_Inverse2Id], [l1].[OneToOne_Optional_Self2Id], [t].[Name], [t0].[Id] FROM ( SELECT [l].[Name] FROM [LevelOne] AS [l] @@ -2288,7 +2288,7 @@ WHERE [l0].[Id] > 3 WHERE [t1].[row] <= 1 ) AS [t0] ON [t].[Name] = [t0].[Name] INNER JOIN [LevelTwo] AS [l1] ON [t0].[Id] = [l1].[OneToMany_Optional_Inverse2Id] -ORDER BY [t].[Name], [t0].[Name]"); +ORDER BY [t].[Name], [t0].[Id]"); } public override async Task Include_collection_with_groupby_in_subquery_and_filter_after_groupby(bool async) @@ -2311,9 +2311,9 @@ FROM [LevelOne] AS [l0] ) AS [t1] WHERE [t1].[row] <= 1 ) AS [t0] ON [t].[Name] = [t0].[Name] -ORDER BY [t].[Name], [t0].[Name]", +ORDER BY [t].[Name], [t0].[Id]", // - @"SELECT [l1].[Id], [l1].[Date], [l1].[Level1_Optional_Id], [l1].[Level1_Required_Id], [l1].[Name], [l1].[OneToMany_Optional_Inverse2Id], [l1].[OneToMany_Optional_Self_Inverse2Id], [l1].[OneToMany_Required_Inverse2Id], [l1].[OneToMany_Required_Self_Inverse2Id], [l1].[OneToOne_Optional_PK_Inverse2Id], [l1].[OneToOne_Optional_Self2Id], [t].[Name], [t0].[Name] + @"SELECT [l1].[Id], [l1].[Date], [l1].[Level1_Optional_Id], [l1].[Level1_Required_Id], [l1].[Name], [l1].[OneToMany_Optional_Inverse2Id], [l1].[OneToMany_Optional_Self_Inverse2Id], [l1].[OneToMany_Required_Inverse2Id], [l1].[OneToMany_Required_Self_Inverse2Id], [l1].[OneToOne_Optional_PK_Inverse2Id], [l1].[OneToOne_Optional_Self2Id], [t].[Name], [t0].[Id] FROM ( SELECT [l].[Name] FROM [LevelOne] AS [l] @@ -2329,7 +2329,7 @@ FROM [LevelOne] AS [l0] WHERE [t1].[row] <= 1 ) AS [t0] ON [t].[Name] = [t0].[Name] INNER JOIN [LevelTwo] AS [l1] ON [t0].[Id] = [l1].[OneToMany_Optional_Inverse2Id] -ORDER BY [t].[Name], [t0].[Name]"); +ORDER BY [t].[Name], [t0].[Id]"); } public override async Task Include_reference_collection_order_by_reference_navigation(bool async) @@ -3111,7 +3111,7 @@ FROM [LevelOne] AS [l0] ) AS [t1] WHERE (1 < [t1].[row]) AND ([t1].[row] <= 6) ) AS [t0] ON [t].[Date] = [t0].[Date] -ORDER BY [t].[Date], [t0].[Date], [t0].[Name]"); +ORDER BY [t].[Date], [t0].[Date], [t0].[Name], [t0].[Id]"); } public override async Task Skip_Take_on_grouping_element_with_reference_include(bool async) diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsQuerySqlServerTest.cs index ba668023866..f76c3ca9b08 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsQuerySqlServerTest.cs @@ -2611,7 +2611,7 @@ FROM [LevelOne] AS [l0] WHERE [t1].[row] <= 1 ) AS [t0] ON [t].[Name] = [t0].[Name] LEFT JOIN [LevelThree] AS [l2] ON [t0].[Id0] = [l2].[OneToMany_Optional_Inverse3Id] -ORDER BY [t].[Name], [t0].[Name], [t0].[Id0]"); +ORDER BY [t].[Name], [t0].[Id], [t0].[Id0]"); } public override async Task String_include_multiple_derived_navigation_with_same_name_and_same_type(bool async) diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/GearsOfWarQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/GearsOfWarQuerySqlServerTest.cs index d7d70fd8348..91227850ccf 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/GearsOfWarQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/GearsOfWarQuerySqlServerTest.cs @@ -5589,7 +5589,7 @@ GROUP BY [g].[Rank] LEFT JOIN ( SELECT [t1].[Nickname], [t1].[SquadId], [t1].[AssignedCityName], [t1].[CityOfBirthName], [t1].[Discriminator], [t1].[FullName], [t1].[HasSoulPatch], [t1].[LeaderNickname], [t1].[LeaderSquadId], [t1].[Rank], [t1].[Name], [t1].[Location], [t1].[Nation] FROM ( - SELECT [g0].[Nickname], [g0].[SquadId], [g0].[AssignedCityName], [g0].[CityOfBirthName], [g0].[Discriminator], [g0].[FullName], [g0].[HasSoulPatch], [g0].[LeaderNickname], [g0].[LeaderSquadId], [g0].[Rank], [c].[Name], [c].[Location], [c].[Nation], ROW_NUMBER() OVER(PARTITION BY [g0].[Rank] ORDER BY [g0].[Rank], [c].[Name]) AS [row] + SELECT [g0].[Nickname], [g0].[SquadId], [g0].[AssignedCityName], [g0].[CityOfBirthName], [g0].[Discriminator], [g0].[FullName], [g0].[HasSoulPatch], [g0].[LeaderNickname], [g0].[LeaderSquadId], [g0].[Rank], [c].[Name], [c].[Location], [c].[Nation], ROW_NUMBER() OVER(PARTITION BY [g0].[Rank] ORDER BY [g0].[Nickname], [g0].[SquadId], [c].[Name]) AS [row] FROM [Gears] AS [g0] INNER JOIN [Cities] AS [c] ON [g0].[CityOfBirthName] = [c].[Name] WHERE [g0].[HasSoulPatch] = CAST(1 AS bit) diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindGroupByQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindGroupByQuerySqlServerTest.cs index 1a474be074f..3e5f589eb62 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindGroupByQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindGroupByQuerySqlServerTest.cs @@ -1893,7 +1893,7 @@ GROUP BY [e].[Title] LEFT JOIN ( SELECT [t1].[EmployeeID], [t1].[City], [t1].[Country], [t1].[FirstName], [t1].[ReportsTo], [t1].[Title] FROM ( - SELECT [e0].[EmployeeID], [e0].[City], [e0].[Country], [e0].[FirstName], [e0].[ReportsTo], [e0].[Title], ROW_NUMBER() OVER(PARTITION BY [e0].[Title] ORDER BY [e0].[Title]) AS [row] + SELECT [e0].[EmployeeID], [e0].[City], [e0].[Country], [e0].[FirstName], [e0].[ReportsTo], [e0].[Title], ROW_NUMBER() OVER(PARTITION BY [e0].[Title] ORDER BY [e0].[EmployeeID]) AS [row] FROM [Employees] AS [e0] WHERE ([e0].[Title] = N'Sales Representative') AND ([e0].[EmployeeID] = 1) ) AS [t1] @@ -1915,6 +1915,74 @@ FROM [Employees] AS [e] GROUP BY [e].[EmployeeID]"); } + public override async Task GroupBy_select_grouping_list(bool async) + { + await base.GroupBy_select_grouping_list(async); + + AssertSql( + @"SELECT [t].[City], [c0].[CustomerID], [c0].[Address], [c0].[City], [c0].[CompanyName], [c0].[ContactName], [c0].[ContactTitle], [c0].[Country], [c0].[Fax], [c0].[Phone], [c0].[PostalCode], [c0].[Region] +FROM ( + SELECT [c].[City] + FROM [Customers] AS [c] + GROUP BY [c].[City] +) AS [t] +LEFT JOIN [Customers] AS [c0] ON [t].[City] = [c0].[City] +ORDER BY [t].[City]"); + } + + public override async Task GroupBy_select_grouping_array(bool async) + { + await base.GroupBy_select_grouping_array(async); + + AssertSql( + @"SELECT [t].[City], [c0].[CustomerID], [c0].[Address], [c0].[City], [c0].[CompanyName], [c0].[ContactName], [c0].[ContactTitle], [c0].[Country], [c0].[Fax], [c0].[Phone], [c0].[PostalCode], [c0].[Region] +FROM ( + SELECT [c].[City] + FROM [Customers] AS [c] + GROUP BY [c].[City] +) AS [t] +LEFT JOIN [Customers] AS [c0] ON [t].[City] = [c0].[City] +ORDER BY [t].[City]"); + } + + public override async Task GroupBy_select_grouping_composed_list(bool async) + { + await base.GroupBy_select_grouping_composed_list(async); + + AssertSql( + @"SELECT [t].[City], [t0].[CustomerID], [t0].[Address], [t0].[City], [t0].[CompanyName], [t0].[ContactName], [t0].[ContactTitle], [t0].[Country], [t0].[Fax], [t0].[Phone], [t0].[PostalCode], [t0].[Region] +FROM ( + SELECT [c].[City] + FROM [Customers] AS [c] + GROUP BY [c].[City] +) AS [t] +LEFT JOIN ( + SELECT [c0].[CustomerID], [c0].[Address], [c0].[City], [c0].[CompanyName], [c0].[ContactName], [c0].[ContactTitle], [c0].[Country], [c0].[Fax], [c0].[Phone], [c0].[PostalCode], [c0].[Region] + FROM [Customers] AS [c0] + WHERE [c0].[CustomerID] LIKE N'A%' +) AS [t0] ON ([t].[City] = [t0].[City]) OR ([t].[City] IS NULL AND [t0].[City] IS NULL) +ORDER BY [t].[City]"); + } + + public override async Task GroupBy_select_grouping_composed_list_2(bool async) + { + await base.GroupBy_select_grouping_composed_list_2(async); + + AssertSql( + @"SELECT [t].[City], [t0].[CustomerID], [t0].[Address], [t0].[City], [t0].[CompanyName], [t0].[ContactName], [t0].[ContactTitle], [t0].[Country], [t0].[Fax], [t0].[Phone], [t0].[PostalCode], [t0].[Region] +FROM ( + SELECT [c].[City] + FROM [Customers] AS [c] + GROUP BY [c].[City] +) AS [t] +LEFT JOIN ( + SELECT [c0].[CustomerID], [c0].[Address], [c0].[City], [c0].[CompanyName], [c0].[ContactName], [c0].[ContactTitle], [c0].[Country], [c0].[Fax], [c0].[Phone], [c0].[PostalCode], [c0].[Region] + FROM [Customers] AS [c0] +) AS [t0] ON [t].[City] = [t0].[City] +ORDER BY [t].[City], [t0].[CustomerID]"); + } + + public override async Task Select_GroupBy_SelectMany(bool async) { await base.Select_GroupBy_SelectMany(async); diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindIncludeQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindIncludeQuerySqlServerTest.cs index dc3d1e44671..f38630ac6cd 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindIncludeQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindIncludeQuerySqlServerTest.cs @@ -1097,7 +1097,7 @@ public override async Task Include_collection_Join_GroupBy_Select(bool async) await base.Include_collection_Join_GroupBy_Select(async); AssertSql( - @"SELECT [t0].[OrderID], [t0].[CustomerID], [t0].[EmployeeID], [t0].[OrderDate], [t].[OrderID], [o3].[OrderID], [o3].[ProductID], [o3].[Discount], [o3].[Quantity], [o3].[UnitPrice] + @"SELECT [t0].[OrderID], [t0].[CustomerID], [t0].[EmployeeID], [t0].[OrderDate], [t].[OrderID], [t0].[OrderID0], [t0].[ProductID], [o3].[OrderID], [o3].[ProductID], [o3].[Discount], [o3].[Quantity], [o3].[UnitPrice] FROM ( SELECT [o].[OrderID] FROM [Orders] AS [o] @@ -1106,9 +1106,9 @@ FROM [Orders] AS [o] GROUP BY [o].[OrderID] ) AS [t] LEFT JOIN ( - SELECT [t1].[OrderID], [t1].[CustomerID], [t1].[EmployeeID], [t1].[OrderDate] + SELECT [t1].[OrderID], [t1].[CustomerID], [t1].[EmployeeID], [t1].[OrderDate], [t1].[OrderID0], [t1].[ProductID] FROM ( - SELECT [o1].[OrderID], [o1].[CustomerID], [o1].[EmployeeID], [o1].[OrderDate], ROW_NUMBER() OVER(PARTITION BY [o1].[OrderID] ORDER BY [o1].[OrderID]) AS [row] + SELECT [o1].[OrderID], [o1].[CustomerID], [o1].[EmployeeID], [o1].[OrderDate], [o2].[OrderID] AS [OrderID0], [o2].[ProductID], ROW_NUMBER() OVER(PARTITION BY [o1].[OrderID] ORDER BY [o1].[OrderID]) AS [row] FROM [Orders] AS [o1] INNER JOIN [Order Details] AS [o2] ON [o1].[OrderID] = [o2].[OrderID] WHERE [o1].[OrderID] = 10248 @@ -1116,7 +1116,7 @@ FROM [Orders] AS [o1] WHERE [t1].[row] <= 1 ) AS [t0] ON [t].[OrderID] = [t0].[OrderID] LEFT JOIN [Order Details] AS [o3] ON [t0].[OrderID] = [o3].[OrderID] -ORDER BY [t].[OrderID], [t0].[OrderID], [o3].[OrderID]"); +ORDER BY [t].[OrderID], [t0].[OrderID], [t0].[OrderID0], [t0].[ProductID], [o3].[OrderID]"); } public override async Task Include_reference_Join_GroupBy_Select(bool async) @@ -1150,7 +1150,7 @@ public override async Task Join_Include_collection_GroupBy_Select(bool async) await base.Join_Include_collection_GroupBy_Select(async); AssertSql( - @"SELECT [t0].[OrderID], [t0].[CustomerID], [t0].[EmployeeID], [t0].[OrderDate], [t].[OrderID], [o3].[OrderID], [o3].[ProductID], [o3].[Discount], [o3].[Quantity], [o3].[UnitPrice] + @"SELECT [t0].[OrderID], [t0].[CustomerID], [t0].[EmployeeID], [t0].[OrderDate], [t].[OrderID], [t0].[OrderID0], [t0].[ProductID], [o3].[OrderID], [o3].[ProductID], [o3].[Discount], [o3].[Quantity], [o3].[UnitPrice] FROM ( SELECT [o0].[OrderID] FROM [Order Details] AS [o] @@ -1159,9 +1159,9 @@ FROM [Order Details] AS [o] GROUP BY [o0].[OrderID] ) AS [t] LEFT JOIN ( - SELECT [t1].[OrderID], [t1].[CustomerID], [t1].[EmployeeID], [t1].[OrderDate] + SELECT [t1].[OrderID], [t1].[CustomerID], [t1].[EmployeeID], [t1].[OrderDate], [t1].[OrderID0], [t1].[ProductID] FROM ( - SELECT [o2].[OrderID], [o2].[CustomerID], [o2].[EmployeeID], [o2].[OrderDate], ROW_NUMBER() OVER(PARTITION BY [o2].[OrderID] ORDER BY [o2].[OrderID]) AS [row] + SELECT [o2].[OrderID], [o2].[CustomerID], [o2].[EmployeeID], [o2].[OrderDate], [o1].[OrderID] AS [OrderID0], [o1].[ProductID], ROW_NUMBER() OVER(PARTITION BY [o2].[OrderID] ORDER BY [o2].[OrderID]) AS [row] FROM [Order Details] AS [o1] INNER JOIN [Orders] AS [o2] ON [o1].[OrderID] = [o2].[OrderID] WHERE [o1].[OrderID] = 10248 @@ -1169,7 +1169,7 @@ FROM [Order Details] AS [o1] WHERE [t1].[row] <= 1 ) AS [t0] ON [t].[OrderID] = [t0].[OrderID] LEFT JOIN [Order Details] AS [o3] ON [t0].[OrderID] = [o3].[OrderID] -ORDER BY [t].[OrderID], [t0].[OrderID], [o3].[OrderID]"); +ORDER BY [t].[OrderID], [t0].[OrderID0], [t0].[ProductID], [t0].[OrderID], [o3].[OrderID]"); } public override async Task Join_Include_reference_GroupBy_Select(bool async) @@ -1201,7 +1201,7 @@ public override async Task Include_collection_SelectMany_GroupBy_Select(bool asy await base.Include_collection_SelectMany_GroupBy_Select(async); AssertSql( - @"SELECT [t0].[OrderID], [t0].[CustomerID], [t0].[EmployeeID], [t0].[OrderDate], [t].[OrderID], [o3].[OrderID], [o3].[ProductID], [o3].[Discount], [o3].[Quantity], [o3].[UnitPrice] + @"SELECT [t0].[OrderID], [t0].[CustomerID], [t0].[EmployeeID], [t0].[OrderDate], [t].[OrderID], [t0].[OrderID0], [t0].[ProductID], [o3].[OrderID], [o3].[ProductID], [o3].[Discount], [o3].[Quantity], [o3].[UnitPrice] FROM ( SELECT [o].[OrderID] FROM [Orders] AS [o] @@ -1210,9 +1210,9 @@ CROSS JOIN [Order Details] AS [o0] GROUP BY [o].[OrderID] ) AS [t] LEFT JOIN ( - SELECT [t1].[OrderID], [t1].[CustomerID], [t1].[EmployeeID], [t1].[OrderDate] + SELECT [t1].[OrderID], [t1].[CustomerID], [t1].[EmployeeID], [t1].[OrderDate], [t1].[OrderID0], [t1].[ProductID] FROM ( - SELECT [o1].[OrderID], [o1].[CustomerID], [o1].[EmployeeID], [o1].[OrderDate], ROW_NUMBER() OVER(PARTITION BY [o1].[OrderID] ORDER BY [o1].[OrderID]) AS [row] + SELECT [o1].[OrderID], [o1].[CustomerID], [o1].[EmployeeID], [o1].[OrderDate], [o2].[OrderID] AS [OrderID0], [o2].[ProductID], ROW_NUMBER() OVER(PARTITION BY [o1].[OrderID] ORDER BY [o1].[OrderID]) AS [row] FROM [Orders] AS [o1] CROSS JOIN [Order Details] AS [o2] WHERE [o1].[OrderID] = 10248 @@ -1220,7 +1220,7 @@ CROSS JOIN [Order Details] AS [o2] WHERE [t1].[row] <= 1 ) AS [t0] ON [t].[OrderID] = [t0].[OrderID] LEFT JOIN [Order Details] AS [o3] ON [t0].[OrderID] = [o3].[OrderID] -ORDER BY [t].[OrderID], [t0].[OrderID], [o3].[OrderID]"); +ORDER BY [t].[OrderID], [t0].[OrderID], [t0].[OrderID0], [t0].[ProductID], [o3].[OrderID]"); } public override async Task Include_reference_SelectMany_GroupBy_Select(bool async) @@ -1254,7 +1254,7 @@ public override async Task SelectMany_Include_collection_GroupBy_Select(bool asy await base.SelectMany_Include_collection_GroupBy_Select(async); AssertSql( - @"SELECT [t0].[OrderID], [t0].[CustomerID], [t0].[EmployeeID], [t0].[OrderDate], [t].[OrderID], [o3].[OrderID], [o3].[ProductID], [o3].[Discount], [o3].[Quantity], [o3].[UnitPrice] + @"SELECT [t0].[OrderID], [t0].[CustomerID], [t0].[EmployeeID], [t0].[OrderDate], [t].[OrderID], [t0].[OrderID0], [t0].[ProductID], [o3].[OrderID], [o3].[ProductID], [o3].[Discount], [o3].[Quantity], [o3].[UnitPrice] FROM ( SELECT [o0].[OrderID] FROM [Order Details] AS [o] @@ -1263,9 +1263,9 @@ CROSS JOIN [Orders] AS [o0] GROUP BY [o0].[OrderID] ) AS [t] LEFT JOIN ( - SELECT [t1].[OrderID], [t1].[CustomerID], [t1].[EmployeeID], [t1].[OrderDate] + SELECT [t1].[OrderID], [t1].[CustomerID], [t1].[EmployeeID], [t1].[OrderDate], [t1].[OrderID0], [t1].[ProductID] FROM ( - SELECT [o2].[OrderID], [o2].[CustomerID], [o2].[EmployeeID], [o2].[OrderDate], ROW_NUMBER() OVER(PARTITION BY [o2].[OrderID] ORDER BY [o2].[OrderID]) AS [row] + SELECT [o2].[OrderID], [o2].[CustomerID], [o2].[EmployeeID], [o2].[OrderDate], [o1].[OrderID] AS [OrderID0], [o1].[ProductID], ROW_NUMBER() OVER(PARTITION BY [o2].[OrderID] ORDER BY [o2].[OrderID]) AS [row] FROM [Order Details] AS [o1] CROSS JOIN [Orders] AS [o2] WHERE [o1].[OrderID] = 10248 @@ -1273,7 +1273,7 @@ CROSS JOIN [Orders] AS [o2] WHERE [t1].[row] <= 1 ) AS [t0] ON [t].[OrderID] = [t0].[OrderID] LEFT JOIN [Order Details] AS [o3] ON [t0].[OrderID] = [o3].[OrderID] -ORDER BY [t].[OrderID], [t0].[OrderID], [o3].[OrderID]"); +ORDER BY [t].[OrderID], [t0].[OrderID0], [t0].[ProductID], [t0].[OrderID], [o3].[OrderID]"); } public override async Task SelectMany_Include_reference_GroupBy_Select(bool async) diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindSplitIncludeQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindSplitIncludeQuerySqlServerTest.cs index 15f238a0fc6..7cb18e992ff 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindSplitIncludeQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindSplitIncludeQuerySqlServerTest.cs @@ -1477,7 +1477,7 @@ public override async Task Include_collection_Join_GroupBy_Select(bool async) await base.Include_collection_Join_GroupBy_Select(async); AssertSql( - @"SELECT [t0].[OrderID], [t0].[CustomerID], [t0].[EmployeeID], [t0].[OrderDate], [t].[OrderID] + @"SELECT [t0].[OrderID], [t0].[CustomerID], [t0].[EmployeeID], [t0].[OrderDate], [t].[OrderID], [t0].[OrderID0], [t0].[ProductID] FROM ( SELECT [o].[OrderID] FROM [Orders] AS [o] @@ -1486,18 +1486,18 @@ FROM [Orders] AS [o] GROUP BY [o].[OrderID] ) AS [t] LEFT JOIN ( - SELECT [t1].[OrderID], [t1].[CustomerID], [t1].[EmployeeID], [t1].[OrderDate] + SELECT [t1].[OrderID], [t1].[CustomerID], [t1].[EmployeeID], [t1].[OrderDate], [t1].[OrderID0], [t1].[ProductID] FROM ( - SELECT [o1].[OrderID], [o1].[CustomerID], [o1].[EmployeeID], [o1].[OrderDate], ROW_NUMBER() OVER(PARTITION BY [o1].[OrderID] ORDER BY [o1].[OrderID]) AS [row] + SELECT [o1].[OrderID], [o1].[CustomerID], [o1].[EmployeeID], [o1].[OrderDate], [o2].[OrderID] AS [OrderID0], [o2].[ProductID], ROW_NUMBER() OVER(PARTITION BY [o1].[OrderID] ORDER BY [o1].[OrderID]) AS [row] FROM [Orders] AS [o1] INNER JOIN [Order Details] AS [o2] ON [o1].[OrderID] = [o2].[OrderID] WHERE [o1].[OrderID] = 10248 ) AS [t1] WHERE [t1].[row] <= 1 ) AS [t0] ON [t].[OrderID] = [t0].[OrderID] -ORDER BY [t].[OrderID], [t0].[OrderID]", +ORDER BY [t].[OrderID], [t0].[OrderID], [t0].[OrderID0], [t0].[ProductID]", // - @"SELECT [o3].[OrderID], [o3].[ProductID], [o3].[Discount], [o3].[Quantity], [o3].[UnitPrice], [t].[OrderID], [t0].[OrderID] + @"SELECT [o3].[OrderID], [o3].[ProductID], [o3].[Discount], [o3].[Quantity], [o3].[UnitPrice], [t].[OrderID], [t0].[OrderID], [t0].[OrderID0], [t0].[ProductID] FROM ( SELECT [o].[OrderID] FROM [Orders] AS [o] @@ -1506,9 +1506,9 @@ FROM [Orders] AS [o] GROUP BY [o].[OrderID] ) AS [t] LEFT JOIN ( - SELECT [t1].[OrderID] + SELECT [t1].[OrderID], [t1].[OrderID0], [t1].[ProductID] FROM ( - SELECT [o1].[OrderID], ROW_NUMBER() OVER(PARTITION BY [o1].[OrderID] ORDER BY [o1].[OrderID]) AS [row] + SELECT [o1].[OrderID], [o2].[OrderID] AS [OrderID0], [o2].[ProductID], ROW_NUMBER() OVER(PARTITION BY [o1].[OrderID] ORDER BY [o1].[OrderID]) AS [row] FROM [Orders] AS [o1] INNER JOIN [Order Details] AS [o2] ON [o1].[OrderID] = [o2].[OrderID] WHERE [o1].[OrderID] = 10248 @@ -1516,7 +1516,7 @@ FROM [Orders] AS [o1] WHERE [t1].[row] <= 1 ) AS [t0] ON [t].[OrderID] = [t0].[OrderID] INNER JOIN [Order Details] AS [o3] ON [t0].[OrderID] = [o3].[OrderID] -ORDER BY [t].[OrderID], [t0].[OrderID]"); +ORDER BY [t].[OrderID], [t0].[OrderID], [t0].[OrderID0], [t0].[ProductID]"); } public override async Task Include_reference_Join_GroupBy_Select(bool async) @@ -1550,7 +1550,7 @@ public override async Task Join_Include_collection_GroupBy_Select(bool async) await base.Join_Include_collection_GroupBy_Select(async); AssertSql( - @"SELECT [t0].[OrderID], [t0].[CustomerID], [t0].[EmployeeID], [t0].[OrderDate], [t].[OrderID] + @"SELECT [t0].[OrderID], [t0].[CustomerID], [t0].[EmployeeID], [t0].[OrderDate], [t].[OrderID], [t0].[OrderID0], [t0].[ProductID] FROM ( SELECT [o0].[OrderID] FROM [Order Details] AS [o] @@ -1559,18 +1559,18 @@ FROM [Order Details] AS [o] GROUP BY [o0].[OrderID] ) AS [t] LEFT JOIN ( - SELECT [t1].[OrderID], [t1].[CustomerID], [t1].[EmployeeID], [t1].[OrderDate] + SELECT [t1].[OrderID], [t1].[CustomerID], [t1].[EmployeeID], [t1].[OrderDate], [t1].[OrderID0], [t1].[ProductID] FROM ( - SELECT [o2].[OrderID], [o2].[CustomerID], [o2].[EmployeeID], [o2].[OrderDate], ROW_NUMBER() OVER(PARTITION BY [o2].[OrderID] ORDER BY [o2].[OrderID]) AS [row] + SELECT [o2].[OrderID], [o2].[CustomerID], [o2].[EmployeeID], [o2].[OrderDate], [o1].[OrderID] AS [OrderID0], [o1].[ProductID], ROW_NUMBER() OVER(PARTITION BY [o2].[OrderID] ORDER BY [o2].[OrderID]) AS [row] FROM [Order Details] AS [o1] INNER JOIN [Orders] AS [o2] ON [o1].[OrderID] = [o2].[OrderID] WHERE [o1].[OrderID] = 10248 ) AS [t1] WHERE [t1].[row] <= 1 ) AS [t0] ON [t].[OrderID] = [t0].[OrderID] -ORDER BY [t].[OrderID], [t0].[OrderID]", +ORDER BY [t].[OrderID], [t0].[OrderID0], [t0].[ProductID], [t0].[OrderID]", // - @"SELECT [o3].[OrderID], [o3].[ProductID], [o3].[Discount], [o3].[Quantity], [o3].[UnitPrice], [t].[OrderID], [t0].[OrderID] + @"SELECT [o3].[OrderID], [o3].[ProductID], [o3].[Discount], [o3].[Quantity], [o3].[UnitPrice], [t].[OrderID], [t0].[OrderID0], [t0].[ProductID], [t0].[OrderID] FROM ( SELECT [o0].[OrderID] FROM [Order Details] AS [o] @@ -1579,9 +1579,9 @@ FROM [Order Details] AS [o] GROUP BY [o0].[OrderID] ) AS [t] LEFT JOIN ( - SELECT [t1].[OrderID] + SELECT [t1].[OrderID], [t1].[OrderID0], [t1].[ProductID] FROM ( - SELECT [o2].[OrderID], ROW_NUMBER() OVER(PARTITION BY [o2].[OrderID] ORDER BY [o2].[OrderID]) AS [row] + SELECT [o2].[OrderID], [o1].[OrderID] AS [OrderID0], [o1].[ProductID], ROW_NUMBER() OVER(PARTITION BY [o2].[OrderID] ORDER BY [o2].[OrderID]) AS [row] FROM [Order Details] AS [o1] INNER JOIN [Orders] AS [o2] ON [o1].[OrderID] = [o2].[OrderID] WHERE [o1].[OrderID] = 10248 @@ -1589,7 +1589,7 @@ FROM [Order Details] AS [o1] WHERE [t1].[row] <= 1 ) AS [t0] ON [t].[OrderID] = [t0].[OrderID] INNER JOIN [Order Details] AS [o3] ON [t0].[OrderID] = [o3].[OrderID] -ORDER BY [t].[OrderID], [t0].[OrderID]"); +ORDER BY [t].[OrderID], [t0].[OrderID0], [t0].[ProductID], [t0].[OrderID]"); } public override async Task Join_Include_reference_GroupBy_Select(bool async) @@ -1621,7 +1621,7 @@ public override async Task Include_collection_SelectMany_GroupBy_Select(bool asy await base.Include_collection_SelectMany_GroupBy_Select(async); AssertSql( - @"SELECT [t0].[OrderID], [t0].[CustomerID], [t0].[EmployeeID], [t0].[OrderDate], [t].[OrderID] + @"SELECT [t0].[OrderID], [t0].[CustomerID], [t0].[EmployeeID], [t0].[OrderDate], [t].[OrderID], [t0].[OrderID0], [t0].[ProductID] FROM ( SELECT [o].[OrderID] FROM [Orders] AS [o] @@ -1630,18 +1630,18 @@ CROSS JOIN [Order Details] AS [o0] GROUP BY [o].[OrderID] ) AS [t] LEFT JOIN ( - SELECT [t1].[OrderID], [t1].[CustomerID], [t1].[EmployeeID], [t1].[OrderDate] + SELECT [t1].[OrderID], [t1].[CustomerID], [t1].[EmployeeID], [t1].[OrderDate], [t1].[OrderID0], [t1].[ProductID] FROM ( - SELECT [o1].[OrderID], [o1].[CustomerID], [o1].[EmployeeID], [o1].[OrderDate], ROW_NUMBER() OVER(PARTITION BY [o1].[OrderID] ORDER BY [o1].[OrderID]) AS [row] + SELECT [o1].[OrderID], [o1].[CustomerID], [o1].[EmployeeID], [o1].[OrderDate], [o2].[OrderID] AS [OrderID0], [o2].[ProductID], ROW_NUMBER() OVER(PARTITION BY [o1].[OrderID] ORDER BY [o1].[OrderID]) AS [row] FROM [Orders] AS [o1] CROSS JOIN [Order Details] AS [o2] WHERE [o1].[OrderID] = 10248 ) AS [t1] WHERE [t1].[row] <= 1 ) AS [t0] ON [t].[OrderID] = [t0].[OrderID] -ORDER BY [t].[OrderID], [t0].[OrderID]", +ORDER BY [t].[OrderID], [t0].[OrderID], [t0].[OrderID0], [t0].[ProductID]", // - @"SELECT [o3].[OrderID], [o3].[ProductID], [o3].[Discount], [o3].[Quantity], [o3].[UnitPrice], [t].[OrderID], [t0].[OrderID] + @"SELECT [o3].[OrderID], [o3].[ProductID], [o3].[Discount], [o3].[Quantity], [o3].[UnitPrice], [t].[OrderID], [t0].[OrderID], [t0].[OrderID0], [t0].[ProductID] FROM ( SELECT [o].[OrderID] FROM [Orders] AS [o] @@ -1650,9 +1650,9 @@ CROSS JOIN [Order Details] AS [o0] GROUP BY [o].[OrderID] ) AS [t] LEFT JOIN ( - SELECT [t1].[OrderID] + SELECT [t1].[OrderID], [t1].[OrderID0], [t1].[ProductID] FROM ( - SELECT [o1].[OrderID], ROW_NUMBER() OVER(PARTITION BY [o1].[OrderID] ORDER BY [o1].[OrderID]) AS [row] + SELECT [o1].[OrderID], [o2].[OrderID] AS [OrderID0], [o2].[ProductID], ROW_NUMBER() OVER(PARTITION BY [o1].[OrderID] ORDER BY [o1].[OrderID]) AS [row] FROM [Orders] AS [o1] CROSS JOIN [Order Details] AS [o2] WHERE [o1].[OrderID] = 10248 @@ -1660,7 +1660,7 @@ CROSS JOIN [Order Details] AS [o2] WHERE [t1].[row] <= 1 ) AS [t0] ON [t].[OrderID] = [t0].[OrderID] INNER JOIN [Order Details] AS [o3] ON [t0].[OrderID] = [o3].[OrderID] -ORDER BY [t].[OrderID], [t0].[OrderID]"); +ORDER BY [t].[OrderID], [t0].[OrderID], [t0].[OrderID0], [t0].[ProductID]"); } public override async Task Include_reference_SelectMany_GroupBy_Select(bool async) @@ -1694,7 +1694,7 @@ public override async Task SelectMany_Include_collection_GroupBy_Select(bool asy await base.SelectMany_Include_collection_GroupBy_Select(async); AssertSql( - @"SELECT [t0].[OrderID], [t0].[CustomerID], [t0].[EmployeeID], [t0].[OrderDate], [t].[OrderID] + @"SELECT [t0].[OrderID], [t0].[CustomerID], [t0].[EmployeeID], [t0].[OrderDate], [t].[OrderID], [t0].[OrderID0], [t0].[ProductID] FROM ( SELECT [o0].[OrderID] FROM [Order Details] AS [o] @@ -1703,18 +1703,18 @@ CROSS JOIN [Orders] AS [o0] GROUP BY [o0].[OrderID] ) AS [t] LEFT JOIN ( - SELECT [t1].[OrderID], [t1].[CustomerID], [t1].[EmployeeID], [t1].[OrderDate] + SELECT [t1].[OrderID], [t1].[CustomerID], [t1].[EmployeeID], [t1].[OrderDate], [t1].[OrderID0], [t1].[ProductID] FROM ( - SELECT [o2].[OrderID], [o2].[CustomerID], [o2].[EmployeeID], [o2].[OrderDate], ROW_NUMBER() OVER(PARTITION BY [o2].[OrderID] ORDER BY [o2].[OrderID]) AS [row] + SELECT [o2].[OrderID], [o2].[CustomerID], [o2].[EmployeeID], [o2].[OrderDate], [o1].[OrderID] AS [OrderID0], [o1].[ProductID], ROW_NUMBER() OVER(PARTITION BY [o2].[OrderID] ORDER BY [o2].[OrderID]) AS [row] FROM [Order Details] AS [o1] CROSS JOIN [Orders] AS [o2] WHERE [o1].[OrderID] = 10248 ) AS [t1] WHERE [t1].[row] <= 1 ) AS [t0] ON [t].[OrderID] = [t0].[OrderID] -ORDER BY [t].[OrderID], [t0].[OrderID]", +ORDER BY [t].[OrderID], [t0].[OrderID0], [t0].[ProductID], [t0].[OrderID]", // - @"SELECT [o3].[OrderID], [o3].[ProductID], [o3].[Discount], [o3].[Quantity], [o3].[UnitPrice], [t].[OrderID], [t0].[OrderID] + @"SELECT [o3].[OrderID], [o3].[ProductID], [o3].[Discount], [o3].[Quantity], [o3].[UnitPrice], [t].[OrderID], [t0].[OrderID0], [t0].[ProductID], [t0].[OrderID] FROM ( SELECT [o0].[OrderID] FROM [Order Details] AS [o] @@ -1723,9 +1723,9 @@ CROSS JOIN [Orders] AS [o0] GROUP BY [o0].[OrderID] ) AS [t] LEFT JOIN ( - SELECT [t1].[OrderID] + SELECT [t1].[OrderID], [t1].[OrderID0], [t1].[ProductID] FROM ( - SELECT [o2].[OrderID], ROW_NUMBER() OVER(PARTITION BY [o2].[OrderID] ORDER BY [o2].[OrderID]) AS [row] + SELECT [o2].[OrderID], [o1].[OrderID] AS [OrderID0], [o1].[ProductID], ROW_NUMBER() OVER(PARTITION BY [o2].[OrderID] ORDER BY [o2].[OrderID]) AS [row] FROM [Order Details] AS [o1] CROSS JOIN [Orders] AS [o2] WHERE [o1].[OrderID] = 10248 @@ -1733,7 +1733,7 @@ CROSS JOIN [Orders] AS [o2] WHERE [t1].[row] <= 1 ) AS [t0] ON [t].[OrderID] = [t0].[OrderID] INNER JOIN [Order Details] AS [o3] ON [t0].[OrderID] = [o3].[OrderID] -ORDER BY [t].[OrderID], [t0].[OrderID]"); +ORDER BY [t].[OrderID], [t0].[OrderID0], [t0].[ProductID], [t0].[OrderID]"); } public override async Task SelectMany_Include_reference_GroupBy_Select(bool async) diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/TPTGearsOfWarQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/TPTGearsOfWarQuerySqlServerTest.cs index 14dcaaf851e..10058b6924e 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/TPTGearsOfWarQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/TPTGearsOfWarQuerySqlServerTest.cs @@ -6539,7 +6539,7 @@ LEFT JOIN ( FROM ( SELECT [g0].[Nickname], [g0].[SquadId], [g0].[AssignedCityName], [g0].[CityOfBirthName], [g0].[FullName], [g0].[HasSoulPatch], [g0].[LeaderNickname], [g0].[LeaderSquadId], [g0].[Rank], CASE WHEN [o0].[Nickname] IS NOT NULL THEN N'Officer' - END AS [Discriminator], [c].[Name], [c].[Location], [c].[Nation], ROW_NUMBER() OVER(PARTITION BY [g0].[Rank] ORDER BY [g0].[Rank], [c].[Name]) AS [row] + END AS [Discriminator], [c].[Name], [c].[Location], [c].[Nation], ROW_NUMBER() OVER(PARTITION BY [g0].[Rank] ORDER BY [g0].[Nickname], [g0].[SquadId], [c].[Name]) AS [row] FROM [Gears] AS [g0] LEFT JOIN [Officers] AS [o0] ON ([g0].[Nickname] = [o0].[Nickname]) AND ([g0].[SquadId] = [o0].[SquadId]) INNER JOIN [Cities] AS [c] ON [g0].[CityOfBirthName] = [c].[Name]