2011-02-16 6 views
0

데이터베이스에 저장된 문자열 (예 : "(x + y) * 4")을 가져 와서 x 값을 가져 오는 간단한 계산 엔진이 있습니다. y를 데이터베이스에서 가져 와서 계산을 수행하고 그 결과를 데이터베이스에 저장합니다. 이것이 너무 오래 걸리는 것 같아요. 나는 Linq 함정에 들어갔을 까봐 두렵습니다. 이 문제를 개선 할 수있는 방법이 있으면 알려주십시오.Linq 쿼리를 최적화하고 속도 값을 업데이트하는 방법

public Nullable<decimal> CalculateFormulaByYearDistrict(int formulaId, int fundingYearId, int districtId) 
     { 
      string formulaText = ""; 
      decimal? retValue = null;  

      using (STARSEntities context = new STARSEntities()) 
      { 

       var formulaItems = from fi in context.STARS_FormulaItem 
            where fi.FormulaId == formulaId 
            select fi; 

       STARS_Formula formula = formulaItems.FirstOrDefault().STARS_Formula; 
       formulaText = formula.FormulaText; 

       foreach (STARS_FormulaItem formulaItem in formulaItems) 
       { 
        int accountingItemId = formulaItem.AccountingItemId; 

        var itemValue = (from iv in context.AccountingItemValues 
            join di in context.STARS_DistrictInputData 
            on iv.DomainSpecificId equals di.DistrictInputDataId 
            where (di.DistrictId == districtId || di.DistrictId == -1) //District -1 is the invalid and universal district for coefficients 
            && di.DomainYearReportingPeriod.FundingYearId == fundingYearId 
            && iv.AccountingItemId == accountingItemId 
            select iv).SingleOrDefault(); 
        //If no value exists for the requested Assessment Item Value, then force an error message into the formula text 
        //to be thrown during calculate. 
        if (itemValue != null) 
         formulaText = Regex.Replace(formulaText, @"\b" + formulaItem.ItemCode + @"\b", itemValue.Amount.ToString()); 
        else 
         formulaText = Regex.Replace(formulaText, @"\b" + formulaItem.ItemCode + @"\b", "No Value Exists for " + formulaItem.ItemCode); 
       } 
       switch (formula.FormulaTypeId) 
       { 
        case (int)FormulaType.CALC: 
         retValue = Calculate(formulaText); 
         break; 
        case (int)FormulaType.EXPONENT: 
         // pull the number directly after e and and calculate the Math.Exp(value) and then push that into the calculation. 
         retValue = Calculate(ReplaceExponent(formulaText)); 
         break; 
        case (int)FormulaType.IFTHEN: 
         // evaluate the If statements and pass any math to the calculator. 
         retValue = Calculate(EvaluateIf(formulaText)); 
         break; 
        default: 
         break; 
       } 
      }    
      return retValue; 
     } 

public bool CalculateAndSaveResults(DistrictDataCategory category, List<int> districtIds, int fundingYearId, int userId) 
     { 
      //Optimization Logic 
      DateTime startTime = DateTime.Now; 
      Debug.WriteLine("Starting Calculate and Save at:" + startTime.ToString()); 

      using (STARSEntities context = new STARSEntities()) 
      { 

       var formulas = from f in context.STARS_FormulaCategory 
           where f.DistrictDataCategoryId == (int)category 
           select f.STARS_Formula; 

       foreach (var districtId in districtIds) 
       { 
        Debug.WriteLine("District: " + districtId.ToString()); 
        DateTime districtStartTime = DateTime.Now; 

        foreach (var formula in formulas) 
        { 
         var itemValue = (from iv in context.AccountingItemValues 
             join di in context.STARS_DistrictInputData 
             on iv.DomainSpecificId equals di.DistrictInputDataId 
             where (di.DistrictId == districtId) 
             && di.DomainYearReportingPeriod.FundingYearId == fundingYearId 
             && iv.AccountingItemId == formula.ResultAccountingItemId 
             select new { iv, di }).SingleOrDefault(); 

         itemValue.iv.Amount = CalculateFormulaByYearDistrict(formula.FormulaId, fundingYearId, districtId); 

         //Update Actual Amount Record 
         itemValue.iv.LastUpdated = DateTime.Now; 
         itemValue.iv.UpdatedBy = userId; 

         //Update District Data Import Record 
         itemValue.di.LastUpdated = DateTime.Now; 
         itemValue.di.UpdatedBy = userId; 
        } 
        Debug.WriteLine("District Calculation took: " + ((TimeSpan)(DateTime.Now - districtStartTime)).ToString() + "for " + districtId.ToString()); 
       } 

       context.SaveChanges(); 
      } 
      Debug.WriteLine("Finished Calculate and Save at:" + ((TimeSpan)(DateTime.Now - startTime)).ToString()); 
      return true; 
     } 

