web-dev-qa-db-ja.com

C#でSqlDataReaderを使用して行数を取得する方法

私の質問は、C#でSqlDataReaderを使用して、クエリによって返される行数を取得する方法です。これに関するいくつかの答えを見ましたが、Read()メソッドでwhileループを実行し、カウンターをインクリメントすることを示すものを除いて、明確に定義されたものはありませんでした。

私の問題は、最初の行が列ヘッダー名で、その後のすべての行が行データになるように多次元配列を埋めようとしていることです。

リストコントロールに内容をダンプするだけで心配することはできませんが、自分の個人的な知識を得るために、さまざまな形式で選択して表示するときに配列からデータを引き出したり出したりしたいと思います。

したがって、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
}
90
Tomasz Iniewicz

次の2つのオプションのみがあります。

  • すべての行を読んで調べてください(そして、保存することもできます)

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

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

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

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

91
Henk Holterman

すべての行を取得する必要がなく、二重クエリを避けたい場合は、おそらく次のようなものを試すことができます。

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

同じコマンドを再利用するとトランザクションが自動的に追加されるかどうかわからない場合は、トランザクションを追加する必要があります...

8
Pit Ming

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

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

6
Daniel Segan

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

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

5
Pete OHanlon

pitの回答を完了し、パフォーマンスを向上させるには、すべてを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();
}
2
mehdi

また、上位の結果を返す必要があるが、クエリに一致する合計行を取得したいという状況に直面しています。最後にこのソリューションに到達します:

   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;
    }
1
Pit Ming