Efcore: The query uses a row limiting operator ('Skip'/'Take') without an 'OrderBy' operator. This may lead to unpredictable results.

Created on 20 Oct 2020  路  2Comments  路  Source: dotnet/efcore

I have a very simple view that once I call this view with EF Core, I get the warning

The query uses a row limiting operator ('Skip'/'Take') without an 'OrderBy' operator. This may lead to unpredictable results.

ALTER VIEW [dbo].[GridListings]

AS 

SELECT  l.*,
c.Name as city, r.code AS region, cn.code AS country, '' as url, '' as thumbnail

FROM [dbo].Listings l
    INNER JOIN Cities c ON l.CityId = c.CityId
    INNER JOIN Regions r ON r.RegionId = c.RegionId
    INNER JOIN Countries cn ON cn.CountryId = r.CountryId
GO

```C#
var entities = await _context.GridListings
.Skip(offset)
.Take(request.PageSize)
.OrderBy(x => x.Createddate)
.ToListAsync();


Generated SQL (Sql Server Profiler)
```tsql
exec sp_executesql N'SELECT [t].[Address], [t].[AvailableDate], [t].[Baths], [t].[BedroomsAvailable], [t].[City], [t].[Country], [t].[Createddate], [t].[Description], [t].[ExpiryDate], [t].[Has24HourSecurity], [t].[HasAccessibleWashroomsInSuite], [t].[HasAirConditioning], [t].[HasAudioPrompts], [t].[HasBalcony], [t].[HasBarrierFreeEntranceAndRamp], [t].[HasBicyleParking], [t].[HasBrailleLabels], [t].[HasConcierge], [t].[HasDishwasher], [t].[HasElevator], [t].[HasFridge], [t].[HasGym], [t].[HasLandline], [t].[HasLaundryInBuilding], [t].[HasLaundryInUnit], [t].[HasPool], [t].[HasRentDeferral], [t].[HasStorageLocker], [t].[HasVisualAids], [t].[HasYard], [t].[ImportWebsite], [t].[ImportedContactInfo], [t].[ImportedEmail], [t].[ImportedPhoneNumber], [t].[ImportedReferenceNumber], [t].[ImportedSourceId], [t].[IsAutoGenerated], [t].[IsCableIncluded], [t].[IsDenIncluded], [t].[IsFurnished], [t].[IsHeatIncluded], [t].[IsHydroIncluded], [t].[IsInternetIncluded], [t].[IsPetFriendly], [t].[IsSmokingFriendly], [t].[IsWaterIncluded], [t].[IsWheelChairAccessible], [t].[LastUpdatedDate], [t].[LeaseDuration], [t].[ListingId], [t].[Location], [t].[NumberParkingSpotsIncluded], [t].[Price], [t].[Region], [t].[RentalsVirtualOptions], [t].[Sqft], [t].[Thumbnail], [t].[Title], [t].[UnitTypeId], [t].[Url]
FROM (
    SELECT [g].[Address], [g].[AvailableDate], [g].[Baths], [g].[BedroomsAvailable], [g].[City], [g].[Country], [g].[Createddate], [g].[Description], [g].[ExpiryDate], [g].[Has24HourSecurity], [g].[HasAccessibleWashroomsInSuite], [g].[HasAirConditioning], [g].[HasAudioPrompts], [g].[HasBalcony], [g].[HasBarrierFreeEntranceAndRamp], [g].[HasBicyleParking], [g].[HasBrailleLabels], [g].[HasConcierge], [g].[HasDishwasher], [g].[HasElevator], [g].[HasFridge], [g].[HasGym], [g].[HasLandline], [g].[HasLaundryInBuilding], [g].[HasLaundryInUnit], [g].[HasPool], [g].[HasRentDeferral], [g].[HasStorageLocker], [g].[HasVisualAids], [g].[HasYard], [g].[ImportWebsite], [g].[ImportedContactInfo], [g].[ImportedEmail], [g].[ImportedPhoneNumber], [g].[ImportedReferenceNumber], [g].[ImportedSourceId], [g].[IsAutoGenerated], [g].[IsCableIncluded], [g].[IsDenIncluded], [g].[IsFurnished], [g].[IsHeatIncluded], [g].[IsHydroIncluded], [g].[IsInternetIncluded], [g].[IsPetFriendly], [g].[IsSmokingFriendly], [g].[IsWaterIncluded], [g].[IsWheelChairAccessible], [g].[LastUpdatedDate], [g].[LeaseDuration], [g].[ListingId], [g].[Location], [g].[NumberParkingSpotsIncluded], [g].[Price], [g].[Region], [g].[RentalsVirtualOptions], [g].[Sqft], [g].[Thumbnail], [g].[Title], [g].[UnitTypeId], [g].[Url]
    FROM [GridListings] AS [g]
    ORDER BY (SELECT 1)
    OFFSET @__p_0 ROWS FETCH NEXT @__p_1 ROWS ONLY
) AS [t]
ORDER BY [t].[Createddate]',N'@__p_0 int,@__p_1 int',@__p_0=0,@__p_1=21

EF Core version: 5.0 RC2

closed-question customer-reported

Most helpful comment

Warning happens before Skip/Take are applied before OrderBy. That means Skip/Take would be applied on non-deterministic ordering hence the warning. Please apply OrderBy before Skip/Take so you get consistent results.

All 2 comments

The below code sample works well for me with the latest 5.0 builds. Can you please tweak it so that it fails, or submit a full, runnable code sample?


Attempted repro

```c#
await using var ctx = new GroupListingContext();
await ctx.Database.EnsureDeletedAsync();
await ctx.Database.EnsureCreatedAsync();

await ctx.Database.ExecuteSqlRawAsync(@"
CREATE VIEW [GridListings] AS
SELECT 1 AS [Id], 'SomeName' AS [Name], CAST ('2020-01-01' AS datetime2) AS [Createddate]");

var offset = 1;
var limit = 1;

_ = await ctx.GridListings
.Skip(offset)
.Take(limit)
.OrderBy(x => x.Createddate)
.ToListAsync();

public class GroupListingContext : DbContext
{
public DbSet GridListings { get; set; }

static ILoggerFactory ContextLoggerFactory
    => LoggerFactory.Create(b => b.AddConsole().AddFilter("", LogLevel.Information));

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    => optionsBuilder
        .UseSqlServer(...)
        .EnableSensitiveDataLogging()
        .UseLoggerFactory(ContextLoggerFactory);

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<GridListings>().ToView("GridListings");
}

}

public class GridListings
{
public int Id { get; set; }
public string Name { get; set; }
public DateTime Createddate { get; set; }
}
```

Warning happens before Skip/Take are applied before OrderBy. That means Skip/Take would be applied on non-deterministic ordering hence the warning. Please apply OrderBy before Skip/Take so you get consistent results.

Was this page helpful?
0 / 5 - 0 ratings