Entity Framework 6:慢 "WHERE NOT IN" 语句
Entity Framework 6: Slow "WHERE NOT IN" statement
我正在使用 Entity Framework 6.0 和 SQL Server localdb,我尝试使用 Enumerable.Contains 功能,但是当我们有一个大列表时我发现它很慢。
我研究并发现针对这种情况建议使用 sql 语句,如:Why is .Contains slow? Most efficient way to get multiple entities by primary key?.
中所述
现在我想知道为什么下面的语句这么慢,需要 180 多秒才能 运行 "ToList ()"?
using (var context = new MyDbContext())
{
Random rand = new Random();
var ids = new List<int>();
for (int i = 0; i < 20000; i++)
ids.Add(rand.Next(550000));
Stopwatch watch = new Stopwatch();
watch.Start();
var values = new StringBuilder();
values.AppendFormat("{0}", ids[0]);
for (int i = 1; i < ids.Count; i++)
values.AppendFormat(", {0}", ids[i]);
var sql = string.Format(
"SELECT * FROM [MyDb].[dbo].[MyEntities] WHERE NOT [ID] IN ({0})",
values);
var result = context.Set<MyEntity>().SqlQuery(sql).ToList();
watch.Stop();
var msec = watch.ElapsedMilliseconds;
}
请注意,该语句类似于上面的 link test3,只是我们包含了术语 "NOT"。
对不起我的英语不好^^。
当使用 EF 运行 你的查询时,执行时间结合了几个因素:
- 构建和翻译查询的时间
- 运行 查询和接收结果的时间
- 将查询结果具体化为实体的时间
通过直接使用 SQL 和 SqlQuery
方法,您可以省去构建查询并将其转换为 SQL 的时间。
在您的情况下,与查询执行和结果具体化相比,这段时间相对较小(如果要具体化的结果太多,这可能会花费相当多的时间)。
您应该检查一下 运行 直接查询需要多少时间以及它有多少结果 return(如果结果太多,比如数千个结果,那么您应该尝试最小化查询结果或使用分页以相对较小的块获取数据)。
像您这样向数据库发送一个非常大的筛选条件可能会非常慢。根据 table 的大小,检索所有值并在内存中进行过滤而不是使用 HashSet
会更快。试试这个:
Random rand = new Random();
var set= new HashSet<int>();
for (int i = 0; i < 20000; i++)
set.Add(rand.Next(550000));
Stopwatch watch = new Stopwatch();
watch.Start();
var sql = "SELECT * FROM [MyDb].[dbo].[MyEntities]";
var result = context.Set<MyEntity>()
.SqlQuery(sql)
.AsEnumerable()
.Where(x => !set.Contains(x.ID))
.ToList();
watch.Stop();
我正在使用 Entity Framework 6.0 和 SQL Server localdb,我尝试使用 Enumerable.Contains 功能,但是当我们有一个大列表时我发现它很慢。
我研究并发现针对这种情况建议使用 sql 语句,如:Why is .Contains slow? Most efficient way to get multiple entities by primary key?.
中所述现在我想知道为什么下面的语句这么慢,需要 180 多秒才能 运行 "ToList ()"?
using (var context = new MyDbContext())
{
Random rand = new Random();
var ids = new List<int>();
for (int i = 0; i < 20000; i++)
ids.Add(rand.Next(550000));
Stopwatch watch = new Stopwatch();
watch.Start();
var values = new StringBuilder();
values.AppendFormat("{0}", ids[0]);
for (int i = 1; i < ids.Count; i++)
values.AppendFormat(", {0}", ids[i]);
var sql = string.Format(
"SELECT * FROM [MyDb].[dbo].[MyEntities] WHERE NOT [ID] IN ({0})",
values);
var result = context.Set<MyEntity>().SqlQuery(sql).ToList();
watch.Stop();
var msec = watch.ElapsedMilliseconds;
}
请注意,该语句类似于上面的 link test3,只是我们包含了术语 "NOT"。
对不起我的英语不好^^。
当使用 EF 运行 你的查询时,执行时间结合了几个因素:
- 构建和翻译查询的时间
- 运行 查询和接收结果的时间
- 将查询结果具体化为实体的时间
通过直接使用 SQL 和 SqlQuery
方法,您可以省去构建查询并将其转换为 SQL 的时间。
在您的情况下,与查询执行和结果具体化相比,这段时间相对较小(如果要具体化的结果太多,这可能会花费相当多的时间)。
您应该检查一下 运行 直接查询需要多少时间以及它有多少结果 return(如果结果太多,比如数千个结果,那么您应该尝试最小化查询结果或使用分页以相对较小的块获取数据)。
像您这样向数据库发送一个非常大的筛选条件可能会非常慢。根据 table 的大小,检索所有值并在内存中进行过滤而不是使用 HashSet
会更快。试试这个:
Random rand = new Random();
var set= new HashSet<int>();
for (int i = 0; i < 20000; i++)
set.Add(rand.Next(550000));
Stopwatch watch = new Stopwatch();
watch.Start();
var sql = "SELECT * FROM [MyDb].[dbo].[MyEntities]";
var result = context.Set<MyEntity>()
.SqlQuery(sql)
.AsEnumerable()
.Where(x => !set.Contains(x.ID))
.ToList();
watch.Stop();