Cách lấy số hàng bằng SqlDataReader trong C #


98

Câu hỏi của tôi là làm thế nào để lấy số hàng được trả về bởi một truy vấn bằng cách sử dụng SqlDataReadertrong C #. Tôi đã thấy một số câu trả lời về điều này nhưng không có câu trả lời nào được xác định rõ ràng ngoại trừ một câu trả lời cho biết thực hiện một vòng lặp while với Read()phương thức và tăng một bộ đếm.

Vấn đề của tôi là tôi đang cố gắng điền vào một mảng đa chiều với hàng đầu tiên là tên tiêu đề cột và mọi hàng sau đó là dữ liệu hàng.

Tôi biết rằng tôi chỉ có thể kết xuất nội dung trong điều khiển Danh sách và không phải lo lắng về điều đó, nhưng để chỉnh sửa cá nhân của riêng tôi và tôi cũng muốn kéo dữ liệu vào và ra khỏi mảng khi tôi chọn và hiển thị nó ở các định dạng khác nhau.

Vì vậy, tôi nghĩ rằng tôi không thể làm theo cách Read()và sau đó tăng ++ vì điều đó có nghĩa là tôi sẽ phải mở Read()và sau đó mở Read()lại để lấy số lượng hàng và sau đó là dữ liệu cột.

Chỉ là một ví dụ nhỏ về những gì tôi đang nói đến:

int counter = 0;    

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

và sau đó một vòng lặp for để chạy qua các cột và bật lên

something.Read();

int dbFields = sqlRead.FieldCount;

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

Câu trả lời:


96

Chỉ có hai lựa chọn:

  • Tìm hiểu bằng cách đọc tất cả các hàng (và sau đó bạn cũng có thể lưu trữ chúng)

  • chạy trước một truy vấn SELECT COUNT (*) chuyên biệt.

Thực hiện hai lần vòng lặp DataReader rất tốn kém, bạn sẽ phải thực hiện lại truy vấn.

Và (nhờ Pete OHanlon) tùy chọn thứ hai chỉ an toàn đồng thời khi bạn sử dụng giao dịch có mức cách ly Ảnh chụp nhanh.

Vì bạn vẫn muốn lưu trữ tất cả các hàng trong bộ nhớ, tùy chọn hợp lý duy nhất là đọc tất cả các hàng trong một bộ lưu trữ linh hoạt ( List<>hoặc DataTable) và sau đó sao chép dữ liệu sang bất kỳ định dạng nào bạn muốn. Hoạt động trong bộ nhớ sẽ luôn hiệu quả hơn nhiều.


5
Henk nói đúng: không có thành viên nào của DataReader cho phép bạn lấy số hàng vì nó là trình đọc chỉ chuyển tiếp. Tốt hơn hết bạn nên làm trước tiên là lấy số lượng và sau đó thực hiện truy vấn, có lẽ trong một truy vấn nhiều kết quả nên bạn chỉ nhấn cơ sở dữ liệu một lần.
flipdoubt

14
Vấn đề với số lượng chuyên biệt là có khả năng số đếm khác với số hàng được trả về vì ai đó đã thay đổi dữ liệu theo cách dẫn đến số hàng được trả về.
Pete OHanlon

1
Pete, bạn nói đúng, nó sẽ yêu cầu một IsolationLevel đắt tiền.
Henk Holterman

1
Cảm ơn tất cả! Điều này đang trở nên rõ ràng hơn. Vì vậy, tốt hơn là kết xuất tất cả thông tin vào DataSet hay chạy qua SQL COUNT (*), lưu trữ nó và sau đó chạy truy vấn bắt buộc? Hay chúng ta đang nói về việc chạy đếm và lưu trữ mọi thứ trong DataSet?
Tomasz Iniewicz

4
Mức RepeatableReadcô lập không thực hiện khóa phạm vi nên nó vẫn cho phép các bản ghi được chèn vào, bạn cần sử dụng mức cô lập của Snapshothoặc Serializable.
Lukazoid

10

Nếu bạn không cần truy xuất tất cả hàng và muốn tránh thực hiện truy vấn kép, bạn có thể thử một cái gì đó như vậy:

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

Bạn có thể phải thêm một giao dịch mà không chắc chắn nếu việc sử dụng lại cùng một lệnh sẽ tự động thêm một giao dịch trên đó ...


1
Bất kỳ ai cũng có thể nói liệu @@ ROWCOUNT có luôn dựa vào truy vấn cuối cùng chạy ở trên không? Sự cố nếu nhiều kết nối chạy truy vấn song song?
YvesR

1
Nó có cần thiết để làm gì sqlCon.Close();không? Tôi nghĩ điều đó usingnên làm điều đó cho bạn.
hơi xanh

1
nó sẽ không hoạt động trong trường hợp chúng tôi cần số lượng hàng trước khi lấy dữ liệu từ trình đọc
Heemanshu Bhalla

8

Theo trên, một tập dữ liệu hoặc tập dữ liệu đã nhập có thể là một cấu trúc tạm thời tốt mà bạn có thể sử dụng để thực hiện việc lọc của mình. SqlDataReader có nghĩa là đọc dữ liệu rất nhanh. Trong khi ở trong vòng lặp while (), bạn vẫn được kết nối với DB và nó đang chờ bạn làm bất cứ điều gì bạn đang làm để đọc / xử lý kết quả tiếp theo trước khi nó chuyển sang. Trong trường hợp này, bạn có thể nhận được hiệu suất tốt hơn nếu lấy tất cả dữ liệu, đóng kết nối với DB và xử lý kết quả "ngoại tuyến".

Mọi người dường như ghét bộ dữ liệu, vì vậy những điều trên cũng có thể được thực hiện với một bộ sưu tập các đối tượng được đánh máy mạnh.


2
Bản thân tôi yêu DataSets, vì chúng là một bản trình bày chung được viết tốt và cực kỳ hữu ích cho dữ liệu dựa trên bảng. Thật kỳ lạ, tôi đã nhận thấy rằng hầu hết những người tránh DataSet cho ORM đều là những người cố gắng viết mã của riêng họ để càng chung chung càng tốt (thường là vô nghĩa).
MusiGenesis

5
Daniel, 'ở trên' không phải là một cách hay để tham khảo một câu trả lời khác.
Henk Holterman

6

Bạn không thể đếm số hàng trực tiếp từ trình đọc dữ liệu vì nó được gọi là con trỏ firehose - có nghĩa là dữ liệu được đọc trên cơ sở từng hàng dựa trên việc đọc đang được thực hiện. Tôi khuyên bạn không nên thực hiện 2 lần đọc trên dữ liệu vì có khả năng dữ liệu đã thay đổi giữa việc thực hiện 2 lần đọc, và do đó bạn sẽ nhận được các kết quả khác nhau.

Những gì bạn có thể làm là đọc dữ liệu vào một cấu trúc tạm thời và sử dụng nó thay cho lần đọc thứ hai. Ngoài ra, bạn sẽ cần phải thay đổi cơ chế mà bạn truy xuất dữ liệu và sử dụng thứ gì đó như DataTable để thay thế.


5

để hoàn thành câu trả lời Pit và để có hiệu suất tốt hơn: nhận tất cả trong một truy vấn và sử dụng phương pháp 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

Tôi cũng phải đối mặt với tình huống khi tôi cần trả về kết quả hàng đầu nhưng cũng muốn nhận tổng số hàng phù hợp với truy vấn. tôi đoán được giải pháp này:

   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;
    }
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.