10

我正在使用 LINQ to Entities 来显示分页结果。但是我遇到了Skip(),Take()OrderBy()调用的组合问题。

一切正常,除了OrderBy()分配得太晚。Skip()它在结果集被and削减后执行Take()

因此,每一页结果都有按顺序排列的项目。但是排序是在少量数据的页面上完成的,而不是对整个数据集进行排序,然后用Skip()和限制这些记录Take()

如何设置这些语句的优先级?

我的例子(简化)

var query = ctx.EntitySet.Where(/* filter */).OrderByDescending(e => e.ChangedDate);
int total = query.Count();
var result = query.Skip(n).Take(x).ToList();

一种可能(但不好)的解决方案

一种可能的解决方案是将聚集索引应用到按列排序,但该列经常更改,这会降低数据库在插入和更新时的性能。我真的不想那样做。

编辑

我运行ToTraceString()了我的查询,我们可以实际看到 order by 何时应用于结果集。最后不幸的是。:(

SELECT 
-- columns
FROM  (SELECT 
    -- columns
    FROM   (SELECT -- columns
        FROM ( SELECT 
            -- columns
            FROM table1 AS Extent1
            WHERE  EXISTS (SELECT 
                -- single constant column
                FROM table2 AS Extent2
                WHERE (Extent1.ID = Extent2.ID) AND (Extent2.userId = :p__linq__4)
            )
        )  AS Project2
        limit 0,10  ) AS Limit1
    LEFT OUTER JOIN  (SELECT 
        -- columns
        FROM table2 AS Extent3 ) AS Project3 ON Limit1.ID = Project3.ID
UNION ALL
    SELECT 
    -- columns
    FROM   (SELECT -- columns
        FROM ( SELECT 
            -- columns
            FROM table1 AS Extent4
            WHERE  EXISTS (SELECT 
                -- single constant column
                FROM table2 AS Extent5
                WHERE (Extent4.ID = Extent5.ID) AND (Extent5.userId = :p__linq__4)
            )
        )  AS Project6
        limit 0,10  ) AS Limit2
    INNER JOIN table3 AS Extent6 ON Limit2.ID = Extent6.ID) AS UnionAll1
ORDER BY UnionAll1.ChangedDate DESC, UnionAll1.ID ASC, UnionAll1.C1 ASC
4

6 回答 6

4

我的解决方法

我设法解决了这个问题。不要在这里误会我的意思。我还没有解决优先级问题,但我已经减轻了它。

我做了什么?

这是我在得到Devart答复之前一直使用的代码。如果他们无法克服这个问题,我最终将不得不使用这段代码。

// get ordered list of IDs
List<int> ids = ctx.MyEntitySet
    .Include(/* Related entity set that is needed in where clause */)
    .Where(/* filter */)
    .OrderByDescending(e => e.ChangedDate)
    .Select(e => e.Id)
    .ToList();

// get total count
int total = ids.Count;

if (total > 0)
{
    // get a single page of results
    List<MyEntity> result = ctx.MyEntitySet
        .Include(/* related entity set (as described above) */)
        .Include(/* additional entity set that's neede in end results */)
        .Where(string.Format("it.Id in {{{0}}}", string.Join(",", ids.ConvertAll(id => id.ToString()).Skip(pageSize * currentPageIndex).Take(pageSize).ToArray())))
        .OrderByDescending(e => e.ChangedOn)
        .ToList();
}

首先,我得到了我的实体的有序 ID。即使使用更大的数据集,仅获取 ID 的性能也很好。MySql 查询非常简单并且执行得非常好。在第二部分中,我对这些 ID 进行分区并使用它们来获取实际的实体实例。

考虑到这一点,这应该比我一开始做的方式更好(如我的问题中所述),因为由于简化了查询,获得总数要快得多。第二部分实际上非常相似,除了我的实体是通过它们的 ID 返回的,而不是使用Skipand Take...

希望有人会发现此解决方案有帮助。

于 2010-03-24T17:06:45.317 回答
2

我没有直接使用 Linq to Entities,但它应该有一种方法可以在需要时将特定的存储过程挂接到某些位置。(Linq to SQL 做到了。)如果是这样,您可以将此查询转换为存储过程,准确地执行所需的操作,并有效地执行此操作。

于 2010-03-22T17:33:21.830 回答
0

假设您评论将值保留在 List 中是不可接受的:

没有办法完全减少迭代,如你所愿(我也会尝试过,生活在希望中)。将迭代减少一个会很好。是否可以只获取一次计数并缓存/会话它?然后你可以:

int total = ctx.EntitySet.Count;  // Hopefully you can not repeat doing this.
var result = ctx.EntitySet.Where(/* filter */).OrderBy(/* expression */).Skip(n).Take(x).ToList();

希望您可以以某种方式缓存 Count,或者避免每次都需要它。即使你做不到,这也是你能做的最好的。

于 2010-03-22T16:46:28.097 回答
0

您绝对确定订单已关闭吗?SQL 是什么样的?

您可以按如下方式重新排序代码并发布输出吗?

// Redefine your queries. 
var query = ctx.EntitySet.Where(/* filter */).OrderBy(e => e.ChangedDate); 
var skipped = query.Skip(n).Take(x);

// let's look at the SQL, shall we?
var querySQL = query.ToTraceString();
var skippedSQL = skipped.ToTraceString();

// actual execution of the queries...
int total = query.Count(); 
var result = skipped.ToList(); 

编辑:

我绝对确定。您可以检查我的“编辑”以查看我的查询的跟踪结果以及在这种情况下必须跳过的跟踪结果。计数并不重要。

是的,我看到了。哇,真是个笨蛋。甚至可能是一个彻头彻尾的错误。我注意到您没有使用 SQL Server ...您使用的是什么数据库?看起来它可能是 MySQl。

于 2010-03-22T17:08:46.147 回答
0

您能否创建一个说明问题的示例并将其发送给我们(support * devart * com,主题“EF:Skip, Take, OrderBy”)?
希望我们能够帮助您。
您也可以使用我们的论坛联系表格与我们联系。

于 2010-03-24T14:42:42.190 回答
-1

单程:

var query = ctx.EntitySet.Where(/* filter */).OrderBy(/* expression */).ToList();
int total = query.Count;
var result = query.Skip(n).Take(x).ToList();

在跳过之前将其转换为列表。这不是太有效,请注意...

于 2010-03-22T16:28:57.740 回答