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
}

回答:


96

オプションは2つしかありません。

  • すべての行を読み取って見つけます(そして、それらを保存することもできます)

  • 事前に特別なSELECT COUNT(*)クエリを実行します。

DataReaderループを2回実行すると非常にコストがかかるため、クエリを再実行する必要があります。

(Pete OHanlonのおかげで)2番目のオプションは、スナップショット分離レベルでトランザクションを使用する場合にのみ、同時実行に対して安全です。

とにかくすべての行をメモリに保存する必要があるので、唯一の賢明なオプションは、柔軟なストレージ(List<>またはDataTable)ですべての行を読み取り、データを任意の形式にコピーすることです。インメモリ操作は常にはるかに効率的です。


5
Henkは正解です。前方読み取り専用リーダーであるため、行数を取得できるDataReaderのメンバーはありません。最初にカウントを取得してからクエリを実行することをお勧めします。おそらく複数結果クエリで、データベースに1回しかアクセスしないようにします。
フリップダウト2009

14
特殊化されたカウントの問題は、他の誰かが行の数を返すような方法でデータを変更したため、カウントが返された行の数と異なる可能性があることです。
ピートオアンロン2009

1
ピート、その通り、高価なIsolationLevelが必要になります。
ヘンクホルターマン

1
皆さん、ありがとうございました!これはより明確になってきています。それでは、すべての情報をDataSetにダンプするか、SQL COUNT(*)を実行して保存し、必要なクエリを実行する方が良いでしょうか?それとも、カウントを実行してすべてをDataSetに格納することについて話しているのでしょうか。
Tomasz Iniewicz

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あなたのためにそれを行う必要があります。
2015年

1
リーダーからデータを取得する前に行数が必要な場合は機能しません
Heemanshu Bhalla

8

上記のとおり、データセットまたは型付きデータセットは、フィルタリングに使用できる優れた一時的な構造である可能性があります。SqlDataReaderは、データを非常に迅速に読み取ることを目的としています。while()ループにいる間もDBに接続されており、次の結果を読み取り/処理するために、何を実行するかを待機しています。この場合、すべてのデータを取得し、DBへの接続を閉じて、結果を「オフライン」で処理すると、パフォーマンスが向上する可能性があります。

人々はデータセットを嫌うようですので、上記は強く型付けされたオブジェクトのコレクションでも行うことができます。


2
DataSetは、テーブルベースのデータを適切に記述した非常に便利な汎用表現であるため、私自身も大好きです。奇妙なことに、ORMのDataSetを避けているほとんどの人は、独自のコードをできるだけ一般的なもの(通常は無意味)に書き込もうとする人と同じであることに気付きました。
MusiGenesis 2009

5
ダニエル、「上」は別の答えを参照する良い方法ではありません。
ヘンクホルターマン

6

これはファイアホースカーソルと呼ばれるものであるため、データリーダーから行の数を直接取得することはできません。つまり、実行されている読み取りに基づいて、行ごとにデータが読み取られます。2つの読み取りを実行する間にデータが変更される可能性があるため、データに対して2つの読み取りを実行しないことをお勧めします。その結果、異なる結果が得られます。

あなたができることは、データを一時的な構造に読み込み、2番目の読み込みの代わりにそれを使用することです。または、データを取得するメカニズムを変更して、代わりにDataTableなどを使用する必要があります。


5

ピットの回答を完了し、パフォーマンスを向上させるには、1つのクエリですべてを取得し、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;
    }
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.