【发布时间】:2012-09-07 21:27:31
【问题描述】:
我正在使用实体框架,我需要检查名称 = "xyz" 的产品是否存在......
我想我可以使用 Any()、Exists() 或 First()。
对于这种情况,哪一个是最佳选择?哪个性能最好?
谢谢,
米格尔
【问题讨论】:
标签: entity-framework ef-code-first exists any
我正在使用实体框架,我需要检查名称 = "xyz" 的产品是否存在......
我想我可以使用 Any()、Exists() 或 First()。
对于这种情况,哪一个是最佳选择?哪个性能最好?
谢谢,
米格尔
【问题讨论】:
标签: entity-framework ef-code-first exists any
好的,我不打算对此发表意见,但 Diego 的回答使事情变得复杂,我认为需要进行一些额外的解释。
在大多数情况下,.Any() 会更快。以下是一些示例。
Workflows.Where(w => w.Activities.Any())
Workflows.Where(w => w.Activities.Any(a => a.Title == "xyz"))
在上面的两个例子中,Entity Framework 产生了一个最优的查询。 .Any() 调用是谓词的一部分,Entity Framework 可以很好地处理这个问题。但是,如果我们将 .Any() 的结果作为结果集的一部分,如下所示:
Workflows.Select(w => w.Activities.Any(a => a.Title == "xyz"))
...突然,Entity Framework 决定创建两个版本的条件,因此查询的工作量是它真正需要的工作量的两倍。但是,以下查询也好不到哪里去:
Workflows.Select(w => w.Activities.Count(a => a.Title == "xyz") > 0)
鉴于上述查询,Entity Framework 仍将创建两个版本的条件,此外它还需要 SQL Server 进行实际计数,这意味着它不会在找到项目后立即短路.
但如果您只是比较这两个查询:
Activities.Any(a => a.Title == "xyz")Activities.Count(a => a.Title == "xyz") > 0...哪个会更快?视情况而定。
第一个查询产生了一个低效的双条件查询,这意味着它需要花费两倍的时间。
第二个查询强制数据库在不短路的情况下检查表中的每个项目,这意味着它可能需要 N 倍的时间,具体取决于在找到一个之前需要评估多少个项目匹配。假设该表有 10,000 个项目:
Title 和键列上的索引,则此查询将花费大约一半时间作为第一个查询。总之,如果你是:
那么,您可以预期 .Any() 花费的时间是 .Count() 的两倍。例如,查询可能需要 100 毫秒而不是 50 毫秒。或者 10 毫秒而不是 5。
在任何其他情况下.Any() 应该至少与.Count() 一样快,并且可能比.Count() 快几个数量级。
无论如何,在您确定这实际上是您的产品性能不佳的根源之前,您应该更加关注易于理解的内容。 .Any() 更清楚、更简洁地说明了您真正想要弄清楚的内容,所以坚持下去。
【讨论】:
Count() > 0,我会使用 FirstOrDefault() != null。
Any() 创建一个带有 2 个 EXISTS 子句的 CASE。见WI 192。
任何在数据库级别转换为“存在”。 First 转换为 Select Top 1 ... 在这些之间, Exists 将执行 First ,因为不需要获取实际对象,只需要一个布尔结果值。
至少您没有询问 .Where(x => x.Count() > 0) ,它需要评估和迭代整个匹配集,然后才能确定您有一条记录。任何都会使请求短路并且可以显着加快。
【讨论】:
.Any()比较简单,而且会优于.Where(x => x.Count > 0),所以应该首选。
有人会认为Any() 提供了更好的结果,因为它转换为EXISTS 查询...但是 EF 严重损坏,生成了这个(已编辑):
SELECT
CASE WHEN ( EXISTS (SELECT
1 AS [C1]
FROM [MyTable] AS [Extent1]
WHERE Condition
)) THEN cast(1 as bit) WHEN ( NOT EXISTS (SELECT
1 AS [C1]
FROM [MyTable] AS [Extent2]
WHERE Condition
)) THEN cast(0 as bit) END AS [C1]
FROM ( SELECT 1 AS X ) AS [SingleRowTable1]
代替:
SELECT
CASE WHEN ( EXISTS (SELECT
1 AS [C1]
FROM [MyTable] AS [Extent1]
WHERE Condition
)) THEN cast(1 as bit)
ELSE cast(0 as bit) END AS [C1]
FROM ( SELECT 1 AS X ) AS [SingleRowTable1]
...基本上将查询成本加倍(对于简单的查询;对于复杂的查询更糟)
我发现使用.Count(condition) > 0 几乎总是更快(成本与正确编写的EXISTS 查询完全相同)
【讨论】:
好的,我决定自己尝试一下。请注意,我将 OracleManagedDataAccess 提供程序与 OracleEntityFramework 一起使用,但我猜它会生成兼容的 SQL。
我发现对于一个简单的谓词,First() 比 Any() 更快。我将展示 EF 中的两个查询和生成的 SQL。请注意,这是一个简化的示例,但问题是询问简单谓词是否存在、是否存在或首先更快。
var any = db.Employees.Any(x => x.LAST_NAME.Equals("Davenski"));
那么这在数据库中解决了什么问题?
SELECT
CASE WHEN ( EXISTS (SELECT
1 AS "C1"
FROM "MYSCHEMA"."EMPLOYEES" "Extent1"
WHERE ('Davenski' = "Extent1"."LAST_NAME")
)) THEN 1 ELSE 0 END AS "C1"
FROM ( SELECT 1 FROM DUAL ) "SingleRowTable1"
它正在创建一个 CASE 语句。我们知道,ANY 只是合成糖。它解析为数据库级别的 EXISTS 查询。如果您在数据库级别也使用 ANY,就会发生这种情况。但这似乎不是这个查询最优化的 SQL。 在上面的示例中,此处不需要 EF 构造 Any(),它只会使查询复杂化。
var first = db.Employees.Where(x => x.LAST_NAME.Equals("Davenski")).Select(x=>x.ID).First();
这在数据库中解析为:
SELECT
"Extent1"."ID" AS "ID"
FROM "MYSCHEMA"."EMPLOYEES" "Extent1"
WHERE ('Davenski' = "Extent1"."LAST_NAME") AND (ROWNUM <= (1) )
现在这看起来是比初始查询更优化的查询。为什么?它没有使用 CASE ... THEN 语句。
我多次运行这些琐碎的示例,在几乎所有情况下(没有双关语),First() 更快。
此外,我运行了一个原始 SQL 查询,认为这样会更快:
var sql = db.Database.SqlQuery<int>("SELECT ID FROM MYSCHEMA.EMPLOYEES WHERE LAST_NAME = 'Davenski' AND ROWNUM <= (1)").First();
性能实际上是最慢的,但类似于 Any EF 构造。
反思:
这是一个真正有意义的例子。我正在测试相关数据的存在性。我不想从相关表中获取所有记录。在这里我想知道是否有带有评论的调查。
var b = db.Survey.Where(x => x.Comments.Any()).ToList();
这是生成的 SQL:
SELECT
"Extent1"."SURVEY_ID" AS "SURVEY_ID",
"Extent1"."SURVEY_DATE" AS "SURVEY_DATE"
FROM "MYSCHEMA"."SURVEY" "Extent1"
WHERE ( EXISTS (SELECT
1 AS "C1"
FROM "MYSCHEMA"."COMMENTS" "Extent2"
WHERE ("Extent1"."SURVEY_ID" = "Extent2"."SURVEY_ID")
))
这是优化的 SQL! 我相信 EF 在生成 SQL 方面做得很好。但是您必须了解 EF 构造如何映射到 DB 构造,否则您可能会创建一些讨厌的查询。
获取相关数据计数的最佳方法可能是使用集合查询计数进行显式加载。这比之前帖子中提供的示例要好得多。在这种情况下,您不会加载相关实体,而只是获取计数。在这里,我只是想找出我对特定调查有多少评论。
var d = db.Survey.Find(1);
var e = db.Entry(d).Collection(f => f.Comments)
.Query()
.Count();
【讨论】:
Any() 和 First() 与 IEnumerable 一起使用,这为您提供了懒惰评估事物的灵活性。但是Exists() 需要列表。
我希望这可以为您解决问题并帮助您决定使用哪一个。
【讨论】: