添加.ToArray或.ToList是否总能使数据库查询更快?

时间:2014-01-28 14:46:20

标签: c# linq performance toarray

我注意到在向查询添加.ToArray()或.ToList()时,数据库查询运行得更快。这是因为数据集被加载到内存中,所有后续查询都是在内存中完成而不是进行进一步昂贵的数据库调用吗?

对于数据库查询的内存存储应该有什么限制,因为我担心耗尽太多的内部内存会降低整个项目的速度,因为我确信占用太多的内存存储可能会减慢速度急剧下降。

编辑:这是针对Linq-SQL查询的。我正在使用SQL Server 2008。

示例1:执行大型数据库查询并过滤内存

我有一个包含5 000行的数据库表。我查询整个表(例如SELECT * From Clients)。我接下来的几个查询基于上一个查询中的字段:a)获取所有男性客户; b)获取所有女性客户c)获取FirstName以A开头的所有客户。

示例2:执行更频繁的数据库调用

使用5000行的同一个Client表,我需要执行3个查询a)获取所有男性客户; b)获取所有女性客户c)获取FirstName以A开头的所有客户端。我通过数据库调用而不是内存中进行所有查询。

哪种方法更有效?

1 个答案:

答案 0 :(得分:26)

注意:我在查询中使用了实体框架“上下文”。答案适用于Entity Framework或LINQ-to-SQL。

ToArray()和ToList()永远不会加速查询。但是,如果您不小心多次运行查询,它们可能会出现这种情况。这是使用LINQ时常见的错误。例如:

// Construct a query to find all the cute puppies
var data = myContext.Puppies.Where(puppy => puppy.Type == PuppyTypes.Cute);

以上行实际上并没有在数据库中查询可爱的小狗。相反,它构造一个查询,在将来的某个时候,查询小狗。这称为延迟执行。测验问题:上面例子中的“数据”是什么类型?

当您枚举变量数据时,实际上会运行查询。此变量的类型为IEnumerable。 IEnumebles与Lists或Arrays不同。 IEnumerable仅仅是可以获得数据的承诺。这并不意味着您实际拥有数据。

// Display the puppies
foreach (Puppy p in data) { Console.WriteLine(p.Name); }

上面的foreach调用将强制执行查询。小狗将进入屏幕,但它们不会被缓存在任何地方的数组或列表中。所以现在,如果你这样做:

// Display their owner's names
foreach (Puppy p in data) { Console.Writeline(p.OwnerName); }

此foreach使查询再次执行 。事实上,如果在两次调用之间在数据库中添加或删除了新的小狗,您甚至可能得到不同的结果!因此,假设我们将ToList()添加到初始调用中。

// Construct a query to find all the cute puppies
var data = myContext.Puppies.Where(puppy => puppy.Type == PuppyTypes.Cute).ToList();

现在,数据类型为List。在引擎盖下,这将创建一个IEnumerable,然后通过它进行foreach,并将结果放入列表中。现在,如果您显示小狗名称和所有者,它将不会两次查询数据库。而是循环遍历列表两次。

如果你搜索延迟执行,你会发现很多有趣的用途,以及我列出的那些警告。通常,您希望确保只运行一次查询。如果那需要ToList()那么好。但是不要不必要地添加ToList()。使用最后一个例子:

var data = myContext.Puppies.Where(puppy => puppy.Type == PuppyTypes.Cute).ToList();
foreach (Puppy p in data) { Console.WriteLine(p.Name + "," + p.OwnerName); }

你在这里有ToList()吗?没有!因为当你真的不需要列表时,这会增加列表的开销。更糟糕的是:

var data = myContext.Puppies.Where(puppy => puppy.Type == PuppyTypes.Cute).ToList();
Console.Writeline(data.Count());

更糟糕的是!它将所有的小狗拉到了记忆中的一个列表中,然后计算它们。但是这个问题甚至根本不需要抓住小狗。这样做:

var data = myContext.Puppies.Where(puppy => puppy.Type == PuppyTypes.Cute);
Console.Writeline(data.Count());

它实际上告诉SQL服务器计算小狗的数量,并且从不浪费带宽或内存实际将小狗从数据库中加载或将其发送到C#或其中任何一个。

我希望这有帮助!