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


98

我的问题是如何获取SqlDataReader在C#中使用查询返回的行数。我已经看到了一些有关此问题的答案,但是除了状态声明使用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
}

Answers:


96

只有两个选项:

  • 通过读取所有行来查找(然后您也可以存储它们)

  • 事先运行专门的SELECT COUNT(*)查询。

两次遍历DataReader循环确实很昂贵,您将不得不重新执行查询。

并且(感谢Pete OHanlon),第二个选项仅在使用具有快照隔离级别的事务时才是并发安全的。

由于您无论如何都要结束将所有行存储在内存中,因此唯一明智的选择是读取灵活存储(List<>DataTable)中的所有行,然后将数据复制为所需的任何格式。内存中操作将始终更加高效。


5
Henk是对的:DataReader的任何成员都不允许您获取行数,因为它是仅向前的阅读器。您最好先进行计数,然后再执行查询(最好在多结果查询中执行),这样您只需访问数据库一次。
flipdoubt

14
专用计数的问题在于计数有可能与返回的行数不同,因为其他人以导致返回的行数的方式更改了数据。
皮特·OHanlon,09年

1
皮特,您是对的,这需要昂贵的IsolationLevel。
亨克·霍尔特曼

1
谢谢你们!这变得越来越清楚。那么将所有信息转储到数据集中还是通过SQL COUNT(*)运行,存储它然后运行所需的查询会更好吗?还是我们在谈论运行计数并将所有内容存储在DataSet中?
Tomasz Iniewicz 09年

4
一个RepeatableRead隔离级别不执行范围锁定所以它仍然允许插入的记录,你需要使用的隔离级别SnapshotSerializable
2014年

10

如果您不需要检索所有行并希望避免进行重复查询,则可以尝试执行以下操作:

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

您可能必须添加事务,不确定重新使用同一命令是否会自动在其上添加事务...


1
有人可以说@@ ROWCOUNT是否始终依赖上面运行的最后一个查询吗?如果许多连接并行运行查询,会出现问题吗?
YvesR 2015年

1
需要这样做sqlCon.Close();吗?我认为using应该为您做。
蓝色

1
如果在从读取器中获取数据之前需要行
计数

8

根据上述,数据集或类型化数据集可能是一个很好的临时结构,可用于进行过滤。SqlDataReader用于非常快速地读取数据。当您进入while()循环时,您仍然连接到数据库,并且它在等待您做任何事情,以便在下一个结果继续运行之前读取/处理它。在这种情况下,如果您提取所有数据,关闭与数据库的连接并“离线”处理结果,则可能会获得更好的性能。

人们似乎讨厌数据集,因此上述操作也可以通过使用强类型对象的集合来完成。


2
我个人喜欢DataSet,因为它们是基于表的数据的精心编写且极其有用的通用表示形式。奇怪的是,我注意到大多数避免使用DataSet for ORM的人都是试图编写自己的代码以使其尽可能通用的人(通常毫无意义)。
MusiGenesis

5
丹尼尔(Daniel),“高于”不是引用其他答案的好方法。
Henk Holterman

6

您无法直接从数据读取器中获取行数,因为这就是所谓的流水光标(firehose cursor)-这意味着将基于执行的读取逐行读取数据。我建议不要对数据进行两次读取,因为两次读取之间数据可能会发生变化,因此您会得到不同的结果。

您可以做的是将数据读取到一个临时结构中,并用它代替第二次读取。另外,您将需要更改检索数据的机制,并改用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();
}

1

当我需要返回最佳结果但又想获得与查询匹配的总行时,我也遇到了一种情况。我最终得到这个解决方案:

   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;
    }
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.