2012-04-07 30 views
6

Tôi đang làm việc trên một đoạn mã, được viết bởi đồng nghiệp, giao diện với ứng dụng CRM mà công ty chúng tôi sử dụng. Có hai truy vấn LINQ to Entities trong đoạn mã này được thực hiện nhiều lần trong ứng dụng của chúng tôi và tôi đã được yêu cầu tối ưu hóa chúng vì một trong số chúng thực sự chậm.Truy vấn LINQ to Entities mất nhiều thời gian để biên dịch, SQL chạy nhanh

Đây là các truy vấn:

Truy vấn đầu tiên, tính năng này biên dịch ngay lập tức. Nó lấy thông tin liên quan từ cơ sở dữ liệu CRM, lọc theo một danh sách các ID liên quan đưa ra bởi các ứng dụng:

from relation in context.ADRELATION 
where ((relationIds.Contains(relation.FIDADRELATION)) && (relation.FLDELETED != -1)) 
join addressTable in context.ADDRESS on relation.FIDADDRESS equals addressTable.FIDADDRESS 
    into temporaryAddressTable 
from address in temporaryAddressTable.DefaultIfEmpty() 
join mailAddressTable in context.ADDRESS on relation.FIDMAILADDRESS equals 
    mailAddressTable.FIDADDRESS into temporaryMailAddressTable 
from mailAddress in temporaryMailAddressTable.DefaultIfEmpty() 
select new { Relation = relation, Address = address, MailAddress = mailAddress }; 

Các truy vấn thứ hai, trong đó có khoảng 4-5 giây để biên dịch, và lấy thông tin về những người từ cơ sở dữ liệu (một lần nữa được lọc theo danh sách các ID):

from role in context.ROLE 
join relationTable in context.ADRELATION on role.FIDADRELATION equals relationTable.FIDADRELATION into temporaryRelationTable 
from relation in temporaryRelationTable.DefaultIfEmpty() 
join personTable in context.PERSON on role.FIDPERS equals personTable.FIDPERS into temporaryPersonTable 
from person in temporaryPersonTable.DefaultIfEmpty() 
join nationalityTable in context.TBNATION on person.FIDTBNATION equals nationalityTable.FIDTBNATION into temporaryNationalities 
from nationality in temporaryNationalities.DefaultIfEmpty() 
join titelTable in context.TBTITLE on person.FIDTBTITLE equals titelTable.FIDTBTITLE into temporaryTitles 
from title in temporaryTitles.DefaultIfEmpty() 
join suffixTable in context.TBSUFFIX on person.FIDTBSUFFIX equals suffixTable.FIDTBSUFFIX into temporarySuffixes 
from suffix in temporarySuffixes.DefaultIfEmpty() 
where ((rolIds.Contains(role.FIDROLE)) && (relation.FLDELETED != -1)) 
select new { Role = role, Person = person, relation = relation, Nationality = nationality, Title = title.FTXTBTITLE, Suffix = suffix.FTXTBSUFFIX }; 

Tôi đã thiết lập SQL Profiler và lấy SQL từ cả hai truy vấn, sau đó chạy nó trong SQL Server Management Studio. Cả hai truy vấn đều chạy rất nhanh, ngay cả với số ID lớn (~ 1000). Vì vậy, vấn đề dường như nằm trong quá trình biên dịch truy vấn LINQ.

Tôi đã thử sử dụng truy vấn đã biên dịch, nhưng vì chúng chỉ có thể chứa các tham số nguyên thủy, tôi phải loại bỏ phần đó bằng bộ lọc và áp dụng sau cuộc gọi Invoke(), vì vậy tôi không chắc chắn giúp nhiều. Ngoài ra, kể từ khi mã này chạy trong một hoạt động dịch vụ WCF, tôi không chắc chắn nếu các truy vấn được biên dịch thậm chí sẽ vẫn còn tồn tại trên các cuộc gọi tiếp theo.

Cuối cùng những gì tôi đã thử là chỉ chọn một cột trong truy vấn thứ hai. Mặc dù điều này rõ ràng sẽ không cung cấp cho tôi thông tin tôi cần, nhưng tôi nghĩ nó sẽ nhanh hơn ~ 200 cột mà chúng tôi đang chọn bây giờ. Không có trường hợp như vậy, nó vẫn mất 4-5 giây.

Tôi không phải là chuyên gia LINQ, vì vậy tôi hầu như không thể theo mã này (tôi có cảm giác nó không được viết tối ưu, nhưng không thể đặt ngón tay của tôi lên đó). Bất cứ ai có thể cho tôi một gợi ý là tại sao vấn đề này có thể xảy ra?

Giải pháp duy nhất tôi còn lại là chọn thủ công tất cả thông tin thay vì tham gia tất cả các bảng này. Sau đó tôi sẽ kết thúc với khoảng 5-6 truy vấn. Không quá tệ, tôi đoán, nhưng vì tôi không giao dịch với SQL không hiệu quả khủng khiếp ở đây (hoặc ít nhất là một mức độ không thể chấp nhận được), tôi đã hy vọng để ngăn chặn điều đó.

