我们可以使用 Skip()、Take() 和 OrderBy() 控制 LINQ 表达式顺序吗
我正在使用 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
I'm using LINQ to Entities to display paged results. But I'm having issues with the combination of Skip()
, Take()
and OrderBy()
calls.
Everything works fine, except that OrderBy()
is assigned too late. It's executed after result set has been cut down by Skip()
and Take()
.
So each page of results has items in order. But ordering is done on a page handful of data instead of ordering of the whole set and then limiting those records with Skip()
and Take()
.
How do I set precedence with these statements?
My example (simplified)
var query = ctx.EntitySet.Where(/* filter */).OrderByDescending(e => e.ChangedDate);
int total = query.Count();
var result = query.Skip(n).Take(x).ToList();
One possible (but a bad) solution
One possible solution would be to apply clustered index to order by column, but this column changes frequently, which would slow database performance on inserts and updates. And I really don't want to do that.
EDIT
I ran ToTraceString()
on my query where we can actually see when order by is applied to the result set. Unfortunately at the end. :(
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
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
我的解决方案
我已经成功解决了这个问题。不要误会我的意思。到目前为止我还没有解决优先级问题,但我已经减轻了它。
我做了什么?
这是我在从 Devart 得到答案之前一直使用的代码。如果他们无法解决这个问题,我最终将不得不使用此代码。
首先,我得到了我的实体的有序 ID。即使对于较大的数据集,仅获取 ID 也具有良好的性能。 MySql 查询非常简单并且性能非常好。在第二部分中,我对这些 ID 进行分区并使用它们来获取实际的实体实例。
考虑到这一点,这应该比我一开始的方式执行得更好(如我的问题中所述),因为由于简化的查询,获得总计数要快得多。第二部分实际上非常相似,只是我的实体是通过 ID 返回的,而不是使用
Skip
和Take
进行分区...希望有人会发现这个解决方案有帮助。
My workaround solution
I've managed to workaround this problem. Don't get me wrong here. I haven't solved precedence issue as of yet, but I've mitigated it.
What I did?
This is the code I've used until I get an answer from Devart. If they won't be able to overcome this issue I'll have to use this code in the end.
First of all I'm getting ordered IDs of my entities. Getting only IDs is well performant even with larger set of data. MySql query is quite simple and performs really well. In the second part I partition these IDs and use them to get actual entity instances.
Thinking of it, this should perform even better than the way I was doing it at the beginning (as described in my question), because getting total count is much much quicker due to simplified query. The second part is practically very very similar, except that my entities are returned rather by their IDs instead of partitioned using
Skip
andTake
...Hopefully someone may find this solution helpful.
我没有直接使用 Linq to Entities,但它应该有一种方法可以在需要时将特定的存储过程挂接到某些位置。 (Linq to SQL 就是这样做的。)如果是这样,您可以将此查询转换为存储过程,准确地执行所需的操作,并且高效地执行。
I haven't worked directly with Linq to Entities, but it should have a way to hook specific stored procedures into certain locations when needed. (Linq to SQL did.) If so, you could turn this query into a stored procedure, doing exacly what is required, and doing it efficiently.
假设您的评论认为将值保留在列表中是不可接受的:
没有办法按照您的意图完全最小化迭代(正如我也尝试过的那样,生活在希望中)。将迭代次数减少 1 次就好了。是否可以只获取一次计数并缓存/会话它?然后你可以:
希望你能以某种方式缓存计数,或者避免每次都需要它。即使你做不到,这也是你能做的最好的事情了。
Assuming from you comment the persisting the values in a List is not acceptable:
There's no way to completely minimize the iterations, as you intended (and as I would have tried too, living in hope). Cutting the iterations down by one would be nice. Is it possible to just get the Count once and cache/session it? Then you could:
Hopefully you can cache the Count somehow, or avoid needing it every time. Even if you can't, this is the best you can do.
您能否创建一个示例来说明问题并将其发送给我们(支持 * devart * com,主题“EF:Skip、Take、OrderBy”)?
希望我们能够帮助您。
您还可以使用我们的论坛或联系表。
Could you please create a sample illusrating the problem and send it to us (support * devart * com, subject "EF: Skip, Take, OrderBy")?
Hope we will be able to help you.
You can also contact us using our forums or contact form.
您完全确定订单已关闭吗? SQL 是什么样的?
您可以按如下方式重新排序代码并发布输出吗?
编辑:
是的,我看到了。哇,这真是一个难题。甚至可能是一个彻底的错误。我注意到你没有使用 SQL Server...你使用什么数据库?看起来可能是MySQl。
Are you absolutely certain the ordering is off? What does the SQL look like?
Can you reorder your code as follows and post the output?
Edit:
Yeah, I see it. Wow, that's a stumper. Might even be an outright bug. I note you're not using SQL Server... what DB are you using? Looks like it might be MySQl.
一种方法:
在跳过之前将其转换为列表。请注意,这不是太有效...
One way:
Convert it to a List before skipping. It's not too efficient, mind you...