简体   繁体   English

Linq通过sql实体命令

[英]Linq to entity order by sql

I'm doing some tests with EF and Linq to entities to try and improve my application performance. 我正在使用EFLinq 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM