如何在 C# 中使用 SqlDataReader 获取行数
Posted
技术标签:
【中文标题】如何在 C# 中使用 SqlDataReader 获取行数【英文标题】:How to get number of rows using SqlDataReader in C# 【发布时间】:2009-09-05 13:22:42 【问题描述】:我的问题是如何在 C# 中使用 SqlDataReader
获取查询返回的行数。我已经看到了一些关于此的答案,但除了一个声明使用Read()
方法执行while循环并增加一个计数器的答案之外,没有一个明确定义。
我的问题是我试图填充一个多维数组,第一行是列标题名称,之后的每一行都是行数据。
我知道我可以将内容转储到 List 控件中而不用担心,但是为了我个人的启迪,我还想在我选择的时候将数据拉入和拉出数组并将其显示在不同的格式。
所以我认为我不能执行 Read()
然后递增 ++ 方式,因为这意味着我必须打开 Read()
然后再次打开 Read()
以获取行数和列数据。
只是我所说的一个小例子:
int counter = 0;
while (sqlRead.Read())
//get rows
counter++
然后一个 for 循环遍历列并弹出
something.Read();
int dbFields = sqlRead.FieldCount;
for (int i = 0; i < dbFields; i++)
// do stuff to array
【问题讨论】:
【参考方案1】:只有两种选择:
通过读取所有行找出来(然后你不妨存储它们)
事先运行专门的 SELECT COUNT(*) 查询。
两次通过 DataReader 循环非常昂贵,您必须重新执行查询。
并且(感谢 Pete OHanlon)第二个选项仅在您使用具有快照隔离级别的事务时是并发安全的。
由于您无论如何都希望最终将所有行存储在内存中,唯一明智的选择是读取灵活存储(List<>
或DataTable
)中的所有行,然后将数据复制为您想要的任何格式。内存中的操作总是会更有效率。
【讨论】:
Henk 是对的:DataReader 中没有任何成员可以让您获取行数,因为它是仅向前的阅读器。您最好先获取计数,然后执行查询,也许在多结果查询中,这样您只需访问数据库一次。 专用计数的问题在于,计数可能与返回的行数不同,因为其他人以某种方式更改了数据,导致返回的行数。 Pete,你是对的,它需要一个昂贵的 IsolationLevel。 谢谢大家!这越来越清楚了。那么将所有信息转储到 DataSet 还是通过 SQL COUNT(*) 运行、存储它然后运行所需的查询更好?还是我们在谈论运行计数并将所有内容存储在 DataSet 中? ARepeatableRead
隔离级别不执行范围锁定,因此它仍然允许插入记录,您需要使用Snapshot
或Serializable
的隔离级别。【参考方案2】:
如果您不需要检索所有行并希望避免进行双重查询,您可以尝试类似的方法:
using (var sqlCon = new SqlConnection("Server=127.0.0.1;Database=MyDb;User Id=Me;Password=glop;"))
sqlCon.Open();
var com = sqlCon.CreateCommand();
com.CommandText = "select * from BigTable";
using (var reader = com.ExecuteReader())
//here you retrieve what you need
com.CommandText = "select @@ROWCOUNT";
var totalRow = com.ExecuteScalar();
sqlCon.Close();
您可能必须添加一个事务,不确定是否重复使用相同的命令会自动在其上添加一个事务...
【讨论】:
任何人都可以说@@ROWCOUNT 是否总是依赖于上面运行的最后一个查询?如果多个连接并行运行查询会出现问题吗? 需要sqlCon.Close();
吗?我认为using
应该为你做这件事。
如果我们在从读取器检索数据之前需要行数,它将不起作用【参考方案3】:
如上所述,数据集或类型化数据集可能是一个很好的临时结构,您可以使用它来进行过滤。 SqlDataReader 旨在非常快速地读取数据。当您在 while() 循环中时,您仍然连接到数据库,它正在等待您执行您正在做的任何事情,以便在下一个结果继续之前读取/处理它。在这种情况下,如果您拉入所有数据,关闭与数据库的连接并“离线”处理结果,您可能会获得更好的性能。
人们似乎讨厌数据集,因此也可以使用强类型对象的集合来完成上述操作。
【讨论】:
我自己也喜欢 DataSet,因为它们是基于表格的数据的编写良好且非常有用的通用表示。奇怪的是,我注意到大多数为 ORM 避开 DataSet 的人都试图编写自己的代码以使其尽可能通用(通常毫无意义)。 Daniel,'above' 不是引用另一个答案的好方法。【参考方案4】:您无法直接从数据读取器获取行数,因为它是所谓的 firehose 游标 - 这意味着数据是根据正在执行的读取逐行读取的。我建议不要对数据进行 2 次读取,因为在 2 次读取之间数据可能会发生变化,因此您会得到不同的结果。
您可以做的是将数据读入一个临时结构,并使用它来代替第二次读取。或者,您需要更改检索数据的机制并改用 DataTable 之类的东西。
【讨论】:
【参考方案5】:完成 Pit 答案并获得更好的性能: 一站式查询并使用 NextResult 方法。
using (var sqlCon = new SqlConnection("Server=127.0.0.1;Database=MyDb;User Id=Me;Password=glop;"))
sqlCon.Open();
var com = sqlCon.CreateCommand();
com.CommandText = "select * from BigTable;select @@ROWCOUNT;";
using (var reader = com.ExecuteReader())
while(reader.Read())
//iterate code
int totalRow = 0 ;
reader.NextResult();
if(reader.Read())
totalRow = (int)reader[0];
sqlCon.Close();
【讨论】:
【参考方案6】:当我需要返回最佳结果但又想获得与查询匹配的总行数时,我也面临这样的情况。我终于找到了这个解决方案:
public string Format(SelectQuery selectQuery)
string result;
if (string.IsNullOrWhiteSpace(selectQuery.WherePart))
result = string.Format(
@"
declare @maxResult int;
set @maxResult = 0;
WITH Total AS
(
SELECT count(*) as [Count] FROM 2
)
SELECT top (@maxResult) Total.[Count], 1 FROM Total, 2", m_limit.To, selectQuery.SelectPart, selectQuery.FromPart);
else
result = string.Format(
@"
declare @maxResult int;
set @maxResult = 0;
WITH Total AS
(
SELECT count(*) as [Count] FROM 2 WHERE 3
)
SELECT top (@maxResult) Total.[Count], 1 FROM Total, 2 WHERE 3", m_limit.To, selectQuery.SelectPart, selectQuery.FromPart, selectQuery.WherePart);
if (!string.IsNullOrWhiteSpace(selectQuery.OrderPart))
result = string.Format("0 ORDER BY 1", result, selectQuery.OrderPart);
return result;
【讨论】:
以上是关于如何在 C# 中使用 SqlDataReader 获取行数的主要内容,如果未能解决你的问题,请参考以下文章
在 c# 中使用 SqlDataReader 填充 List<T>