簡體   English   中英

實體框架和Top and OrderBy的OData性能不佳

[英]Bad OData Performance with Entity Framework and Top and OrderBy

我正在使用OData 5.8.0和EntityFramework 6.1.3,查詢:

&$filter=fieldA eq 'ABCDEFG'&$skip=0&$top=10&$orderby=fieldB desc

結果是:

SELECT TOP (10) 
    [Project1].[FieldA] AS [FieldA], 
    [Project1].[FieldB] AS [FieldB], 
    FROM ( SELECT [Project1].[FieldA] AS [FieldA], [Project1].[FieldB] AS [FieldB], row_number() OVER (ORDER BY [Project1].[FieldB] DESC, [Project1].[FieldA] ASC) AS [row_number]
        FROM ( SELECT 
            [Extent1].[FieldA] AS [FieldA], 
            [Extent1].[FieldB] AS [FieldB], 
            FROM [dbo].[table] AS [Extent1]
            WHERE ([Extent1].[FieldA] = 'ABCDEFG') OR (([Extent1].[FieldA] IS NULL) AND ('ABCDEFG' IS NULL))
        )  AS [Project1]
    )  AS [Project1]
    WHERE [Project1].[row_number] > 0
    ORDER BY [Project1].[FieldB] DESC, [Project1].[FieldA] ASC

對於大量的字段A,大約需要20秒才能在DB上運行。

如果我使用相同的LINQ:

var newList = table.Where(f => f.fieldA == 'ABCDEFG').OrderByDescending(f => f.fieldB).Take(10).Skip(0).ToList();

結果是:

SELECT 
    [Limit1].[FieldA] AS [FieldA], 
    [Limit1].[FieldB] AS [FieldB]
    FROM ( SELECT [Limit1].[FieldA] AS [FieldA], [Limit1].[FieldB] AS [FieldB], row_number() OVER (ORDER BY [Limit1].[FieldB] DESC) AS [row_number]
        FROM ( SELECT TOP (10) [Project1].[FieldA] AS [FieldA], [Project1].[FieldB] AS [FieldB]
            FROM ( SELECT 
                [Extent1].[FieldA] AS [FieldA], 
                [Extent1].[FieldB] AS [FieldB]
                FROM [dbo].[table] AS [Extent1]
                WHERE ([Extent1].[FieldA] = 'ABCDEFG') OR (([Extent1].[FieldA] IS NULL) AND ('ABCDEFG' IS NULL))
            )  AS [Project1]
            ORDER BY [Project1].[FieldB] DESC
        )  AS [Limit1]
    )  AS [Limit1]
    WHERE [Limit1].[row_number] > 0
    ORDER BY [Limit1].[FieldB] DES

運行需要120毫秒。

如何強制OData使用相同的表達式(即不在外部語句中使用TOP)?

我發現這個問題是OData不是很聰明,並且以錯誤的順序應用查詢選項。 下面的代碼首先應用orderBy,然后應用頂部:

private static IQueryable<Item> ApplyOptimizedOdataOptions(IQueryable<Item> origQuery,  ODataQueryOptions<Item> options)
{
    var defaultOdataQuerySettings = new ODataQuerySettings();
    if (options.Top != null && options.OrderBy != null)
    {
        // We can optimze this query. Apply the OrderBy first, then Top.
        IQueryable results = options.OrderBy.ApplyTo(origQuery, defaultOdataQuerySettings);
        results = options.Top.ApplyTo(results, defaultOdataQuerySettings);
        results = options.ApplyTo(results, defaultOdataQuerySettings, AllowedQueryOptions.Top | AllowedQueryOptions.OrderBy);

        return results as IQueryable<Item>;
    }

    return options.ApplyTo(origQuery, defaultOdataQuerySettings) as IQueryable<Item>;
}

如果我運行通過使用此IQueryable生成的結果SQL語句:

SET STATISTICS TIME ON;
// Run SQL here
SET STATISTICS TIME OFF;

重新排列這些語句會導致:

SQL Server執行時間:CPU時間= 0毫秒,經過的時間= 1毫秒。

與不重新訂購相比:

SQL Server執行時間:CPU時間= 1213 ms,經過的時間= 20112ms。

速度提升約20,000倍。

實際上,orderby應該比top早應用,以便top穩定並創建場景。運行sql時,這是相同的邏輯,總是應該有一個top帶有orderby,或者按默認順序排列,在這種情況下,您應該只使用top,得到結果后再訂購。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM