Take(10) vs TOP 10 使用 SqlDataReader?
Take(10) vs TOP 10 With SqlDataReader?
我有一个方法可以使用 SqlDataReader 读取数据并生成 returns 一个 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 服务器传输到客户端,即使数据读取器只读取几行(只要连接打开) - 我应该避免Take(10)
方法,因为无论如何都会将额外的数据传输到客户端,或者避免它是过早的优化(因为 yield return 代码会在读取 10 行然后读取数据后关闭连接无论如何传输都会停止)?
您还可以使用分页 Skip(0) 和 Take(10) 更加灵活。
SQL 服务器 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
C# with sql 2012 - 使用命令的存储过程:)
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=Whosebug;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));
}
}
}
由于是否优化"premature"是主观的,所以我选择将此问题解释为"does using a DataReader
and stopping the read after 10 rows have the same performance characteristics as using TOP(10)
in the query?"
答案是否定的。将 TOP(10)
传递给服务器允许优化器调整读取、内存授予、I/O 缓冲区、锁定粒度和并行性,同时知道查询将 return(在这种情况下,还读取) 最多 10 行。省略 TOP
意味着它必须为客户端将要读取所有行的情况做好准备——无论您是否真的提前停止。
无论您是否阅读,服务器都会发送行,这是不正确的。使用 SqlDataReader
拉取行在概念上是逐行操作:当您发出 Reader.MoveNext
时,您从服务器获取下一行并且仅获取该行。但是为了提高性能,行在您请求它们之前被缓冲(在服务器端和网络缓冲区中)。因此,在您第一次 .MoveNext
调用后,可能会在缓冲区中检索到 100 行,即使您只读取了其中的 10 行。
关于开销,这不是我主要关心的问题,因为这些缓冲区最终具有固定大小:服务器不会去缓冲结果集的所有行,无论有多少(这将是非常一般效率低下)。如果您只读取 10 行,那么您的查询最终是 return 1,000 行还是 1,000,000 行(如果它 运行 完成)在缓冲方面并不重要,但主要是在查询计划方面。尽管如此,它确实增加了开销。
我有一个方法可以使用 SqlDataReader 读取数据并生成 returns 一个 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 服务器传输到客户端,即使数据读取器只读取几行(只要连接打开) - 我应该避免Take(10)
方法,因为无论如何都会将额外的数据传输到客户端,或者避免它是过早的优化(因为 yield return 代码会在读取 10 行然后读取数据后关闭连接无论如何传输都会停止)?
您还可以使用分页 Skip(0) 和 Take(10) 更加灵活。
SQL 服务器 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
C# with sql 2012 - 使用命令的存储过程:)
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=Whosebug;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));
}
}
}
由于是否优化"premature"是主观的,所以我选择将此问题解释为"does using a DataReader
and stopping the read after 10 rows have the same performance characteristics as using TOP(10)
in the query?"
答案是否定的。将 TOP(10)
传递给服务器允许优化器调整读取、内存授予、I/O 缓冲区、锁定粒度和并行性,同时知道查询将 return(在这种情况下,还读取) 最多 10 行。省略 TOP
意味着它必须为客户端将要读取所有行的情况做好准备——无论您是否真的提前停止。
无论您是否阅读,服务器都会发送行,这是不正确的。使用 SqlDataReader
拉取行在概念上是逐行操作:当您发出 Reader.MoveNext
时,您从服务器获取下一行并且仅获取该行。但是为了提高性能,行在您请求它们之前被缓冲(在服务器端和网络缓冲区中)。因此,在您第一次 .MoveNext
调用后,可能会在缓冲区中检索到 100 行,即使您只读取了其中的 10 行。
关于开销,这不是我主要关心的问题,因为这些缓冲区最终具有固定大小:服务器不会去缓冲结果集的所有行,无论有多少(这将是非常一般效率低下)。如果您只读取 10 行,那么您的查询最终是 return 1,000 行还是 1,000,000 行(如果它 运行 完成)在缓冲方面并不重要,但主要是在查询计划方面。尽管如此,它确实增加了开销。