2012-06-28 34 views
10

Tôi đã cố gắng làm tiêu đề càng cụ thể càng tốt. Về cơ bản những gì tôi đã chạy bên trong một chủ đề nền bây giờ là một số mã trông giống như:Cách thực hiện truy vấn SQL đối với hoạt động DataTable có thể bị hủy

SqlConnection conn = new SqlConnection(connstring); 
        SqlCommand cmd = new SqlCommand(query, conn); 
        conn.Open(); 
        SqlDataAdapter sda = new SqlDataAdapter(cmd); 
        sda.Fill(Results); 
        conn.Close(); 
        sda.Dispose(); 

Trường hợp truy vấn là một chuỗi đại diện cho truy vấn tốn thời gian và conn là đối tượng kết nối.

Vấn đề của tôi bây giờ là tôi cần nút dừng. Tôi đã nhận ra rằng việc giết người làm nền sẽ vô giá trị vì tôi vẫn muốn giữ kết quả còn lại sau khi truy vấn bị hủy bỏ. Thêm vào đó, nó sẽ không thể kiểm tra trạng thái bị hủy cho đến sau truy vấn.

Những gì tôi đã đi lên với cho đến nay:

Tôi đã cố gắng để khái niệm làm thế nào để xử lý việc này một cách hiệu quả mà không cần dùng quá lớn của một hit hiệu suất.

Ý tưởng của tôi là sử dụng SqlDataReader để đọc dữ liệu từ mảnh truy vấn tại một thời điểm để tôi có "vòng lặp" để kiểm tra cờ tôi có thể đặt từ GUI qua nút. Vấn đề là xa như tôi biết tôi không thể sử dụng phương thức Load() của một datatable và vẫn có thể hủy bỏ sqlcommand. Nếu tôi sai, xin vui lòng cho tôi biết vì điều đó sẽ làm cho việc hủy bỏ dễ dàng hơn một chút.

Trong ánh sáng của những gì tôi phát hiện ra tôi đến việc thực hiện tôi chỉ có thể hủy bỏ giữa truy vấn SqlCommand nếu tôi đã làm một cái gì đó giống như dưới đây (pseudo-code):

while(reader.Read()) 
{ 
//check flag status 
//if it is set to 'kill' fire off the kill thread 

//otherwise populate the datatable with what was read 
} 

Tuy nhiên, nó dường như với tôi điều này sẽ rất hiệu quả và có thể tốn kém. Đây có phải là cách duy nhất để giết một sqlcommand trong tiến trình mà hoàn toàn cần phải được trong một datatable? Bất kỳ trợ giúp sẽ được đánh giá cao!

+1

một câu hỏi rất phong nha! – Adi

+0

Giết chủ đề không bao giờ là một ý tưởng hay. Bạn đã thử 'cmd.Cancel();'? –

+0

Tại sao bạn nghĩ sử dụng DataReader sẽ tốn kém? – Habib

Trả lời

5

Có thực sự hai công đoạn mà vấn đề hủy:

  1. hủy việc thực hiện truy vấn ban đầu trước khi các hàng đầu tiên được trả về
  2. hủy bỏ quá trình đọc các hàng khi họ được phục vụ

Tuỳ theo tính chất của thực tế tuyên bố sql, eithe r của các bước này có thể là 99% thời gian, vì vậy cả hai bước này nên được xem xét. Ví dụ: gọi SELECT * trên một số bảng có hàng tỷ hàng sẽ không có thời gian để thực hiện thực hiện nhưng sẽ mất nhiều thời gian đọc. Ngược lại, yêu cầu tham gia siêu phức tạp trên các bảng được điều chỉnh kém và sau đó gói tất cả trong một số mệnh đề tổng hợp có thể mất vài phút để thực thi nhưng thời gian không đáng kể để đọc một số hàng khi chúng thực sự được trả về.

Các công cụ cơ sở dữ liệu nâng cao cũng sẽ lưu trữ nhiều khối hàng tại một thời điểm cho các truy vấn phức tạp, vì vậy bạn sẽ thấy các khoảng dừng xen kẽ nơi động cơ đang thực hiện truy vấn trên hàng loạt tiếp theo và sau đó là dữ liệu nhanh trả về lô kết quả tiếp theo.

hủy việc thực hiện truy vấn

