如何使用 C # 中的 SqlDataReader 获取行数

我的问题是如何使用 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
}
309928 次浏览

There are only two options:

  • Find out by reading all rows (and then you might as well store them)

  • run a specialized SELECT COUNT(*) query beforehand.

Going twice through the DataReader loop is really expensive, you would have to re-execute the query.

And (thanks to Pete OHanlon) the second option is only concurrency-safe when you use a transaction with a Snapshot isolation level.

Since you want to end up storing all rows in memory anyway the only sensible option is to read all rows in a flexible storage (List<> or DataTable) and then copy the data to any format you want. The in-memory operation will always be much more efficient.

You can't get a count of rows directly from a data reader because it's what is known as a firehose cursor - which means that the data is read on a row by row basis based on the read being performed. I'd advise against doing 2 reads on the data because there's the potential that the data has changed between doing the 2 reads, and thus you'd get different results.

What you could do is read the data into a temporary structure, and use that in place of the second read. Alternatively, you'll need to change the mechanism by which you retrieve the data and use something like a DataTable instead.

Per above, a dataset or typed dataset might be a good temorary structure which you could use to do your filtering. A SqlDataReader is meant to read the data very quickly. While you are in the while() loop you are still connected to the DB and it is waiting for you to do whatever you are doing in order to read/process the next result before it moves on. In this case you might get better performance if you pull in all of the data, close the connection to the DB and process the results "offline".

People seem to hate datasets, so the above could be done wiht a collection of strongly typed objects as well.

If you do not need to retrieve all the row and want to avoid to make a double query, you can probably try something like that:

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();
}

You may have to add a transaction not sure if reusing the same command will automatically add a transaction on it...

I also face a situation when I needed to return a top result but also wanted to get the total rows that where matching the query. i finaly get to this solution:

   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;
}

to complete of Pit answer and for better perfromance : get all in one query and use NextResult method.

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();
}