기본 데이터 구조에 대한 정보가 필요하면 알려주십시오. 중요하게 보이는 점은 주어진 지구의 특정 유형에 대한 모든 계산을 수행 할 수 있도록 수식 텍스트를 저장하는 수식 표 사이에 연관 개체가 있다는 것입니다. 저장된 실제 값은 AccountingItemValue 테이블에 있지만 회계 항목 값에 대한 위치 정보가있는 DistrictInputData라는 관련 테이블이 있습니다.

대단히 감사합니다.

+0

http://en.wikipedia.org/wiki/Inner-platform_effect – asawyer

답변

1

나는 더 세분화 된 수준에서 방법과 프로파일 링을 분해함으로써 시작할 것입니다; 성능이 저하되는 원인을 정확히 찾아 내십시오.

문제는 Linq가 아니지만 데이터베이스에있을 수 있습니다. 데이터베이스를 프로파일 링하고 최적화 한 적이 있습니까? 당신은 합리적인 색인을 가지고 있으며 그들은 EF가 생성하는 SQL에 의해 사용되고 있습니까?

나는 당신의 linq 쿼리에 분명히 잘못된 것이 아무것도 보이지 않습니다.

+0

DB 조정 권고자는 일부 색인을 제안했으며 일부 통계 및 성능은 약 50 % 증가했습니다. – Noel

+0

이것은 현재의 문제점을 해결하기 위해 DB 최적화의 힘을 과소 평가했습니다. – Noel

+0

처리해야했던 성능 문제의 98 %가 SQL 수준 최적화였습니다. – flesh

0

루프 내부의 쿼리 성능을 절대로 과소 평가하지 마십시오. 어쩌면 최선의 방법은 다른 접근 방식에서 그것을보고 루프 된 쿼리 중 일부를 추출하는 것입니다. 타이머를 실행하여 정확히 어디에서 가장 오래 걸리는지 확인하십시오. foreach 루프 내에서 LINQ 쿼리를 사용하고 싶습니다.

0

Linq의 작업이 전체 데이터베이스를 통해 루프 것이다 가입하고 ON 문 에 결과를 "병합".
그런 다음 결과를 반복하고 WHERE 문 조건으로 필터링합니다.

그래서 경우 :

context.AccountingItemValues = N 
context.STARS_DistrictInputData = M 

그런 다음 작업이 당신에게 결과 (잠시 SQL처럼 생각 할 수 있습니다) 크기 최대 (M, N) (최악의 경우)의 테이블을 제공합니다 가입 할 수 있습니다.

그러면 전체 결과 테이블을 실행하고 WHERE 문을 사용하여 결과를 필터링합니다.

그래서 전체 데이터베이스를 두 번 이상 반복합니다. 그리고 JOIN 연산은 선형이 아니므로 모든 것을 반복 할 수 있습니다.

향상 :

사용 특정 테이블을 어디에 조건 전에 당신이 가입하기 전에 테이블의 크기를 줄일 수 있도록 가입 할 수 있습니다.당신에게

context.AccountingItemValues = numberOf(accountingItemId) 
context.STARS_DistrictInputData = numberOf(fundingYearId) 

을 줄 것이다
그런 다음이 작업이 훨씬 작은 테이블에서 수행 조인.

+0

WHERE 절을 조인 전에 테이블에 적용하도록 변경했으며 몇 백 번의 반복을 통해 성능에 어떤 차이도주지 않았습니다 : 00 : 00 :했다 00.3180318 화학식 검색 및 계산 후 : 00 : 00 : 2 화학식 검색 및 계산 전에 걸렸 00.3490349 그것은 하나 화학식 성능이 저하한다는 표시를하지만 통상 이내 내가 보았던 변이. DB 최적화를 살펴 봅니다. – Noel

관련 문제