2009-09-05 42 views
79

Câu hỏi của tôi là cách lấy số hàng được truy vấn trả về bằng cách sử dụng SqlDataReader trong C#. Tôi đã nhìn thấy một số câu trả lời về điều này nhưng không ai được xác định rõ ràng ngoại trừ một trong những tiểu bang để làm một vòng lặp trong khi với phương pháp Read() và tăng truy cập.Cách lấy số hàng bằng cách sử dụng SqlDataReader trong C#

Vấn đề của tôi là tôi đang cố gắng điền vào 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 đó sẽ là dữ liệu hàng.

Tôi biết rằng tôi có thể đổ nội dung trong một điều khiển Danh sách và không lo lắng về điều đó, nhưng đối với bản 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 Read() và sau đó tăng ++ cách vì điều đó có nghĩa là tôi sẽ phải mở Read() và sau đó mở lại để nhận số lượng hàng và sau đó là dữ liệu cột.

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

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à pop

something.Read(); 

int dbFields = sqlRead.FieldCount; 

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

Trả lời

75

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 truy vấn SELECT COUNT (*) chuyên biệt trước.

Đi hai lần qua vòng lặp DataReader thực sự 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ỉ đồng thời an toàn khi bạn sử dụng giao dịch có mức cô lập Snapshot.

Vì bạ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 bộ nhớ 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.

+4

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

+12

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

+0

Pete, bạn nói đúng, nó sẽ đòi hỏi một IsolationLevel đắt tiền. –

4

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ở hàng theo 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 cấu trúc tạm thời và sử dụng dữ liệu đó thay cho lần đọc thứ hai. Ngoài ra, bạn sẽ cần phải thay đổi cơ chế mà bạn lấy dữ liệu và sử dụng một cái gì đó giống như một DataTable thay thế.

6

Ở trên, tập dữ liệu hoặc tập dữ liệu đã nhập có thể là cấu trúc tạm thời tốt mà bạn có thể sử dụng để lọc. Một SqlDataReader có nghĩa là để đọc dữ liệu rất nhanh chóng. Trong khi bạn đang ở trong vòng lặp while() bạn vẫn đang 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ó tiếp tục.Trong trường hợp này, bạn có thể nhận được hiệu suất tốt hơn nếu bạn kéo tất cả dữ liệu, đóng kết nối tớ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, ở trên có thể được thực hiện với một bộ sưu tập các đối tượng được nhập mạnh mẽ.

+1

Tôi yêu DataSets bản thân mình, vì chúng là một đại diện chung được viết tốt và cực kỳ hữu ích về 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 được DataSet cho ORM đều là những người cố gắng viết mã riêng của họ để trở thành chung nhất có thể (thường là vô nghĩa). – MusiGenesis

+4

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

17

Các lớp quan hệ/Số liệu là tùy chọn phù hợp.

using (DataTable dt = new DataTable()) 
{ 
    dt.Load(sqlRead); 
    Console.WriteLine(dt.Rows.Count); 
} 
+6

Tải tất cả dữ liệu chỉ để nhận được số hàng không phải là một ý tưởng hay. Đặc biệt với DataTable, điều này sẽ dẫn đến chi phí bộ nhớ lỗi. – shatl

+0

@shatl - Đồng ý! nhưng tùy chọn này được đề xuất bởi nhiều poster bao gồm Henk Holterman và tôi nghĩ OP muốn đảm bảo rằng không có bất kỳ phương pháp trực tiếp nào trả về số lượng hàng. – adatapost

+1

@AVD DataTable là trọng lượng nặng chỉ để có được số lượng hàng. –

5

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

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 không chắc chắn nếu tái sử dụng cùng một lệnh sẽ tự động thêm một giao dịch vào nó ...

+0

Bất cứ ai cũng có thể nói nếu @@ ROWCOUNT luôn dựa vào truy vấn cuối cùng chạy trên? Vấn đề nếu nhiều kết nối chạy truy vấn song song? – YvesR

+0

Nó không phải là một vấn đề nếu bạn sử dụng giao dịch ... –

+0

Có cần thiết để làm 'sqlCon.Đóng(); '? Tôi nghĩ rằng 'sử dụng' nên làm điều đó cho bạn. – bluish

0

Tôi cũng phải đối mặt với một tình huống khi tôi cần trả lại kết quả hàng đầu nhưng cũng muốn có tổng số hàng phù hợp với truy vấn. tôi cuối cùng nhậ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; 
    } 
Các vấn đề liên quan