从SqlDataReader获取结果集的数量
本文关键字:结果 SqlDataReader 获取 | 更新日期: 2023-09-27 18:26:47
我有一个返回多个结果的SQL Server存储过程。存储过程的主体可能如下所示:
SELECT * FROM tableA;
SELECT * FROM tableB;
SELECT * FROM tableC;
在这种情况下,存储过程将返回3个结果集。其他存储过程可能返回,例如1、0或任意数量的结果集。每个结果集可能包含0行或更多行。加载这些行时,我需要调用IDataReader.NextResult()
来在结果集之间导航。
如何可靠地获得C#中结果集的计数(而不是行计数)?
在IDataReader
中似乎没有直接计算结果计数的属性或方法。该接口更倾向于以增量/流的方式使用。因此,要计算返回的结果集的数量,请在每次调用IDataReader.NextResult()
时增加一个计数器,它会在消耗数据时返回true
。
然而,有一个陷阱。这个IDataReader.NextResult()
的文档说明:
默认情况下,数据读取器位于第一个结果上。
考虑以下场景:
- 该命令返回了0个结果集。您对
IDataReader.NextResult()
的第一次调用返回false
- 该命令返回了1个结果集。您对
IDataReader.NextResult()
的第一次调用返回false
- 该命令返回了2个结果集。您对
IDataReader.NextResult()
的第二次调用返回false
您可以看到,只要至少有一个结果集,我们就有足够的信息来计算结果集的数量。这将是IDataReader.NextResult()
返回true
的次数加一。
为了检测是否有0个结果集,我们使用读取器中的另一个属性:IDataRecord.FieldCount
。该属性的文档说明:
如果未定位在有效的记录集中,则为0;否则为当前记录中的列数。默认值为-1。
因此,我们可以在第一次打开读取器时读取该字段,以确定我们是否在有效的结果集中。如果该命令未生成结果集,则读取器上的IDataRecord.FieldCount
值最初将小于1。如果该命令至少生成一个结果集,则该值最初将为正。这假设一个结果集不可能有0列(我认为您可以用SQL假设这一点,但不确定)。
因此,我将使用以下内容来计算结果集的数量。如果您还需要保存数据,则必须将该逻辑插入其中:
using (var reader = command.ExecuteReader())
{
var resultCount = 0;
do
{
if (reader.FieldCount > 0)
resultCount++;
while (reader.Read())
{
// Insert logic to actually consume data here…
// HandleRecordByResultIndex(resultCount - 1, (IDataRecord)reader);
}
} while (reader.NextResult());
}
我已经用System.Data.SqlClient
和命令PRINT 'hi'
(0个结果集)、SELECT 1 x WHERE 1=0
(1个结果集,和SELECT 1 x WHERE 1=0; SELECT 1 x WHERE 1=0
(2个结果集。
使用DataReader.NextResult
将读取器推进到下一个结果集
using (var con = new SqlConnection(Properties.Settings.Default.ConnectionString))
{
using (var cmd = new SqlCommand("SELECT * FROM TableA; SELECT * FROM TableB; SELECT * FROM TableC;", con))
{
con.Open();
using (IDataReader rdr = cmd.ExecuteReader())
{
while (rdr.Read())
{
int firstIntCol = rdr.GetInt32(0); // assuming the first column is of type Int32
// other fields ...
}
if (rdr.NextResult())
{
while (rdr.Read())
{
int firstIntCol = rdr.GetInt32(0); // assuming the first column is of type Int32
// other fields ...
}
if (rdr.NextResult())
{
while (rdr.Read())
{
int firstIntCol = rdr.GetInt32(0); // assuming the first column is of type Int32
// other fields ...
}
}
}
}
}
}
除了手动SqlDataReader
方法外,在接受的答案中,另一个需要注意的解决方案是使用SqlDataAdapter
和DataSet
s以及DataTable
s。
当使用这些类时,整个结果集会一次性从服务器中检索出来,您可以在空闲时对它们进行迭代。此外,如果您还设置了DeleteCommand
、InsertCommand
和UpdateCommand
属性,其他几个.net类也知道DataSets和DataTables,并且可以直接连接到它们,以进行只读或读写数据访问。你所得到的"免费的;这样,就可以更改DataSet
中的数据,然后简单地调用Update()
将本地更改推送到数据库。您还获得了RowUpdated
事件处理程序,可以使用它自动实现这一点。
DataSet
s和DataTable
s也保留了数据库架构中的元数据,因此您仍然可以根据需要按名称或索引访问列。
总的来说,它们是一个很好的功能,尽管肯定比SqlDataReader
更重
SqlDataAdapter
的文档如下:https://learn.microsoft.com/en-us/dotnet/api/system.data.sqlclient.sqldataadapter