2012-06-20 30 views
5

Tôi có đoạn code sau đây, mà đang bị lỗi:Làm thế nào để sửa chữa siêu chậm truy vấn EF/LINQ thực hiện nhiều câu lệnh SQL

TPM_USER user = UserManager.GetUser(context, UserId); 
var tasks = (from t in user.TPM_TASK 
      where t.STAGEID > 0 && t.STAGEID != 3 && t.TPM_PROJECTVERSION.STAGEID <= 10 
      orderby t.DUEDATE, t.PROJECTID 
      select t); 

Dòng đầu tiên, UserManager.GetUser chỉ cần một tra cứu đơn giản trong cơ sở dữ liệu để có được đúng TPM_USER bản ghi. Tuy nhiên, dòng thứ hai gây ra tất cả các loại hỗn loạn SQL.

Trước hết, nó thực hiện hai câu lệnh SQL tại đây. Người đầu tiên lấy mỗi hàng đơn trong TPM_TASK được liên kết với người dùng, đó là đôi khi hàng chục ngàn hàng:

SELECT 
-- Columns 
FROM TPMDBO.TPM_USERTASKS "Extent1" 
INNER JOIN TPMDBO.TPM_TASK "Extent2" ON "Extent1".TASKID = "Extent2".TASKID 
WHERE "Extent1".USERID = :EntityKeyValue1 

Truy vấn này mất khoảng 18 giây trên người sử dụng với rất nhiều công việc. Tôi mong đợi mệnh đề WHERE cũng chứa các bộ lọc STAGEID, điều này sẽ loại bỏ phần lớn các hàng.

Tiếp theo, có vẻ như để thực hiện một truy vấn mới cho mỗi TPM_PROJECTVERSION cặp trong danh sách trên:

SELECT 
-- Columns 
FROM TPMDBO.TPM_PROJECTVERSION "Extent1" 
WHERE ("Extent1".PROJECTID = :EntityKeyValue1) AND ("Extent1".VERSIONID = :EntityKeyValue2) 

Mặc dù truy vấn này là nhanh chóng, nó thực hiện hàng trăm lần nếu người dùng có nhiệm vụ trong toàn bộ một bó các dự án.

Truy vấn Tôi muốn tạo ra sẽ giống như thế:

SELECT 
-- Columns 
FROM TPMDBO.TPM_USERTASKS "Extent1" 
INNER JOIN TPMDBO.TPM_TASK "Extent2" ON "Extent1".TASKID = "Extent2".TASKID 
INNER JOIN TPMDBO.TPM_PROJECTVERSION "Extent3" ON "Extent2".PROJECTID = "Extent3".PROJECTID AND "Extent2".VERSIONID = "Extent3".VERSIONID 
WHERE "Extent1".USERID = 5 and "Extent2".STAGEID > 0 and "Extent2".STAGEID <> 3 and "Extent3".STAGEID <= 10 

Các truy vấn trên sẽ chạy trong khoảng 1 giây. Thông thường, tôi có thể chỉ định rằng JOIN bằng cách sử dụng phương thức Include. Tuy nhiên, điều này dường như không hoạt động trên các thuộc tính. Nói cách khác, tôi không thể làm:

from t in user.TPM_TASK.Include("TPM_PROJECTVERSION") 

Có cách nào để tối ưu hóa tuyên bố LINQ này không? Tôi đang sử dụng .NET4 và Oracle là DB phụ trợ.

Giải pháp:

giải pháp này được dựa trên gợi ý Kirk dưới đây, và làm việc kể từ context.TPM_USERTASK không thể được truy vấn trực tiếp:

var tasks = (from t in context.TPM_TASK.Include("TPM_PROJECTVERSION") 
      where t.TPM_USER.Any(y => y.USERID == UserId) && 
      t.STAGEID > 0 && t.STAGEID != 3 && t.TPM_PROJECTVERSION.STAGEID <= 10 
      orderby t.DUEDATE, t.PROJECTID 
      select t); 

không kết quả trong một lồng nhau SELECT chứ không phải là truy vấn TPM_USERTASK trực tiếp, nhưng nó có vẻ khá hiệu quả không-ít hơn.

Trả lời

4

Có, bạn đang kéo xuống một người dùng cụ thể và sau đó tham chiếu mối quan hệ TPM_TASK. Rằng nó được kéo xuống mỗi nhiệm vụ gắn liền với người dùng đó là chính xác những gì nó nghĩa vụ phải làm. Không có bản dịch ORM SQL nào khi bạn làm theo cách này. Bạn đang nhận được một người dùng, sau đó nhận tất cả các nhiệm vụ của mình vào bộ nhớ, và sau đó thực hiện một số lọc phía máy khách. Điều này là tất cả được thực hiện bằng cách sử dụng tải chậm, do đó, SQL sẽ có hiệu quả đặc biệt vì nó không thể thực hiện bất cứ thứ gì.

Thay vào đó, viết lại truy vấn của bạn để đi trực tiếp chống lại TPM_TASK và bộ lọc chống lại người sử dụng:

var tasks = (from t in context.TPM_TASK 
     where t.USERID == user.UserId && t.STAGEID > 0 && t.STAGEID != 3 && t.TPM_PROJECTVERSION.STAGEID <= 10 
     orderby t.DUEDATE, t.PROJECTID 
     select t); 

Lưu ý cách chúng tôi đang kiểm tra t.USERID == user.UserId.Điều này tạo ra hiệu ứng tương tự như user.TPM_TASK nhưng bây giờ tất cả việc nâng hạng nặng được thực hiện bởi cơ sở dữ liệu thay vì trong bộ nhớ.

+0

Thật không may, ý tưởng này sẽ không hoạt động. 'TPM_TASK' không có thuộc tính' USERID'. Người dùng liên quan đến các tác vụ thông qua bảng 'TPM_USERTASK', mà bạn không thể truy vấn trực tiếp vì nó được sử dụng trong mối quan hệ nhiều-nhiều. Có lẽ tôi muốn được tốt hơn ra tạo ra một cái nhìn trong cơ sở dữ liệu hoặc một cái gì đó? –

+0

Bạn chắc chắn có thể viết truy vấn để đi ngược lại 'TPM_USERTASK'. Tôi không chắc chắn chính xác lược đồ của bạn được thiết lập như thế nào, nhưng một cái gì đó dọc theo dòng của 'where t.TPM_TASK.Any (y => y.USERID == t.USER)' hoặc chỉ sử dụng 'join' để đưa vào nhiều nhiều. Bạn chắc chắn không được rối tung với quan điểm để đạt được điều này. –

+0

Được rồi, đang rối tung với ý tưởng đó .. Tôi sẽ cho bạn biết sau vài phút .. –

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