【发布时间】:2016-07-27 09:25:33
【问题描述】:
我有一个使用 SqlDataReader 读取数据并 yield 返回 IEnumerable 的方法,例如:
IEnumerable<string> LoadCustomers()
{
using(SqlDataReader rdr = cmd.ExecuteReader())
{
while (rdr.Read())
{
yield return rdr.GetString(0);
}
}
}
现在假设我只需要最新的 10 位客户。我可以的
LoadCustomers.Take(10)
或将 10 作为参数传递给 sql 并生成我的 sql
SELECT TOP 10 FROM Customers ORDER BY CreationDate DESC
根据this post,即使数据读取器只读取几行(只要连接打开),整个结果集也会从 sql server 传输到客户端 - 我应该避免使用Take(10) 方法吗?无论如何都会向客户端传输额外的数据,或者避免它将是一个过早的优化(因为在读取 10 行后,yield 返回代码会关闭连接,然后数据传输无论如何都会停止)?
【问题讨论】:
-
ORDER BY CreationDate DESC? -
这不会是过早的优化。仅从数据库中获取您实际需要的内容。当您只需要 10 个客户时,选择 10,000 个客户是没有意义的。
-
您误解了该帖子的内容。如果您停止读取,则整个结果集不会传输到客户端,尽管某些行可能已经被缓冲。
SqlDataReader不会在网络之外“预读”。在大多数情况下,您仍希望将TOP(10)发送到数据库服务器的原因,以及为什么这不是过早的优化,是因为如果优化器知道您只需要 10 行而不是读取整个行,它可以生成更有效的计划表(如果没有别的,查询会提前分配更少的内存)。 -
另一种思考方式:过早的优化是当您开始更改正确表达您想要的代码以尝试使其更快时,甚至在确定它是必要的之前。使用
SELECT而不使用TOP,即使您只需要有限数量的行不是从数据库的角度正确表达您想要的内容。这在 SQL 中是一件大事,因为您的声明是服务器用来生成查询计划的唯一东西。极端情况下,这意味着您的所有查询都变为SELECT * FROM [Table]。 -
理论上可以在客户端缓存整个结果集,是的。但这需要相当大的网络缓冲区——您的平均数据包不能容纳超过平均表的几行。超过 10 个,可能,但不是 10,000 个。没有无限的预读/缓冲。
SqlDataReader不是DataTable- 在您请求之前不会实际读取任何行,只是第一行也会拉下一个 X。确实,这也导致了效率低下,因为服务器仍在检索并发送这些行。
标签: c# sql sqldatareader yield-return premature-optimization