From 6fde45116bda181d371bd08effede5810b211885 Mon Sep 17 00:00:00 2001 From: Smit Patel Date: Tue, 24 May 2022 11:32:58 -0700 Subject: [PATCH] Query: Don't add grouping key to projection when Distinct is applied (#28072) Resolves #28039 --- .../Query/SqlExpressions/SelectExpression.cs | 3 +- .../Query/NorthwindGroupByQueryTestBase.cs | 15 +++++ .../Query/SimpleQueryTestBase.cs | 64 +++++++++++++++++++ .../NorthwindGroupByQuerySqlServerTest.cs | 18 ++++++ .../Query/SimpleQuerySqlServerTest.cs | 20 ++++++ 5 files changed, 119 insertions(+), 1 deletion(-) diff --git a/src/EFCore.Relational/Query/SqlExpressions/SelectExpression.cs b/src/EFCore.Relational/Query/SqlExpressions/SelectExpression.cs index b0fe728d429..06d036d5585 100644 --- a/src/EFCore.Relational/Query/SqlExpressions/SelectExpression.cs +++ b/src/EFCore.Relational/Query/SqlExpressions/SelectExpression.cs @@ -2993,7 +2993,8 @@ private SqlRemappingVisitor PushdownIntoSubqueryInternal() } } - if (subquery._groupBy.Count > 0) + if (subquery._groupBy.Count > 0 + && !subquery.IsDistinct) { foreach (var key in subquery._groupBy) { diff --git a/test/EFCore.Specification.Tests/Query/NorthwindGroupByQueryTestBase.cs b/test/EFCore.Specification.Tests/Query/NorthwindGroupByQueryTestBase.cs index 3632e44248c..3a17945c2f6 100644 --- a/test/EFCore.Specification.Tests/Query/NorthwindGroupByQueryTestBase.cs +++ b/test/EFCore.Specification.Tests/Query/NorthwindGroupByQueryTestBase.cs @@ -1854,6 +1854,21 @@ from g in grouping }, entryCount: 63); + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Join_GroupBy_Aggregate_distinct_single_join(bool async) + => AssertQuery( + async, + ss => + from c in ss.Set() + join a in ss.Set().GroupBy(o => new { o.CustomerID, o.OrderDate.Value.Year } ) + .Where(g => g.Count() > 5) + .Select(g => new { CustomerID = g.Key.CustomerID, LastOrderID = g.Max(o => o.OrderID) }) + .Distinct() + on c.CustomerID equals a.CustomerID + select new { c, a.LastOrderID }, + entryCount: 31); + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task Join_GroupBy_Aggregate_with_left_join(bool async) diff --git a/test/EFCore.Specification.Tests/Query/SimpleQueryTestBase.cs b/test/EFCore.Specification.Tests/Query/SimpleQueryTestBase.cs index 287d04ee106..730c0ad738d 100644 --- a/test/EFCore.Specification.Tests/Query/SimpleQueryTestBase.cs +++ b/test/EFCore.Specification.Tests/Query/SimpleQueryTestBase.cs @@ -1134,4 +1134,68 @@ protected class Child26744 public DateTime? SomeOtherNullableDateTime { get; set; } public Parent26744 Parent { get; set; } } + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual async Task Pushdown_does_not_add_grouping_key_to_projection_when_distinct_is_applied(bool async) + { + var contextFactory = await InitializeAsync(); + using var db = contextFactory.CreateContext(); + + + var queryResults = (from i in db.IndexData.Where(a => a.Parcel == "some condition") + .Select(a => new SearchResult { ParcelNumber = a.Parcel, RowId = a.RowId }) + group i by new { i.ParcelNumber, i.RowId } into grp + where grp.Count() == 1 + select grp.Key.ParcelNumber).Distinct(); + + var jsonLookup = (from dcv in db.TableData.Where(a => a.TableId == 123) + join wos in queryResults + on dcv.ParcelNumber equals wos + orderby dcv.ParcelNumber + select dcv.JSON).Take(123456); + + + var result = async + ? await jsonLookup.ToListAsync() + : jsonLookup.ToList(); + } + + protected class Context28039 : DbContext + { + public Context28039(DbContextOptions options) + : base(options) + { + } + + public DbSet IndexData { get; set; } + public DbSet TableData { get; set; } + } + + public class TableData : EntityBase + { + public int TableId { get; set; } + public string ParcelNumber { get; set; } + public short RowId { get; set; } + public string JSON { get; set; } + + } + + public abstract class EntityBase + { + [Key] + public int ID { get; set; } + } + public class IndexData : EntityBase + { + public string Parcel { get; set; } + public int RowId { get; set; } + } + + internal class SearchResult + { + public string ParcelNumber { get; set; } + public int RowId { get; set; } + public string DistinctValue { get; set; } + } } diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindGroupByQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindGroupByQuerySqlServerTest.cs index c3f4165f465..4244e4018bf 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindGroupByQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindGroupByQuerySqlServerTest.cs @@ -1589,6 +1589,24 @@ HAVING COUNT(*) > 5 INNER JOIN [Orders] AS [o0] ON [c].[CustomerID] = [o0].[CustomerID]"); } + public override async Task Join_GroupBy_Aggregate_distinct_single_join(bool async) + { + await base.Join_GroupBy_Aggregate_distinct_single_join(async); + + AssertSql( + @"SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region], [t0].[LastOrderID] +FROM [Customers] AS [c] +INNER JOIN ( + SELECT DISTINCT [t].[CustomerID], MAX([t].[OrderID]) AS [LastOrderID] + FROM ( + SELECT [o].[OrderID], [o].[CustomerID], DATEPART(year, [o].[OrderDate]) AS [Year] + FROM [Orders] AS [o] + ) AS [t] + GROUP BY [t].[CustomerID], [t].[Year] + HAVING COUNT(*) > 5 +) AS [t0] ON [c].[CustomerID] = [t0].[CustomerID]"); + } + public override async Task Join_GroupBy_Aggregate_with_left_join(bool async) { await base.Join_GroupBy_Aggregate_with_left_join(async); diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/SimpleQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/SimpleQuerySqlServerTest.cs index 8a9abf8b137..316b08e54bb 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/SimpleQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/SimpleQuerySqlServerTest.cs @@ -409,4 +409,24 @@ FROM [MyEntities] AS [m] FROM [MyEntities] AS [m] WHERE [dbo].[ModifyDate]([m].[SomeDate]) = @__date_0"); } + + public override async Task Pushdown_does_not_add_grouping_key_to_projection_when_distinct_is_applied(bool async) + { + await base.Pushdown_does_not_add_grouping_key_to_projection_when_distinct_is_applied(async); + + AssertSql( + @"@__p_0='123456' + +SELECT TOP(@__p_0) [t].[JSON] +FROM [TableData] AS [t] +INNER JOIN ( + SELECT DISTINCT [i].[Parcel] + FROM [IndexData] AS [i] + WHERE [i].[Parcel] = N'some condition' + GROUP BY [i].[Parcel], [i].[RowId] + HAVING COUNT(*) = 1 +) AS [t0] ON [t].[ParcelNumber] = [t0].[Parcel] +WHERE [t].[TableId] = 123 +ORDER BY [t].[ParcelNumber]"); + } }