diff --git a/src/EFCore.Relational/Query/SqlExpressions/SelectExpression.cs b/src/EFCore.Relational/Query/SqlExpressions/SelectExpression.cs index 0a761956861..d57e2210403 100644 --- a/src/EFCore.Relational/Query/SqlExpressions/SelectExpression.cs +++ b/src/EFCore.Relational/Query/SqlExpressions/SelectExpression.cs @@ -269,6 +269,11 @@ public void ApplyDistinct() throw new InvalidOperationException(RelationalStrings.DistinctOnCollectionNotSupported); } + if (Limit is SqlConstantExpression { Value: 1 }) + { + return; + } + if (Limit != null || Offset != null) { @@ -1915,6 +1920,11 @@ public void ApplyLimit(SqlExpression sqlExpression) } Limit = sqlExpression; + + if (Offset is null && Limit is SqlConstantExpression { Value: 1 }) + { + IsDistinct = false; + } } /// diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsQuerySqlServer160Test.cs b/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsQuerySqlServer160Test.cs index 7702a3ac4e6..5628b696ada 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsQuerySqlServer160Test.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsQuerySqlServer160Test.cs @@ -69,7 +69,7 @@ where l1.Id < 3 SELECT ( SELECT TOP(1) [l1].[Name] FROM ( - SELECT DISTINCT TOP(1) [l0].[Id], [l0].[Level2_Optional_Id], [l0].[Level2_Required_Id], [l0].[Name], [l0].[OneToMany_Optional_Inverse3Id], [l0].[OneToMany_Optional_Self_Inverse3Id], [l0].[OneToMany_Required_Inverse3Id], [l0].[OneToMany_Required_Self_Inverse3Id], [l0].[OneToOne_Optional_PK_Inverse3Id], [l0].[OneToOne_Optional_Self3Id] + SELECT TOP(1) [l0].[Id], [l0].[Name] FROM [LevelThree] AS [l0] ) AS [l1] ORDER BY [l1].[Id]) diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsQuerySqlServerTest.cs index 1d20f4b4a60..4b9b1070b00 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsQuerySqlServerTest.cs @@ -69,7 +69,7 @@ where l1.Id < 3 SELECT ( SELECT TOP(1) [l1].[Name] FROM ( - SELECT DISTINCT TOP(1) [l0].[Id], [l0].[Level2_Optional_Id], [l0].[Level2_Required_Id], [l0].[Name], [l0].[OneToMany_Optional_Inverse3Id], [l0].[OneToMany_Optional_Self_Inverse3Id], [l0].[OneToMany_Required_Inverse3Id], [l0].[OneToMany_Required_Self_Inverse3Id], [l0].[OneToOne_Optional_PK_Inverse3Id], [l0].[OneToOne_Optional_Self3Id] + SELECT TOP(1) [l0].[Id], [l0].[Name] FROM [LevelThree] AS [l0] ) AS [l1] ORDER BY [l1].[Id]) diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsSharedTypeQuerySqlServer160Test.cs b/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsSharedTypeQuerySqlServer160Test.cs index ce59551f86f..3ea3c2abe3d 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsSharedTypeQuerySqlServer160Test.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsSharedTypeQuerySqlServer160Test.cs @@ -87,7 +87,7 @@ where l1.Id < 3 SELECT ( SELECT TOP(1) [s].[Level3_Name] FROM ( - SELECT DISTINCT TOP(1) [l4].[Id], [l4].[Level2_Optional_Id], [l4].[Level2_Required_Id], [l4].[Level3_Name], [l4].[OneToMany_Optional_Inverse3Id], [l4].[OneToMany_Required_Inverse3Id], [l4].[OneToOne_Optional_PK_Inverse3Id] + SELECT TOP(1) [l4].[Id], [l4].[Level2_Required_Id], [l4].[Level3_Name], [l4].[OneToMany_Required_Inverse3Id] FROM [Level1] AS [l0] LEFT JOIN ( SELECT [l1].[Id], [l1].[OneToOne_Required_PK_Date], [l1].[Level1_Required_Id], [l1].[OneToMany_Required_Inverse2Id] @@ -97,7 +97,7 @@ WHERE [l1].[OneToOne_Required_PK_Date] IS NOT NULL AND [l1].[Level1_Required_Id] WHEN [l2].[OneToOne_Required_PK_Date] IS NOT NULL AND [l2].[Level1_Required_Id] IS NOT NULL AND [l2].[OneToMany_Required_Inverse2Id] IS NOT NULL THEN [l2].[Id] END LEFT JOIN ( - SELECT [l3].[Id], [l3].[Level2_Optional_Id], [l3].[Level2_Required_Id], [l3].[Level3_Name], [l3].[OneToMany_Optional_Inverse3Id], [l3].[OneToMany_Required_Inverse3Id], [l3].[OneToOne_Optional_PK_Inverse3Id] + SELECT [l3].[Id], [l3].[Level2_Required_Id], [l3].[Level3_Name], [l3].[OneToMany_Required_Inverse3Id] FROM [Level1] AS [l3] WHERE [l3].[Level2_Required_Id] IS NOT NULL AND [l3].[OneToMany_Required_Inverse3Id] IS NOT NULL ) AS [l4] ON CASE diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsSharedTypeQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsSharedTypeQuerySqlServerTest.cs index dfd4f46b829..00aba8b691e 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsSharedTypeQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsSharedTypeQuerySqlServerTest.cs @@ -89,7 +89,7 @@ where l1.Id < 3 SELECT ( SELECT TOP(1) [s].[Level3_Name] FROM ( - SELECT DISTINCT TOP(1) [l4].[Id], [l4].[Level2_Optional_Id], [l4].[Level2_Required_Id], [l4].[Level3_Name], [l4].[OneToMany_Optional_Inverse3Id], [l4].[OneToMany_Required_Inverse3Id], [l4].[OneToOne_Optional_PK_Inverse3Id] + SELECT TOP(1) [l4].[Id], [l4].[Level2_Required_Id], [l4].[Level3_Name], [l4].[OneToMany_Required_Inverse3Id] FROM [Level1] AS [l0] LEFT JOIN ( SELECT [l1].[Id], [l1].[OneToOne_Required_PK_Date], [l1].[Level1_Required_Id], [l1].[OneToMany_Required_Inverse2Id] @@ -99,7 +99,7 @@ WHERE [l1].[OneToOne_Required_PK_Date] IS NOT NULL AND [l1].[Level1_Required_Id] WHEN [l2].[OneToOne_Required_PK_Date] IS NOT NULL AND [l2].[Level1_Required_Id] IS NOT NULL AND [l2].[OneToMany_Required_Inverse2Id] IS NOT NULL THEN [l2].[Id] END LEFT JOIN ( - SELECT [l3].[Id], [l3].[Level2_Optional_Id], [l3].[Level2_Required_Id], [l3].[Level3_Name], [l3].[OneToMany_Optional_Inverse3Id], [l3].[OneToMany_Required_Inverse3Id], [l3].[OneToOne_Optional_PK_Inverse3Id] + SELECT [l3].[Id], [l3].[Level2_Required_Id], [l3].[Level3_Name], [l3].[OneToMany_Required_Inverse3Id] FROM [Level1] AS [l3] WHERE [l3].[Level2_Required_Id] IS NOT NULL AND [l3].[OneToMany_Required_Inverse3Id] IS NOT NULL ) AS [l4] ON CASE diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/GearsOfWarQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/GearsOfWarQuerySqlServerTest.cs index 14db73c59cb..0a079557a4a 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/GearsOfWarQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/GearsOfWarQuerySqlServerTest.cs @@ -1562,7 +1562,7 @@ public override async Task Where_subquery_distinct_singleordefault_boolean2(bool SELECT [g].[Nickname], [g].[SquadId], [g].[AssignedCityName], [g].[CityOfBirthName], [g].[Discriminator], [g].[FullName], [g].[HasSoulPatch], [g].[LeaderNickname], [g].[LeaderSquadId], [g].[Rank] FROM [Gears] AS [g] WHERE [g].[HasSoulPatch] = CAST(1 AS bit) AND COALESCE(( - SELECT DISTINCT TOP(1) [w].[IsAutomatic] + SELECT TOP(1) [w].[IsAutomatic] FROM [Weapons] AS [w] WHERE [g].[FullName] = [w].[OwnerFullName] AND [w].[Name] LIKE N'%Lancer%'), CAST(0 AS bit)) = CAST(1 AS bit) ORDER BY [g].[Nickname] @@ -5838,7 +5838,7 @@ public override async Task Select_subquery_distinct_singleordefault_boolean2(boo AssertSql( """ SELECT COALESCE(( - SELECT DISTINCT TOP(1) [w].[IsAutomatic] + SELECT TOP(1) [w].[IsAutomatic] FROM [Weapons] AS [w] WHERE [g].[FullName] = [w].[OwnerFullName] AND [w].[Name] LIKE N'%Lancer%'), CAST(0 AS bit)) FROM [Gears] AS [g] @@ -5889,7 +5889,7 @@ public override async Task Select_subquery_distinct_singleordefault_boolean_empt AssertSql( """ SELECT COALESCE(( - SELECT DISTINCT TOP(1) [w].[IsAutomatic] + SELECT TOP(1) [w].[IsAutomatic] FROM [Weapons] AS [w] WHERE [g].[FullName] = [w].[OwnerFullName] AND [w].[Name] = N'BFG'), CAST(0 AS bit)) FROM [Gears] AS [g] diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindSelectQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindSelectQuerySqlServerTest.cs index 7df93c2dc01..f8c1d6e8041 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindSelectQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindSelectQuerySqlServerTest.cs @@ -795,7 +795,7 @@ public override async Task Project_single_element_from_collection_with_OrderBy_D AssertSql( """ SELECT ( - SELECT DISTINCT TOP(1) [o].[CustomerID] + SELECT TOP(1) [o].[CustomerID] FROM [Orders] AS [o] WHERE [c].[CustomerID] = [o].[CustomerID]) FROM [Customers] AS [c] diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/TPCGearsOfWarQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/TPCGearsOfWarQuerySqlServerTest.cs index f8d41c87f63..160949226d5 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/TPCGearsOfWarQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/TPCGearsOfWarQuerySqlServerTest.cs @@ -2230,7 +2230,7 @@ UNION ALL FROM [Officers] AS [o] ) AS [u] WHERE [u].[HasSoulPatch] = CAST(1 AS bit) AND COALESCE(( - SELECT DISTINCT TOP(1) [w].[IsAutomatic] + SELECT TOP(1) [w].[IsAutomatic] FROM [Weapons] AS [w] WHERE [u].[FullName] = [w].[OwnerFullName] AND [w].[Name] LIKE N'%Lancer%'), CAST(0 AS bit)) = CAST(1 AS bit) ORDER BY [u].[Nickname] @@ -7957,7 +7957,7 @@ public override async Task Select_subquery_distinct_singleordefault_boolean2(boo AssertSql( """ SELECT COALESCE(( - SELECT DISTINCT TOP(1) [w].[IsAutomatic] + SELECT TOP(1) [w].[IsAutomatic] FROM [Weapons] AS [w] WHERE [u].[FullName] = [w].[OwnerFullName] AND [w].[Name] LIKE N'%Lancer%'), CAST(0 AS bit)) FROM ( @@ -8026,7 +8026,7 @@ public override async Task Select_subquery_distinct_singleordefault_boolean_empt AssertSql( """ SELECT COALESCE(( - SELECT DISTINCT TOP(1) [w].[IsAutomatic] + SELECT TOP(1) [w].[IsAutomatic] FROM [Weapons] AS [w] WHERE [u].[FullName] = [w].[OwnerFullName] AND [w].[Name] = N'BFG'), CAST(0 AS bit)) FROM ( diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/TPTGearsOfWarQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/TPTGearsOfWarQuerySqlServerTest.cs index e2260d6cc08..9420310aecc 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/TPTGearsOfWarQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/TPTGearsOfWarQuerySqlServerTest.cs @@ -1859,7 +1859,7 @@ END AS [Discriminator] FROM [Gears] AS [g] LEFT JOIN [Officers] AS [o] ON [g].[Nickname] = [o].[Nickname] AND [g].[SquadId] = [o].[SquadId] WHERE [g].[HasSoulPatch] = CAST(1 AS bit) AND COALESCE(( - SELECT DISTINCT TOP(1) [w].[IsAutomatic] + SELECT TOP(1) [w].[IsAutomatic] FROM [Weapons] AS [w] WHERE [g].[FullName] = [w].[OwnerFullName] AND [w].[Name] LIKE N'%Lancer%'), CAST(0 AS bit)) = CAST(1 AS bit) ORDER BY [g].[Nickname] @@ -6678,7 +6678,7 @@ public override async Task Select_subquery_distinct_singleordefault_boolean2(boo AssertSql( """ SELECT COALESCE(( - SELECT DISTINCT TOP(1) [w].[IsAutomatic] + SELECT TOP(1) [w].[IsAutomatic] FROM [Weapons] AS [w] WHERE [g].[FullName] = [w].[OwnerFullName] AND [w].[Name] LIKE N'%Lancer%'), CAST(0 AS bit)) FROM [Gears] AS [g] @@ -6729,7 +6729,7 @@ public override async Task Select_subquery_distinct_singleordefault_boolean_empt AssertSql( """ SELECT COALESCE(( - SELECT DISTINCT TOP(1) [w].[IsAutomatic] + SELECT TOP(1) [w].[IsAutomatic] FROM [Weapons] AS [w] WHERE [g].[FullName] = [w].[OwnerFullName] AND [w].[Name] = N'BFG'), CAST(0 AS bit)) FROM [Gears] AS [g] diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/TemporalGearsOfWarQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/TemporalGearsOfWarQuerySqlServerTest.cs index 6895ee561b1..73d5554d42f 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/TemporalGearsOfWarQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/TemporalGearsOfWarQuerySqlServerTest.cs @@ -1536,7 +1536,7 @@ public override async Task Select_subquery_distinct_singleordefault_boolean2(boo AssertSql( """ SELECT COALESCE(( - SELECT DISTINCT TOP(1) [w].[IsAutomatic] + SELECT TOP(1) [w].[IsAutomatic] FROM [Weapons] FOR SYSTEM_TIME AS OF '2010-01-01T00:00:00.0000000' AS [w] WHERE [g].[FullName] = [w].[OwnerFullName] AND [w].[Name] LIKE N'%Lancer%'), CAST(0 AS bit)) FROM [Gears] FOR SYSTEM_TIME AS OF '2010-01-01T00:00:00.0000000' AS [g] @@ -2395,7 +2395,7 @@ public override async Task Select_subquery_distinct_singleordefault_boolean_empt AssertSql( """ SELECT COALESCE(( - SELECT DISTINCT TOP(1) [w].[IsAutomatic] + SELECT TOP(1) [w].[IsAutomatic] FROM [Weapons] FOR SYSTEM_TIME AS OF '2010-01-01T00:00:00.0000000' AS [w] WHERE [g].[FullName] = [w].[OwnerFullName] AND [w].[Name] = N'BFG'), CAST(0 AS bit)) FROM [Gears] FOR SYSTEM_TIME AS OF '2010-01-01T00:00:00.0000000' AS [g] @@ -6607,7 +6607,7 @@ public override async Task Where_subquery_distinct_singleordefault_boolean2(bool SELECT [g].[Nickname], [g].[SquadId], [g].[AssignedCityName], [g].[CityOfBirthName], [g].[Discriminator], [g].[FullName], [g].[HasSoulPatch], [g].[LeaderNickname], [g].[LeaderSquadId], [g].[PeriodEnd], [g].[PeriodStart], [g].[Rank] FROM [Gears] FOR SYSTEM_TIME AS OF '2010-01-01T00:00:00.0000000' AS [g] WHERE [g].[HasSoulPatch] = CAST(1 AS bit) AND COALESCE(( - SELECT DISTINCT TOP(1) [w].[IsAutomatic] + SELECT TOP(1) [w].[IsAutomatic] FROM [Weapons] FOR SYSTEM_TIME AS OF '2010-01-01T00:00:00.0000000' AS [w] WHERE [g].[FullName] = [w].[OwnerFullName] AND [w].[Name] LIKE N'%Lancer%'), CAST(0 AS bit)) = CAST(1 AS bit) ORDER BY [g].[Nickname] diff --git a/test/EFCore.Sqlite.FunctionalTests/Query/GearsOfWarQuerySqliteTest.cs b/test/EFCore.Sqlite.FunctionalTests/Query/GearsOfWarQuerySqliteTest.cs index 6fff187cf93..328f42a797b 100644 --- a/test/EFCore.Sqlite.FunctionalTests/Query/GearsOfWarQuerySqliteTest.cs +++ b/test/EFCore.Sqlite.FunctionalTests/Query/GearsOfWarQuerySqliteTest.cs @@ -2335,7 +2335,7 @@ public override async Task Where_subquery_distinct_singleordefault_boolean2(bool SELECT "g"."Nickname", "g"."SquadId", "g"."AssignedCityName", "g"."CityOfBirthName", "g"."Discriminator", "g"."FullName", "g"."HasSoulPatch", "g"."LeaderNickname", "g"."LeaderSquadId", "g"."Rank" FROM "Gears" AS "g" WHERE "g"."HasSoulPatch" AND COALESCE(( - SELECT DISTINCT "w"."IsAutomatic" + SELECT "w"."IsAutomatic" FROM "Weapons" AS "w" WHERE "g"."FullName" = "w"."OwnerFullName" AND instr("w"."Name", 'Lancer') > 0 LIMIT 1), 0) @@ -4852,7 +4852,7 @@ public override async Task Select_subquery_distinct_singleordefault_boolean2(boo AssertSql( """ SELECT COALESCE(( - SELECT DISTINCT "w"."IsAutomatic" + SELECT "w"."IsAutomatic" FROM "Weapons" AS "w" WHERE "g"."FullName" = "w"."OwnerFullName" AND instr("w"."Name", 'Lancer') > 0 LIMIT 1), 0) @@ -7320,7 +7320,7 @@ public override async Task Select_subquery_distinct_singleordefault_boolean_empt AssertSql( """ SELECT COALESCE(( - SELECT DISTINCT "w"."IsAutomatic" + SELECT "w"."IsAutomatic" FROM "Weapons" AS "w" WHERE "g"."FullName" = "w"."OwnerFullName" AND "w"."Name" = 'BFG' LIMIT 1), 0)