2011-02-16 46 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 2011-02-16 20:36:27

回答

1

我會從更細化的層面分析方法和分析入手;找出導致性能下降的原因。

這可能是問題不是Linq,但在數據庫中 - 你有沒有分析和優化你的數據庫?您是否有明智的索引,並且正在使用EF生成的SQL?

我沒有看到你的linq查詢有什麼明顯的錯誤。

+0

DB調整顧問提出了一些指標,一些統計數據和性能提高了約50%。 – Noel 2011-02-16 23:28:15

+0

這對我目前的問題有效,我低估了數據庫優化的力量。 – Noel 2011-02-17 16:28:10

0

永遠不要低估循環內查詢的能力。也許你最好的選擇是從另一種方法看待它,並將一些循環查詢抽出。你是否運行任何計時器來查看它究竟花了多長時間?我願意打賭它是在foreach循環中的那些LINQ查詢。

0

LINQ的JOIN操作將遍歷整個數據庫,然後「合併」對結果ON聲明。
然後它將遍歷結果並通過WHERE語句條件篩選。

所以,如果:

context.AccountingItemValues = N 
context.STARS_DistrictInputData = M 

然後加入操作給你一個結果(讓覺得像SQL片刻)尺寸最大(M,N)(最壞情況)的表。

然後它會遍歷整個結果表,並用WHERE語句過濾結果。

所以你在整個數據庫中循環了兩遍以上。 JOIN操作不是線性的,所以你可以獲得更多的迭代。

提高

使用特定表,其中條件前的加盟,讓您在連接前減少表的大小。
這會給你

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

然後加入操作上要小得多表上完成。