Cảm ơn trước, hy vọng tôi đã làm mọi thứ rõ ràng. Nếu không, vui lòng hỏi và tôi sẽ cung cấp thêm chi tiết.


Edit: tôi đã kết thúc thêm các hiệp hội về khuôn khổ thực thể của tôi (cơ sở dữ liệu mục tiêu không có phím nước ngoài quy định) và viết lại các truy vấn thusly:

context.ROLE.Where(role => rolIds.Contains(role.FIDROLE) && role.Relation.FLDELETED != -1) 
      .Select(role => new 
          { 
           ContactId = role.FIDROLE, 
           Person = role.Person, 
           Nationality = role.Person.Nationality.FTXTBNATION, 
           Title = role.Person.Title.FTXTBTITLE, 
           Suffix = role.Person.Suffix.FTXTBSUFFIX 
          }); 

vẻ hơn rất nhiều có thể đọc được và nó cũng nhanh hơn.

Cảm ơn bạn đã đề xuất, tôi chắc chắn sẽ giữ nguyên việc tạo nhiều truy vấn được biên dịch cho số lượng đối số khác nhau!

Trả lời

1

Câu trả lời của Gabriels là chính xác: Sử dụng truy vấn được biên dịch.

Dường như bạn đang biên dịch lại một lần nữa cho mọi yêu cầu WCF mà tất nhiên đánh bại mục đích khởi tạo một lần. Thay vào đó, hãy đặt truy vấn được biên dịch vào một trường tĩnh.

Chỉnh sửa:

Thực hiện việc này: Gửi tải tối đa cho dịch vụ của bạn và tạm dừng trình gỡ lỗi 10 lần. Nhìn vào ngăn xếp cuộc gọi. Nó có dừng lại thường xuyên hơn trong mã L2S hay trong mã ADO.NET không? Điều này sẽ cho bạn biết nếu vấn đề vẫn còn với L2S hoặc với SQL Server.

Tiếp theo, hãy sửa bộ lọc. Chúng ta cần đẩy nó trở lại truy vấn được biên dịch. Đây là chỉ có thể bằng cách chuyển đổi này:

rolIds.Contains(role.FIDROLE) 

này:

role.FIDROLE == rolIds_0 || role.FIDROLE == rolIds_1 || ... 

Bạn cần một truy vấn biên soạn mới cho mỗi cardinality của rolIds. Điều này là khó chịu, nhưng nó là cần thiết để có được nó để biên dịch. Trong dự án của tôi, tôi đã tự động thực hiện tác vụ này nhưng bạn có thể thực hiện giải pháp một lần ở đây.

Tôi đoán hầu hết các truy vấn sẽ có rất ít vai trò-id để bạn có thể thực hiện 10 truy vấn được biên dịch cho các thẻ hồng ngoại 1-10 và nếu cardinality vượt quá 10 bạn quay trở lại lọc phía máy khách.

+0

Như tôi đã nói về câu trả lời của Gabriel GM, tôi đã đặt chúng trong một trường tĩnh. Tôi sẽ thử lại chỉ để chắc chắn 110%, tôi đoán vậy. Điều đó sẽ làm việc mặc dù, vì tôi sẽ chỉ có thể đặt một phần lên đến mệnh đề where trong truy vấn được biên dịch? –

+0

Tôi đã thêm nhiều nội dung khác. – usr

+1

Ồ, ý tưởng tuyệt vời về điều bản thân! Bạn nói đúng, hầu hết các yêu cầu sẽ có ít hơn 10 id vai trò. Chắc chắn một cái gì đó để xem xét. Cảm ơn! –

0

Nếu bạn quyết định giữ truy vấn bên trong mã, bạn có thể biên dịch nó. Bạn vẫn phải biên dịch truy vấn một lần khi bạn chạy ứng dụng của mình, nhưng tất cả cuộc gọi tiếp theo sẽ sử dụng truy vấn đã được biên dịch đó. Bạn có thể xem trợ giúp MSDN tại đây: http://msdn.microsoft.com/en-us/library/bb399335.aspx.

Một tùy chọn khác là sử dụng quy trình được lưu trữ và gọi thủ tục từ mã của bạn. Do đó không có thời gian biên dịch.

+0

Tôi đã cố gắng sử dụng truy vấn được biên dịch, nhưng vẫn mất 4 giây mỗi khi tôi gọi hoạt động dịch vụ. Tôi có cảm giác nó không được giữ giữa các yêu cầu (tôi đã làm cho nó tĩnh). Tôi cũng không thể sử dụng thủ tục được lưu trữ, vì công ty tạo gói CRM sẽ ngừng hỗ trợ nếu chúng tôi chạm vào cơ sở dữ liệu của họ ngoài cách đọc:/ Có thể giúp tôi viết lại truy vấn để sử dụng thuộc tính điều hướng thay vì tham gia? –

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