2011-11-10 2 views
2

주어진 사용자가 스크립트 질문에 대한 답변을 저장하는 디자인이 있습니다. 하나의 스크립트에는 많은 질문이있을 수 있으며 각 질문에는 주어진 사용자가 여러 번 대답 할 수 있습니다.이 Linq를 더 EF로 만들 수 있습니까?

는 MS SQL 테이블은 더 많거나 적은처럼 (추가 세부 사항을 제거) 볼 :

-Scripts 
ScriptId int (PK, identity) 

-ScriptQuestions 
ScriptId int (PK) 
QuestionId int (PK) 

-Questions 
QuestionId int (PK, identity) 
QuestionText varchar 

-Answers 
AnswerId int (PK, identity) 
QuestionId int 
UserId int 
AnswerText varchar 

내가 제공하는 모든 질문과 마지막 답을 주어진 스크립트와 특정 사용자를 위해이 데이터베이스를 쿼리하고 얻을 싶습니다 각 질문마다 (있는 경우). 는 T-SQL에서 나는 이런 식으로 뭔가 할 것 :

SELECT 
    sq.QuestionId, 
    q.QuestionText, 
    la.AnswerText 
FROM  
    ScriptQuestions sq 
     ON s.ScriptId = sq.ScriptId 
    INNER JOIN Questions q 
     ON sq.QuestionId = q.QuestionId 
    LEFT OUTER JOIN (
      SELECT 
       QuestionId, 
       AnswerText 
      FROM Answers 
      WHERE AnswerId IN (
         SELECT 
          MAX(AnswerId) LastAnswerId 
         FROM Answers 
         WHERE UserId = @userId 
         GROUP BY QuestionId 
        ) 
      ) la 
     ON q.QuestionId = la.QuestionId 
WHERE 
    sq.ScriptId = @scriptId 

(테스트되지 않은,하지만 나는 내가 무엇을 할 것이라고 가까운 생각) 내가 MVC 3 응용 프로그램에 LinqToEF을 사용하고

하고 얻을 그 결과는 내가 사용 :

 var questions = from sq in script.ScriptQuestions 
         select new QuestionsAnswers 
            { 
             QuestionId = sq.QuestionId, 
             QuestionText = sq.Question.QuestionText, 
             LastAnswer = sq.Question.Answers 
              .Where(a => a.UserId == userId) 
              .OrderByDescending(a => a.AnswerId) 
              .Select(a => a.AnswerText) 
              .FirstOrDefault() 
            }; 

그리고 같은 결과를 얻는다하지만 내가 VS 2010에서 Intellitrace 프로파일 러를 실행하면 그 LINQ는 다음 다른 스크립트와의 모든 질문에 대한 SELECT 문을 보내기에이 변환 볼 수 있습니다 SELECT statem 모든 대답에 대한 ent. 따라서 스크립트에 20 개의 질문이있는 경우 위와 같이 SQL 문 하나만 보내는 대신 데이터베이스를 적어도 40 번 쿼리합니다.

LinqToEF 문을 작성하는 방법을 변경하려고 시도했지만 n SELECT 문을 극복하지 못했습니다.

올바른 방법 일 수 없습니까? 그렇습니까?

+0

나는이 쿼리를 함께 결합하는 편이 낫다고 생각한다. '스크립트의 sq에서 .Sqriptquestions는 스크립트에 참여합니다. a.questionid의 답변은 sq.questionid와 같습니다. 여기서 userId == a.UserId select new .. {}'. (정확하게는 아니지만 아이디어). Linq는 여분의 내부 쿼리없이 필터링합니다 (실제로 scriptQuestions에서 항목 당 한 번 실행됩니다). – Independent

답변

1

내 생각에 쿼리가 IQueryable이 아닌 script.ScriptQuestions으로 시작하기 때문에 탐색 속성의 지연로드와 함께 LINQ to Objects와 메모리의 개체를 사용합니다. 따라서 컬렉션은 메모리에서 반복되며 모든 항목에 대해 sq.Questionsq.Question.Answers 탐색 속성에 액세스합니다. 이러한 등록 정보에 액세스 할 때마다 지연로드가 활성화 된 경우 DB에 대한 새 쿼리가 발행되어 등록 정보를 채 웁니다. sq.Question.Answers 컬렉션의 필터는 전체 컬렉션의 메모리에서 수행됩니다.

당신은 그것을 다음과 같은 방법을 변경하려고 할 수 있습니다 지금은 IQueryable와 엔티티에 LINQ를 때문에

var questions = from sq in context.ScriptQuestions 
        where sq.ScriptId == script.ScriptId 
        select new QuestionsAnswers 
           { 
            QuestionId = sq.QuestionId, 
            QuestionText = sq.Question.QuestionText, 
            LastAnswer = sq.Question.Answers 
             .Where(a => a.UserId == userId) 
             .OrderByDescending(a => a.AnswerId) 
             .Select(a => a.AnswerText) 
             .FirstOrDefault() 
           }; 

이 단지 하나의 데이터베이스 쿼리해야합니다.

+0

예! 니가 끝냈어. 훨씬 더 나아졌으며 성능 향상을 위해 OUTER APPLY를 사용합니다. 고마워. – qbantek

관련 문제