我有一个使用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传输到客户端,即使datareader只读取几行(只要连接打开) - 我应该避免使用Take(10)
方法是因为无论如何都要向客户端发送额外的数据,或者为了避免它而过早优化(因为收益返回码会在读取10行之后关闭连接然后数据传输会停止)?
答案 0 :(得分:2)
由于优化是否“过早”是主观的,我选择将此问题解释为“使用DataReader
并在10行具有与使用TOP(10)
相同的性能特征后停止读取在查询?“
答案是否定的。将TOP(10)
传递给服务器允许优化器调整读取,内存授予,I / O缓冲区,锁粒度和并行性,并且知道查询将返回(在这种情况下,也读取)最多10行。抛弃TOP
意味着它必须为客户端将要读取所有行的情况做好准备 - 无论您是否真的提前停止。
无论您是否阅读过,服务器都不会发送行。使用SqlDataReader
拉取行在概念上是逐行操作:当您发出Reader.MoveNext
时,从服务器获取下一行并且只获取该行。但为了提高性能,在请求行之前会对行进行缓冲(在服务器端和行网络缓冲区中都是如此)。因此,在第一次.MoveNext
调用之后,有可能最终在缓冲区中检索到100行,即使您只阅读其中的10行。
关于开销,这不是我主要关注的问题,因为这些缓冲区最终具有固定大小:服务器不会去缓冲结果集的所有行,无论有多少行(这将是非常低效的)一般)。如果你只读了10行,那么如果你的查询在运行完成时最终会返回1000或1,000,000行,那么在缓冲方面并不重要,但主要是在查询计划方面。然而,它确实增加了开销。
答案 1 :(得分:1)
你也可以使用分页Skip(0)和Take(10)更灵活。
SQL SERVER 2012
SELECT name,
CreationDate
FROM customer
ORDER BY
CreationDate
OFFSET @skip ROWS
FETCH NEXT @take ROWS ONLY;
SQL 2005至2008
SET @take = (@skip + @take)
;WITH customer_page_cte AS
(SELECT
name,
CreationDate,
ROW_NUMBER() OVER (ORDER BY CreationDate desc) AS RowNumber
FROM customer
)
SELECT name,
CreationDate
FROM customer_page_cte
WHERE RowNumber > @skip AND RowNumber <= @take
使用sql 2012的C# - 使用存储过程进行命令:)
var command = @"SELECT name,
CreationDate
FROM customer
ORDER BY
CreationDate
OFFSET @skip ROWS
FETCH NEXT @take ROWS ONLY;";
using (var conn = new SqlConnection("Data Source=.;Initial Catalog=Stackoverflow;Integrated Security=True"))
{
conn.Open();
using (var cmd = new SqlCommand(command, conn))
{
cmd.Parameters.AddWithValue("skip", 0);
cmd.Parameters.AddWithValue("take", 10);
var reader = cmd.ExecuteReader();
while (reader.Read())
{
Console.WriteLine(reader.GetString(0));
}
}
}