簡體   English   中英

如何優化Linq查詢並更新速度值

[英]How do I Optimize Linq queries and updating values for speed

我們有一個簡單的計算引擎,它接收存儲在數據庫中的字符串(例如“((x + y)* 4”),從數據庫中提取x和y的值,執行計算並將結果保存到數據庫。 看來這花了太長時間,而且恐怕我已經步入了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的關聯表,該表具有有關會計項目值的位置信息。

非常感謝你。

我將首先分解方法並進行更詳細的分析。 找出導致性能下降的確切原因。

可能是問題不是Linq而是在數據庫中-您是否已對所有數據庫進行概要分析和優化? 您是否具有合理的索引,EF生成的SQL是否正在使用它們?

我認為您的linq查詢沒有任何明顯的錯誤。

永遠不要低估循環內部查詢的功能。 也許您最好的選擇是從另一種方法來看待它,然后將其中一些循環查詢取出。 您是否運行過任何計時器以查看耗時最長的時間? 我願意打賭,它是foreach循環中的那些LINQ查詢。

Linq JOIN操作將遍歷整個數據庫,然后將結果“合並”到ON語句中。
然后它將遍歷結果並通過WHERE語句條件進行過濾。

因此,如果 :

context.AccountingItemValues = N 
context.STARS_DistrictInputData = M

然后, join操作為您提供了一個大小為Max(M,N)(最壞情況)的結果表(讓我們想一想像SQL一樣)。

然后它將遍歷整個結果表,並使用WHERE語句過濾結果。

因此,您要遍歷整個數據庫兩次以上。 而且JOIN操作不是線性的,因此整個過程可以獲得更多的迭代。

改善

使用特定的地方條件之前加入,讓你之前減少表的大小聯接表。
那會給你

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

然后,在較小的表上執行聯接操作。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM