[英]Linq to entity order by sql
I'm doing some tests with EF
and Linq to entities
to try and improve my application performance. 我正在使用EF
和Linq to entities
进行一些测试,以尝试提高我的应用程序性能。
I just notice something odd (to me) that I can't explain and also can't really tell if incurs considerable overhead. 我只是注意到一些奇怪的(对我来说),我无法解释,也无法确定是否会产生相当大的开销。
Here's my linq: 这是我的linq:
var result = from n in query
orderby n.PersonId
select new
{
id = n.Id,
appointmentId = n.AppointmentId,
message = n.Message,
wasRead = n.Read,
canDismiss = (n.Appointment.Status != AppointmentStatus.Waiting),
date = n.IssueDateUtc
};
This is the generated sql: 这是生成的sql:
SELECT
[Project1].[Id] AS [Id],
[Project1].[AppointmentId] AS [AppointmentId],
[Project1].[Message] AS [Message],
[Project1].[Read] AS [Read],
[Project1].[C1] AS [C1],
[Project1].[IssueDateUtc] AS [IssueDateUtc]
FROM ( SELECT
[Extent1].[Id] AS [Id],
[Extent1].[Read] AS [Read],
[Extent1].[Message] AS [Message],
[Extent1].[IssueDateUtc] AS [IssueDateUtc],
[Extent1].[AppointmentId] AS [AppointmentId],
[Extent1].[PersonId] AS [PersonId],
CASE WHEN ( NOT ((1 = [Extent2].[Status]) AND ([Extent2].[Status] IS NOT NULL))) THEN cast(1 as bit) WHEN (1 = [Extent2].[Status]) THEN cast(0 as bit) END AS [C1]
FROM [dbo].[Notification] AS [Extent1]
LEFT OUTER JOIN [dbo].[Appointment] AS [Extent2] ON [Extent1].[AppointmentId] = [Extent2].[Id]
WHERE [Extent1].[PersonId] = @p__linq__0
) AS [Project1]
**ORDER BY [Project1].[PersonId] ASC**
I don't understand the need to kind of group the result in another projection ( Project1
) whilst this seems to work just fine: 我不明白需要将结果分组到另一个投影( Project1
),而这似乎工作得很好:
SELECT
[Extent1].[Id] AS [Id],
[Extent1].[Read] AS [Read],
[Extent1].[Message] AS [Message],
[Extent1].[IssueDateUtc] AS [IssueDateUtc],
[Extent1].[AppointmentId] AS [AppointmentId],
[Extent1].[PersonId] AS [PersonId],
CASE WHEN ( NOT ((1 = [Extent2].[Status]) AND ([Extent2].[Status] IS NOT NULL))) THEN cast(1 as bit) WHEN (1 = [Extent2].[Status]) THEN cast(0 as bit) END AS [C1]
FROM [dbo].[Notification] AS [Extent1]
LEFT OUTER JOIN [dbo].[Appointment] AS [Extent2] ON [Extent1].[AppointmentId] = [Extent2].[Id]
WHERE [Extent1].[PersonId] = @p__linq__0
**ORDER BY [Extent1].[PersonId] ASC**
I found a considerable amount of questionable sql generated by both ef and linq and I'm starting to wonder if I wasn't better off just writing raw sqls. 我发现ef和linq都生成了大量有问题的sql,我开始怀疑我是不是更好的只是编写原始sqls。
Question is: is the generated sql extra bits of code something to be worried about? 问题是:是生成的sql额外的代码需要担心什么? Why is that projection necessary? 为什么这个投影是必要的?
Edit to add a new linq 编辑以添加新的linq
As mentioned in the comments, maybe the verbose was caused by subsequent queries being run. 正如评论中所提到的,可能是后续查询运行导致的冗长。 I rewrote the linq to use only one query object, and the result is still the same: 我重写了linq只使用一个查询对象,结果仍然是相同的:
dbSet.Where(n => n.PersonId == id).Select(n => new
{
Id = n.Id,
AppointmentId = n.AppointmentId,
Message = n.Message,
Read = n.Read,
CanBeDismissed = (n.Appointment.Status != AppointmentStatus.Waiting),
IssueDate = n.IssueDateUtc
}).OrderBy(n => n.Id).ToList();
Execution plan (same for both sqls
) 执行计划(两个sqls
相同)
Edit 2 编辑2
Just got this query from a simple count. 刚从简单的计数中得到这个查询。
dbSet.Count(x => x.Id == 1 && x.Read == false);
SELECT
[GroupBy1].[A1] AS [C1]
FROM ( SELECT
COUNT(1) AS [A1]
FROM [dbo].[Notification] AS [Extent1]
WHERE ([Extent1].[PersonId] = 19) AND (0 = [Extent1].[Read])
) AS [GroupBy1]
Expected: 预期:
SELECT
COUNT(1) AS [A1]
FROM [dbo].[Notification] AS [Extent1]
WHERE ([Extent1].[PersonId] = 19) AND (0 = [Extent1].[Read])
I don't get where all this wrappers are coming from and why. 我没有得到所有这些包装材料的来源和原因。
I put together a small sample project on my machine. 我在我的机器上放了一个小样本项目。 What acutally causes the projection in your first sample is your conditional calculation of the CanBeDismissed field, resulting in a CASE WHEN
in SQL. 在您的第一个样本中实际导致投影的是您对CanBeDismissed字段的条件计算,从而导致SQL中的CASE WHEN
。 If you leave this out, Entity Framework won't do an additional projection. 如果您将其删除,Entity Framework将不会进行额外的预测。
So with an conditional check: 所以通过条件检查:
db.Notifications
.Where(n => n.AppointmentId == 1)
.OrderBy(n => n.Id)
.Select(n => new
{
Id = n.Id,
Message = n.Message,
HasMessage = n.Message != null
}).ToList();
The SQL produced is: 产生的SQL是:
SELECT
[Project1].[Id] AS [Id],
[Project1].[Message] AS [Message],
[Project1].[C1] AS [C1]
FROM ( SELECT
[Extent1].[Id] AS [Id],
[Extent1].[Message] AS [Message],
CASE WHEN ([Extent1].[Message] IS NOT NULL) THEN cast(1 as bit) ELSE cast(0 as bit) END AS [C1]
FROM [dbo].[Notifications] AS [Extent1]
WHERE 1 = [Extent1].[AppointmentId]
) AS [Project1]
ORDER BY [Project1].[Id] ASC
Let me add the resulting execution plan for later reference: 让我添加生成的执行计划供以后参考:
If you leave it out: 如果你把它留下:
db.Notifications
.Where(n => n.AppointmentId == 1)
.OrderBy(n => n.Id)
.Select(n => new
{
Id = n.Id,
Message = n.Message
}).ToList();
No projection is done by EF: EF没有做出任何预测:
SELECT
[Extent1].[Id] AS [Id],
[Extent1].[Message] AS [Message]
FROM [dbo].[Notifications] AS [Extent1]
WHERE 1 = [Extent1].[AppointmentId]
ORDER BY [Extent1].[Id] ASC
So this is the why. 所以这就是原因。 Same applies to your count
sample: if there is any grouping happening, EF will add an additional projection which makes the query more verbose. 同样适用于您的count
样本:如果发生任何分组,EF将添加一个额外的投影,使查询更加详细。 But the important part is, as discussed in the comments to your question, it won't hurt performance , there is no need to worry about this additional projection. 但重要的是,正如你的问题评论中所讨论的那样, 它不会影响性能 ,也不必担心这个额外的预测。
Let me proof this by now adding the execution plan of the following query, where I have just removed the pojection from the first query and moved the orderby to the inner query: 现在让我通过添加以下查询的执行计划来证明这一点,我刚从第一个查询中删除了pojection并将orderby移动到内部查询:
SELECT
[Extent1].[Id] AS [Id],
[Extent1].[Message] AS [Message],
CASE WHEN ([Extent1].[Message] IS NOT NULL) THEN cast(1 as bit) ELSE cast(0 as bit) END AS [C1]
FROM [dbo].[Notifications] AS [Extent1]
WHERE 1 = [Extent1].[AppointmentId]
ORDER BY [Extent1].[Id] ASC
It's exactly the same - there is no additional task added and the cost distribution remains the same. 它完全相同 - 没有添加任何额外任务,成本分配保持不变。 The SQL Query Optimizer will optimize such projecitons away just nicely. SQL查询优化器将很好地优化这些项目。
So again, don't worry about projections - they won't hurt you, while I agree they seem and are sometimes unnecessarily verbose. 所以再次,不要担心预测 - 他们不会伤害你,而我同意他们似乎有时不必要的冗长。 But here are two things that might help you: 但是这里有两件事可以帮到你:
Performance issues: 性能问题:
First, if you are experiencing performance issues with your query, look at why there happens a Clustered Index Scan
in the execution plan you posted. 首先,如果您遇到查询性能问题,请查看在您发布的执行计划中发生Clustered Index Scan
原因。 This is not always a sign for some indexing issues, but it is very often. 这并不总是一些索引问题的标志,但它经常发生。 Your problems might root here. 你的问题可能源于此。
Get rid of unneccesary projections: 摆脱不必要的预测:
If you still want to get rid of those projections in all (or at least more) cases, there is Entity Framework Core 1.0 - it actually produces even nicer SQL than EF 6. It might be worth considering migrating to it, yet be aware that it does not come with all features that EF 6 does, so it might not be an option if you are using features that EF Core 1.0 does not offer. 如果您仍想在所有(或至少更多)情况下摆脱这些预测,那么实体框架核心1.0 - 它实际上产生比EF 6更好的SQL。可能值得考虑迁移到它,但要注意它没有EF 6所具有的所有功能,因此如果您使用的是EF Core 1.0不提供的功能,则可能无法选择。 But it will work with the full .NET Framework 4.x! 但它将与完整的.NET Framework 4.x一起使用!
Here's an example what EF Core 1.0 produces when I execute the first LINQ statement of my answer: 下面是我执行我的答案的第一个LINQ语句时EF Core 1.0产生的示例:
SELECT [n].[Id], [n].[Message], CASE
WHEN [n].[Message] IS NULL
THEN CAST(0 AS BIT) ELSE CAST(1 AS BIT)
END
FROM [Notifications] AS [n]
WHERE ([n].[Id] = 1) AND ([n].[Id] = 1)
ORDER BY [n].[Id]
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.