tug*_*erk 11 c# t-sql linq sql-server entity-framework
我不确定什么时候,但我读了一篇关于这个的文章,表明在使用Entity Framework时我的用法Skip(1).Any()比Count()同情更好(我可能记错了).在看到生成的T-SQL代码后,我不确定这一点.
这是第一个选择:
int userConnectionCount = _dbContext.HubConnections.Count(conn => conn.UserId == user.Id);
bool isAtSingleConnection = (userConnectionCount == 1);
Run Code Online (Sandbox Code Playgroud)
这会生成以下合理的T-SQL代码:
SELECT
[GroupBy1].[A1] AS [C1]
FROM ( SELECT
COUNT(1) AS [A1]
FROM [dbo].[HubConnections] AS [Extent1]
WHERE [Extent1].[UserId] = @p__linq__0
) AS [GroupBy1]
Run Code Online (Sandbox Code Playgroud)
这是另一个选项,就我记得的建议查询:
bool isAtSingleConnection = !_dbContext
.HubConnections.OrderBy(conn => conn.Id)
.Skip(1).Any(conn => conn.UserId == user.Id);
Run Code Online (Sandbox Code Playgroud)
以下是上述LINQ查询生成的T-SQL:
SELECT
CASE WHEN ( EXISTS (SELECT
1 AS [C1]
FROM ( SELECT [Extent1].[Id] AS [Id], [Extent1].[UserId] AS [UserId]
FROM ( SELECT [Extent1].[Id] AS [Id], [Extent1].[UserId] AS [UserId], row_number() OVER (ORDER BY [Extent1].[Id] ASC) AS [row_number]
FROM [dbo].[HubConnections] AS [Extent1]
) AS [Extent1]
WHERE [Extent1].[row_number] > 1
) AS [Skip1]
WHERE [Skip1].[UserId] = @p__linq__0
)) THEN cast(1 as bit) WHEN ( NOT EXISTS (SELECT
1 AS [C1]
FROM ( SELECT [Extent2].[Id] AS [Id], [Extent2].[UserId] AS [UserId]
FROM ( SELECT [Extent2].[Id] AS [Id], [Extent2].[UserId] AS [UserId], row_number() OVER (ORDER BY [Extent2].[Id] ASC) AS [row_number]
FROM [dbo].[HubConnections] AS [Extent2]
) AS [Extent2]
WHERE [Extent2].[row_number] > 1
) AS [Skip2]
WHERE [Skip2].[UserId] = @p__linq__0
)) THEN cast(0 as bit) END AS [C1]
FROM ( SELECT 1 AS X ) AS [SingleRowTable1];
Run Code Online (Sandbox Code Playgroud)
哪一个是正确的方法?这两者之间有很大的性能差异吗?
Rag*_*nti 10
查询性能取决于很多因素,例如存在的索引,实际数据,有关数据的统计信息的陈旧程度等等.SQL查询计划优化器会查看这些不同的度量标准,以提出有效的查询计划.所以,任何直截了当的答案都说查询1总是比查询2好,否则相反就是错误的.
也就是说,我在下面的回答试图解释文章的立场,以及如何Skip(1).Any()比做一个更好(略微)Count() > 1.第二个查询虽然规模较大且几乎不可读,但看起来它可以以有效的方式解释.同样,这取决于前面提到的事情.我们的想法是,数据库必须查看的行数才能得出结果Count().在计数情况下,假设所需的索引存在(Id上的聚簇索引使第二种情况下的OrderBy有效),db必须经过计数行数.在第二种情况下,它必须经过最多两行才能得出答案.
让我们的分析更加科学,看看我的上述理论是否有任何理由.为此,我正在创建一个客户虚拟数据库.Customer类型如下所示,
public class Customer
{
public int ID { get; set; }
public string Name { get; set; }
public int Age { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
我使用这个代码,用大约100K随机行(我真的必须证明这一点)为数据库播种,
for (int j = 0; j < 100; j++)
{
using (CustomersContext db = new CustomersContext())
{
Random r = new Random();
for (int i = 0; i < 1000; i++)
{
Customer c = new Customer
{
Name = Guid.NewGuid().ToString(),
Age = r.Next(0, 100)
};
db.Customers.Add(c);
}
db.SaveChanges();
}
}
Run Code Online (Sandbox Code Playgroud)
这里有示例代码.
现在,我要使用的查询如下,
db.Customers.Where(c => c.Age == 26).Count() > 1; // scenario 1
db.Customers.Where(c => c.Age == 26).OrderBy(c => c.ID).Skip(1).Any() // scenario 2
Run Code Online (Sandbox Code Playgroud)
我已经启动了SQL分析器来捕获查询计划.捕获的计划如下所示,
查看上图中方案1的估算成本和实际行数.

在下面的图像中查看方案2的估计成本和实际行数.

根据初始猜测,与Count情况相比,Skip和任何情况下的估计成本和行数都较少.
抛开所有这些分析,正如许多其他人之前评论过的那样,这些并不是您应该在代码中尝试进行的性能优化.像这样的事情会损害可读性,而且很少(我会说不存在)性能上的好处.我只是为了好玩而进行了这个分析,并且决不会将其作为选择方案2的基础.我会测量并查看是否Count()真的在改变要使用的代码Skip().Any().
我读了一篇关于这个的文章,表明使用
Skip(1).Any()效果比Count().
在LINQ to objects查询中,该语句非常正确.在LINQ to objects查询中,Skip(1).Any()只需要尝试获取序列的前两项,并且它可以忽略它之后的所有项.如果序列包括相当昂贵的操作(和正确执行推迟),甚至更重要的是,如果序列是无限的,这可能是一个大问题.对于大多数查询来说,它会有点重要,但往往不是很多.
对于基于查询提供程序的LINQ查询,它不太可能有显着差异.特别是EF,正如您所见,生成的查询没有明显不同.当然,是否有可能产生差异.查询提供程序可以处理一个案例比另一个案例更好,特定查询可以通过一个或另一个使用的特定重构更好地进行优化.
如果有人建议在这两者之间的EF查询中存在重大差异,那么他们错误地应用了旨在应用于LINQ to对象查询的指南.
| 归档时间: |
|
| 查看次数: |
1517 次 |
| 最近记录: |