Cara mendapatkan jumlah baris menggunakan SqlDataReader di C #

98

Pertanyaan saya adalah bagaimana mendapatkan jumlah baris yang dikembalikan oleh kueri menggunakan SqlDataReaderC #. Saya telah melihat beberapa jawaban tentang ini tetapi tidak ada yang didefinisikan dengan jelas kecuali yang menyatakan untuk melakukan loop sementara dengan Read()metode dan menambah penghitung.

Masalah saya adalah bahwa saya mencoba untuk mengisi array multi-dimensi dengan baris pertama menjadi nama tajuk kolom dan setiap baris setelah itu menjadi data baris.

Saya tahu bahwa saya bisa membuang barang-barang itu ke dalam kontrol Daftar dan tidak mengkhawatirkannya, tetapi untuk perbaikan pribadi saya dan saya juga ingin menarik data masuk dan keluar dari larik saat saya memilih dan menampilkannya dalam format yang berbeda.

Jadi saya pikir saya tidak bisa melakukan cara Read()lalu kenaikan ++ karena itu berarti saya harus membuka Read()dan kemudian membuka Read()lagi untuk mendapatkan jumlah baris dan kemudian data kolom.

Hanya contoh kecil dari apa yang saya bicarakan:

int counter = 0;    

while (sqlRead.Read())
{
    //get rows
    counter++
}

dan kemudian loop for dijalankan melalui kolom dan pop

something.Read();

int dbFields = sqlRead.FieldCount;

for (int i = 0; i < dbFields; i++)
{
   // do stuff to array
}
Tomasz Iniewicz
sumber

Jawaban:

97

Hanya ada dua pilihan:

  • Cari tahu dengan membaca semua baris (dan kemudian Anda mungkin juga menyimpannya)

  • jalankan kueri SELECT COUNT (*) khusus sebelumnya.

Pergi dua kali melalui loop DataReader sangat mahal, Anda harus mengeksekusi ulang kueri.

Dan (terima kasih kepada Pete OHanlon) opsi kedua hanya aman-konkurensi saat Anda menggunakan transaksi dengan tingkat isolasi Snapshot.

Karena Anda ingin menyimpan semua baris dalam memori, satu-satunya pilihan yang masuk akal adalah membaca semua baris dalam penyimpanan fleksibel ( List<>atau DataTable) dan kemudian menyalin data ke format apa pun yang Anda inginkan. Operasi dalam memori akan selalu jauh lebih efisien.

Henk Holterman
sumber
5
Henk benar: tidak ada anggota DataReader yang memungkinkan Anda mendapatkan jumlah baris karena ini hanya pembaca yang meneruskan. Lebih baik Anda terlebih dahulu menghitung dan kemudian menjalankan kueri, mungkin dalam kueri multi-hasil sehingga Anda hanya menekan database satu kali.
flipdoubt
14
Masalah dengan penghitungan khusus adalah bahwa ada potensi penghitungan yang berbeda dari jumlah baris yang dikembalikan karena orang lain telah mengubah data dengan cara yang mengarah ke jumlah baris yang dikembalikan.
Pete OHanlon
1
Pete, kamu benar, itu akan membutuhkan IsolationLevel yang mahal.
Henk Holterman
1
Terima kasih semua! Ini menjadi lebih jelas. Jadi, apakah lebih baik membuang semua info ke Kumpulan Data atau menjalankan SQL COUNT (*), menyimpannya, lalu menjalankan kueri yang diperlukan? Atau apakah kita berbicara tentang menghitung berjalan dan menyimpan semuanya di DataSet?
Tomasz Iniewicz
4
Sebuah RepeatableReadtingkat isolasi tidak melakukan berbagai-penguncian sehingga masih memungkinkan catatan yang akan dimasukkan, Anda harus menggunakan tingkat isolasi Snapshotatau Serializable.
Lukazoid
10

Jika Anda tidak perlu mengambil semua baris dan ingin menghindari membuat kueri ganda, Anda mungkin dapat mencoba sesuatu seperti itu:

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

Anda mungkin harus menambahkan transaksi tidak yakin apakah menggunakan kembali perintah yang sama akan secara otomatis menambahkan transaksi di atasnya ...

Pit Ming
sumber
1
Siapapun dapat mengatakan jika @@ ROWCOUNT selalu mengandalkan kueri terakhir yang berjalan di atas? Masalah jika banyak koneksi menjalankan kueri paralel?
YvesR
1
Apakah itu perlu dilakukan sqlCon.Close();? Saya pikir itu usingharus dilakukan untuk Anda.
kebiruan
1
itu tidak akan berfungsi jika kita membutuhkan rowcount sebelum mengambil data dari pembaca
Heemanshu Bhalla
8

Seperti yang disebutkan di atas, kumpulan data atau kumpulan data yang diketik mungkin merupakan struktur sementara yang baik yang dapat Anda gunakan untuk melakukan pemfilteran. SqlDataReader dimaksudkan untuk membaca data dengan sangat cepat. Saat Anda berada di while () loop, Anda masih terhubung ke DB dan menunggu Anda melakukan apa pun yang Anda lakukan untuk membaca / memproses hasil berikutnya sebelum melanjutkan. Dalam hal ini Anda mungkin mendapatkan kinerja yang lebih baik jika Anda menarik semua data, menutup koneksi ke DB dan memproses hasilnya "offline".

Orang-orang tampaknya membenci kumpulan data, jadi hal di atas dapat dilakukan dengan koleksi objek yang diketik dengan kuat juga.

Daniel Segan
sumber
2
Saya suka DataSets sendiri, karena mereka adalah representasi generik yang ditulis dengan baik dan sangat berguna dari data berbasis tabel. Cukup aneh, saya telah memperhatikan bahwa kebanyakan orang yang menghindari DataSet untuk ORM adalah orang yang sama yang mencoba menulis kode mereka sendiri agar menjadi generik mungkin (biasanya tidak ada gunanya).
MusiGenesis
5
Daniel, 'di atas' bukanlah cara yang baik untuk merujuk pada jawaban lain.
Henk Holterman
6

Anda tidak bisa mendapatkan hitungan baris secara langsung dari pembaca data karena itulah yang dikenal sebagai kursor firehose - yang berarti bahwa data dibaca baris demi baris berdasarkan pembacaan yang dilakukan. Saya menyarankan agar tidak melakukan 2 pembacaan pada data karena ada potensi bahwa data telah berubah antara melakukan 2 pembacaan, dan dengan demikian Anda akan mendapatkan hasil yang berbeda.

Apa yang dapat Anda lakukan adalah membaca data ke dalam struktur sementara, dan menggunakannya sebagai pengganti pembacaan kedua. Alternatifnya, Anda harus mengubah mekanisme yang digunakan untuk mengambil data dan menggunakan sesuatu seperti DataTable.

Pete OHanlon
sumber
5

untuk menyelesaikan jawaban Pit dan untuk kinerja yang lebih baik: dapatkan semua dalam satu kueri dan gunakan metode 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();
}
mehdi
sumber
1

Saya juga menghadapi situasi ketika saya perlu mengembalikan hasil teratas tetapi juga ingin mendapatkan total baris yang cocok dengan kueri. Saya akhirnya mendapatkan solusi ini:

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