Để có thể hủy bỏ một truy vấn trong khi nó đang thực hiện, bạn có thể sử dụng một trong những định nghĩa chồng cho SqlCommand.BeginExecuteReader để bắt đầu truy vấn, và gọi SqlCommand.Cancel để hủy bỏ nó. Hoặc bạn có thể gọi ExecuteReader() đồng bộ trong một chuỗi và vẫn gọi Hủy() từ một chuỗi khác. Tôi không bao gồm các ví dụ mã vì có rất nhiều tài liệu trong tài liệu.

hủy bỏ các hoạt động đọc

Ở đây sử dụng một lá cờ boolean đơn giản có lẽ là cách dễ dàng nhất. Và hãy nhớ nó thực sự dễ dàng để điền vào một dòng của bảng dữ liệu bằng cách sử dụng quá tải Rows.Add() mà phải mất một mảng của đối tượng, đó là:

object[] buffer = new object[reader.FieldCount] 
while(reader.Read()) { 
    if(cancelFlag) break; 
    reader.GetValues(buffer); 
    dataTable.Rows.Add(buffer); 
} 

hủy chặn cuộc gọi đến đọc()

Một loại trường hợp hỗn hợp xảy ra khi, như đã đề cập trước đó, một cuộc gọi đến reader.Read() gây ra các công cụ cơ sở dữ liệu để làm một đợt xử lý chuyên sâu. Như đã lưu ý trong tài liệu MSDN, các cuộc gọi tới Read() có thể bị chặn trong trường hợp này ngay cả khi truy vấn ban đầu được thực hiện với BeginExecuteReader. Bạn vẫn có thể giải quyết vấn đề này bằng cách gọi Read() trong một chuỗi đang xử lý tất cả việc đọc nhưng gọi số Cancel() trong một chuỗi khác. Cách bạn biết nếu bạn đọc là trong một chặn Read gọi là phải có lá cờ khác các bản cập nhật đọc chủ đề trong khi thread giám sát lần đọc:

... 
inRead = true 
while(reader.Read()) { 
    inRead = false 
    ... 
    inRead = true 
} 

// Somewhere else: 
private void foo_onUITimerTick(...) { 
    status.Text = inRead ? "Waiting for server" : "Reading"; 
} 

Về hiệu suất Reader vs Adaptor

Một DataReader thường nhanh hơn sử dụng DataAdapter.Fill(). Toàn bộ điểm của một DataReader là thực sự, thực sự nhanh chóng và đáp ứng cho đọc. Kiểm tra một số cờ boolean một lần mỗi hàng sẽ không thêm một sự khác biệt có thể đo lường trong thời gian ngay cả trên hàng triệu hàng.

Yếu tố hạn chế cho truy vấn cơ sở dữ liệu lớn không phải là thời gian xử lý CPU cục bộ mà kích thước của đường ống I/O (kết nối mạng của bạn cho cơ sở dữ liệu từ xa hoặc tốc độ đĩa của bạn cho một địa phương) hoặc kết hợp tốc độ đĩa của máy chủ db và thời gian xử lý CPU cho một truy vấn phức tạp. Cả DataAdapter và DataReader sẽ dành nhiều thời gian (có lẽ phần lớn thời gian) chỉ đợi vài nano giây tại một thời điểm cho hàng tiếp theo được phục vụ.

Một tiện ích của DataAdapter.Fill() là nó thực hiện phép thuật tự động tạo cột DataTable để phù hợp với kết quả truy vấn, nhưng điều đó không khó thực hiện (xem SqlDataReader.GetSchemaTable()).

+0

Vâng, đó chắc chắn là một phần đã được đưa ra đã được đảm bảo tất cả mọi thứ đã được định dạng đúng khi tôi đặt nó vào datatable. Tôi đã học được TON từ bài đăng này. –

+0

Dường như không thể chỉnh sửa các bình luận sau một thời hạn nhất định. Tôi sẽ ngồi trên bài viết này và suy nghĩ về nó một lúc. Nếu tôi có thêm bất kỳ câu hỏi nào tôi sẽ hỏi. Nếu không, tôi sẽ đánh dấu nó là đã trả lời. Cảm ơn bạn! –

0

Chỉ cần một thử

tôi sẽ đề nghị bạn để đặt một tốn thời gian truy vấn trong một BackgroundWorker và vượt qua lệnh với nó. để bạn có thể giữ đối tượng lệnh trong tầm kiểm soát. Khi hủy bỏ lệnh xuất phát, chỉ cần nói qua (để BackgroundWorker mà cơ bản dở dang) lệnh để Hủy bỏ bởi command.Cancel()

Các vấn đề liên quan