簡體   English   中英

從IQueryable中刪除OrderBy <T>

[英]Remove OrderBy from an IQueryable<T>

我有一個分頁API,它返回用戶請求的行,但一次只有這么多,而不是整個集合。 API按設計工作,但我必須計算可用的記錄總數(正確的頁面計算)。 在API中,我使用Linq2Sql,在我最終提出請求之前,我在IQueryable上工作了很多。 當我去計算時,我會調用類似於:totalRecordCount = queryable.Count();

結果SQL很有意思,但它也增加了一個不必要的Order By,這使得查詢非常昂貴。

exec sp_executesql N'SELECT COUNT(*) AS [value]
FROM (
    SELECT TOP (1) NULL AS [EMPTY]
    FROM [dbo].[JournalEventsView] AS [t0]
    WHERE [t0].[DataOwnerID] = @p0
    ORDER BY [t0].[DataTimeStamp] DESC
    ) AS [t1]',N'@p0 int',@p0=1

因為我正在使用IQueryable,所以我可以在它進入SQL服務器之前操作IQueryable。

我的問題是,如果我已經有一個帶有OrderBy的IQueryable,是否可以在調用Count()之前刪除該OrderBy?

喜歡:totalRecordCount = queryable。 NoOrder .Count();

如果沒有,沒有大事。 我看到很多關於OrderBy的問題,但沒有涉及從Linq表達式中刪除OrderBy的任何問題。

謝謝!

因此,下面的代碼是針對內存數組的尖峰。 使用Entity Framework(或其他任意IQueryProvider實現)可能存在一些障礙。 基本上,我們要做的是訪問表達式樹並查找任何Ordering方法調用,並將其從樹中刪除。 希望這能指出你正確的方向。

class Program
{
    static void Main(string[] args)
    {
        var seq = new[] { 1, 3, 5, 7, 9, 2, 4, 6, 8 };

        var query = seq.OrderBy(x => x);

        Console.WriteLine("Print out in reverse order.");
        foreach (var item in query)
        {
            Console.WriteLine(item);
        }

        Console.WriteLine("Prints out in original order");
        var queryExpression = seq.AsQueryable().OrderBy(x => x).ThenByDescending(x => x).Expression;

        var queryDelegate = Expression.Lambda<Func<IEnumerable<int>>>(new OrderByRemover().Visit(queryExpression)).Compile();

        foreach (var item in queryDelegate())
        {
            Console.WriteLine(item);
        }


        Console.ReadLine();
    }
}

public class OrderByRemover : ExpressionVisitor
{
    protected override Expression VisitMethodCall(MethodCallExpression node)
    {
        if (node.Method.DeclaringType != typeof(Enumerable) && node.Method.DeclaringType != typeof(Queryable))
            return base.VisitMethodCall(node);

        if (node.Method.Name != "OrderBy" && node.Method.Name != "OrderByDescending" && node.Method.Name != "ThenBy" && node.Method.Name != "ThenByDescending")
            return base.VisitMethodCall(node);

        //eliminate the method call from the expression tree by returning the object of the call.
        return base.Visit(node.Arguments[0]);
    }
}

不僅有一個不需要的ORDER BY,還有一個假的TOP(1)。

SELECT TOP (1) NULL AS [EMPTY] ...

該子選擇只返回0或1行。 事實上,如果沒有TOP,那么在子選擇中使用ORDER BY是不合法的。

ORDER BY子句在視圖,內聯函數,派生表,子查詢和公用表表達式中無效,除非還指定了TOP或FOR XML:SELECT COUNT(*)FROM(SELECT * FROM Table1 ORDER BY foo)

sqlfiddle

我想你的LINQ可能做錯了。 你確定你沒有寫過。在調用.Count()之前,在你的查詢中的某個地方獲取.Take(1)或類似內容?

這是錯的:

IQueryable<Foo> foo = (...).OrderBy(x => x.Foo).Take(1);
int count = foo.Count();

你應該這樣做:

IQueryable<Foo> foo = (...);
Iqueryable<Foo> topOne = foo.OrderBy(x => x.Foo).Take(1);
int count = foo.Count();

我擔心沒有簡單的方法可以從可查詢中刪除OrderBy運算符。

但是,你可以做的是根據從重寫queryable.Expression參見這里 )省略OrderBy調用獲得的新表達式重新創建IQueryable

如果你無法消除根本原因,這里有一個解決方法:

totalRecordCount = queryable.OrderBy(x => 0).Count();

SQL Server的查詢優化器將刪除這種無用的排序。 它不會有運行時成本。

我認為你錯誤地實現了分頁代碼。 實際上,您需要兩次查詢數據庫,一次查詢分頁數據源,一次查詢總行數。 這就是設置的外觀。

public IList<MyObj> GetPagedData(string filter, string sort, int skip, int take)
{
   using(var db = new DataContext())
   {
      var q = GetDataInternal(db);
      if(!String.IsNullOrEmpty(filter))
         q = q.Where(filter); //Using Dynamic linq

      if(!String.IsNullOrEmpty(sort))
         q = q.OrderBy(sort); //And here

      return q.Skip(skip).Take(take).ToList();
   }
}

public int GetTotalCount(string filter)
{
    using(var db = new DataContext())
    {
       var q = GetDataInternal(db);
       if(!String.IsNullOrEmpty(filter))
         q = q.Where(filter); //Using Dynamic linq

       return q.Count(); //Without ordering and paging.
    }
}

private static IQuerable<MyObj> GetDataInternal(DataContext db)
{
   return 
        from x in db.JournalEventsView 
        where ...
        select new ...;
}

使用Dynamic linq庫完成過濾和排序

我知道它不是你想要的,但包含DataTimeStamp的[DataOwnerID]上的索引可以使你的查詢更便宜。

暫無
暫無

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

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