我正在使用LINQ to Entities来显示分页结果。但我对Skip()
,Take()
和OrderBy()
电话的组合遇到了问题。
一切正常,但OrderBy()
分配得太迟了。在结果集被Skip()
和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
答案 0 :(得分: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而不是使用Skip
和Take
进行分区...
希望有人可能会觉得这个解决方案很有帮助。
答案 1 :(得分:2)
我没有直接使用Linq to Entities,但它应该有办法在需要时将特定的存储过程挂钩到某些位置。 (Linq to SQL确实如此。)如果是这样,你可以将这个查询变成一个存储过程,尽可能地执行所需的操作,并有效地进行。
答案 2 :(得分:1)
您是否绝对确定订购已关闭? 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。我绝对肯定。您可以检查我的“编辑”以查看我的查询的跟踪结果,其中跳过跟踪结果在这种情况下是必不可少的。伯爵并不重要。
答案 3 :(得分:0)
假设您发表评论,持久化列表中的值是不可接受的:
没有办法像你想象的那样完全减少迭代次数(而且我也会尝试过,生活在希望中)。将迭代次数减少一次将是很好的。有可能只获得Count一次并缓存/会话吗?然后你可以:
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,或者每次都避免需要它。即使你做不到,这是你能做的最好的事情。
答案 4 :(得分:0)
请您创建一个示例来说明问题并将其发送给我们(支持* devart * com,主题为“EF:Skip,Take,OrderBy”)?
希望我们能够帮到你。
您也可以使用我们的forums或contact form与我们联系。
答案 5 :(得分:-1)
一种方式:
var query = ctx.EntitySet.Where(/* filter */).OrderBy(/* expression */).ToList();
int total = query.Count;
var result = query.Skip(n).Take(x).ToList();
在跳过之前将其转换为List。这不是太有效,请注意......