【问题标题】:Get number of result sets from a SqlDataReader从 SqlDataReader 获取结果集的数量
【发布时间】:2013-08-09 23:37:07
【问题描述】:

我有一个返回多个结果的 SQL Server 存储过程。存储过程的主体可能如下所示:

SELECT * FROM tableA;
SELECT * FROM tableB;
SELECT * FROM tableC;

在这种情况下,存储过程返回 3 个结果集。其他存储过程可能会返回,例如,1、0 或任意数量的结果集。每个结果集中可能包含 0 行或更多行。加载这些时,我需要调用 IDataReader.NextResult() 在结果集之间导航。

如何在 C# 中可靠地获取结果集的计数(而不是行计数)?

【问题讨论】:

  • count是什么意思,是结果集的个数还是每行数的个数?
  • 支持所有选择的结果计数,就像他说的那样。
  • 我的意思是结果集的计数(树上方)。每个集合可以包含一些行
  • 我认为没有任何方法可以计算结果集的数量 - 您只需枚举它们(使用reader.GetNextResult())并处理它们。但是,您无法提前确定要处理多少个结果集
  • 只是为了扩展 marc_s 的评论。我认为将读者移过结果不会让我返回 - 所以我无法以动态的方式提前可靠地知道。即使它今天有效,我也可以期待明天的实施更改会打破我的假设或使其有效的黑客攻击。

标签: c# sql sql-server sqldatareader


【解决方案1】:

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 个结果集)对此进行了测试。

【讨论】:

  • 根据场景,我也使用SqlDataReader.HasRows而不是FieldCount(我可能使用HasRowsFieldCount更频繁)。但是,是的,do {...} while() 是一种比公认答案中显示的复杂且不太灵活的方法更好(且更清洁)的方法。 (+1)。没错,一个结果集必须至少有一列。
  • @srutzky 我写的代码可能永远不会是 System.Data.SqlClient 以外的任何东西,而且我对原始 T-SQL 没有任何抽象,但我仍然觉得尝试将自己限制在可用的接口(除了利用 TPL 兼容的Async() 变体,我使用DbCommand)。 FieldCount 确实感觉很hacky,但至少我可以获得“纯”接口代码;-)
  • 这是我的问题的完美答案。谢谢
【解决方案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 ...
                    }
                }
            }
        }
    }
}

【讨论】:

  • if (rdr.NextResult()) count++;你有什么主意吗?方法 NextResult() 移动 sqldatareader 的位置
  • @Mikhail:解释你的问题。您需要每个表的行数还是需要结果集的计数还是需要每个结果集的行数(如上所示)?如果您只需要结果集的数量:int count = 0; while(rdr.NextResult())count++; 应该可以工作。
  • 好的,如果我使用这种方法,我如何才能在计数后返回第一条记录之前的sqldatareader位置?
  • @isxaker 关键是你不能。您可以将所有数据读入一个集合,例如List<T>(如果处理多个结果集,您将需要更复杂的东西),然后您可以同时使用count data pointsuse the data
猜你喜欢
  • 1970-01-01
  • 2013-10-27
  • 1970-01-01
  • 1970-01-01
  • 2017-04-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-15
相关资源
最近更新 更多