Правильный способ сделать вычисления в SQL - PullRequest
1 голос
/ 20 июня 2011
Baseline Table

ProjectId   ProjectName    Forecast
---------   -----------    --------
  11258      Test Proj1      0.678 
  11259      Test Proj2      2.57 

FundEntity Baseline Table

ProjectId    FundEntityId    Forecast    ForecastDollars
---------    -----------     --------    ---------------
  11258          5             0.226
  11258          8             0.226
  11258          11            0.226 

У меня есть хранимая процедура, которая проходит по каждой записи в таблице ProjectSummaryEntity (хм!) И для каждой разбивает прогноз на основе «распределений», установленных в таблице FundEntity. Затем мне нужно рассчитать сумму прогноза в долларах на основе определенного курса в таблице «Курс». Ставка также определяется FundEntity.

Хранимая процедура работает немного медленно (14 с для строк 6,6 КБ), и я не удивлен из-за неэффективности кода. У меня вопрос, как мне делать вычисления "на лету" без использования курсоров?

Причина, по которой я использую курсоры, заключается в том, что:

  • прогнозная сумма разбивается первой (из ProjectSummary), сохраняется в переменную
  • скорость определяется, сохраняется в переменную
  • определяется сумма в долларах, сохраняется в переменную
  • выполняется финальное обновление, чтобы обновить прогноз и прогноз долларов

Если я не использую курсоры, нет способа обновить доллары на основе прогноза. Любая помощь приветствуется. Спасибо!

РЕДАКТИРОВАТЬ: Выше очень упрощенная версия всего этого. Хранимая процедура, которая имеет дело с этим, находится в: http://pastebin.ubuntu.com/629888/

1 Ответ

1 голос
/ 05 июля 2011

Обратите внимание, что хранимая процедура будет работать медленно не из-за вашего курсора, а из-за подзапросов, которые нужно запускать для каждой строки (11 выбирает + 1 обновление).Просто комбинируя некоторые из этих запросов, вы сможете значительно повысить производительность (например, вычисления для forHcm, budgetHcm и revPlanHcm можно легко объединить в один запрос, поскольку все они являются вычислениями для столбцов базовой таблицы).

ToОтветьте на свой конкретный вопрос о курсоре. Можно удалить курсор, поскольку вам не нужно генерировать выходные значения строк на основе выходных значений для других строк (например, если значение hcmRate для одного fundEntityBaselineId было рассчитано с использованием значений hcmRate для других значений FundEntityBaselineId, тогда было бы очень сложно избежать курсора).Последний запрос без курсора будет немного сложным.

Взяв вашу хранимую процедуру на http://pastebin.ubuntu.com/629888/ и предполагая, что если вы используете COALESCE, мне нужно OUTER JOIN (нет COALESCE, тогда INNER JOIN), здесьВот как должен выглядеть ваш отдельный запрос (чтобы исправить разделы, помеченные как «рефакторинг», мне нужно больше узнать о ваших таблицах):

SELECT fblId,
       ForecastAmount * fundAlloc AS foreHCM
       hcmRate,
       RE_Value * fundAlloc AS reqForeHcm,
       PlanAmount * fundAlloc AS budgetHcm
       RevisedPlanAmount * fundAlloc AS revPlanHcm,
       actualForHcm_pre * fundAlloc AS actualForeCastHcm
FROM
(SELECT b.FundEntityBaselineId AS fblId,
        COALESCE(fa.fundAllocation,100)/100 as fundAlloc,
        COALESCE(hcmRate1.Usd_Rate,hcmRate2.HCMRate) as hcmRate,
        bl.ForecastAmount,
        COALESCE(re.Value,0) as RE_Value,
        planHcmRate.BudgetHcmRate as planHcmRate,
        bl.PlanAmount,
        bl.RevisedPlanAmount,
        COALESCE(afp_p.actualForHcm_pre,0) as actualForHcm_pre
        FROM FundEntityBaseline b
          INNER JOIN ProjectDetail pd ON pd.ProjectId = b.fblProjectId
          INNER JOIN Baseline bl on bl.BaselineId = b.BaselineId
          INNER JOIN ProjectTeam pt ON bl.ProjectTeamId = pt.ProjectTeamId
        INNER JOIN SiteTeam st ON pt.SiteTeamId = st.SiteTeamId
        INNER JOIN TeamRole tr ON st.TeamRoleId = tr.RoleId
        INNER JOIN Team t ON tr.TeamId = t.TeamId
          INNER JOIN FundSource fs on fs.FundSourceId = pd.FundSourceId
          INNER JOIN (<* refactor*> SELECT r.BudgetHcmRate
                           FROM Rate r WHERE r.RateName = (SELECT st.RateName
                                                           FROM SiteTeam st INNER JOIN ProjectTeam pt 
                                                                                ON st.SiteTeamId = pt.SiteTeamId
                                                                            INNER JOIN Baseline bl 
                                                                                ON bl.ProjectTeamId = pt.ProjectTeamId
                                                           WHERE bl.BaselineId = fblBaselineId
                                                           FETCH FIRST 1 ROW ONLY)
                           ORDER BY r.EffectiveDate
                           FETCH FIRST 1 ROW ONLY) as planHcmRate,
          LEFT OUTER JOIN (SELECT er.Usd_Rate
                            FROM FundEntity fe
                                INNER JOIN EntityCustomerRate er
                                    ON fe.FundAux1 = er.EntityCustomerRateId) AS hcmRate1 
                                    ON hcmRate1.FundEntityId = b.FundEntityId AND (fs.FundingType IN ('Direct Bill-Single','Direct Bill-Multiple')) AND t.teamType = 'ITDEVMO'
          LEFT OUTER JOIN (<* refactor*> SELECT r.HCMRate
                             FROM Rate r
                                INNER JOIN SiteTeam st ON r.RateName = st.RateName
                                INNER JOIN ProjectTeam pt ON st.SiteTeamId = pt.SiteTeamId
                             WHERE pt.ProjectTeamId = (SELECT bl.ProjectTeamId
                                                       FROM Baseline bl
                                                       WHERE bl.BaselineId = b.BaselineId)
                             ORDER BY r.EffectiveDate DESC
                             FETCH FIRST 1 ROW ONLY) as hcmRate2 ON (fs.FundingType NOT IN ('Direct Bill-Single','Direct Bill-Multiple') OR t.teamType != 'ITDEVMO')
          LEFT OUTER JOIN FundAllocation fa on fa.FundSourceId = pd.FundSourceId AND fa.FundEntityId = b.fblFundEntity
          LEFT OUTER JOIN Request re on bl.ProjectMonth = re.ProjectMonth AND bl.ProjectTeamId = re.ProjectTeamId AND re.BaselineType = 'Reforecast'
          LEFT OUTER JOIN (<*refactor *>SELECT (((SELECT SUM(bl.ForecastAmount) 
                              FROM Baseline bl 
                              WHERE DATE(SUBSTR(bl.ProjectMonth, 5) || '-' || 
                                        (CASE SUBSTR(bl.ProjectMonth, 1, 3)
                                             WHEN 'Jan' THEN '01'
                                             WHEN 'Feb' THEN '02'
                                             WHEN 'Mar' THEN '03'
                                             WHEN 'Apr' THEN '04'
                                             WHEN 'May' THEN '05'
                                             WHEN 'Jun' THEN '06'
                                             WHEN 'Jul' THEN '07'
                                             WHEN 'Aug' THEN '08'
                                             WHEN 'Sep' THEN '09'
                                             WHEN 'Oct' THEN '10'
                                             WHEN 'Nov' THEN '11'
                                             WHEN 'Dec' THEN '12'
                                         END) || '-01') > DATE((SELECT CurrentMonth FROM Pmo)) AND
                                    bl.ProjectTeamId = (SELECT ProjectTeamId FROM Baseline WHERE BaselineId = fblBaselineId)) 
                              +
                             (SELECT SUM(bl.HcmActualAmount) 
                              FROM Baseline bl 
                              WHERE DATE(SUBSTR(bl.ProjectMonth, 5) || '-' || 
                                        (CASE SUBSTR(bl.ProjectMonth, 1, 3)
                                             WHEN 'Jan' THEN '01'
                                             WHEN 'Feb' THEN '02'
                                             WHEN 'Mar' THEN '03'
                                             WHEN 'Apr' THEN '04'
                                             WHEN 'May' THEN '05'
                                             WHEN 'Jun' THEN '06'
                                             WHEN 'Jul' THEN '07'
                                             WHEN 'Aug' THEN '08'
                                             WHEN 'Sep' THEN '09'
                                             WHEN 'Oct' THEN '10'
                                             WHEN 'Nov' THEN '11'
                                             WHEN 'Dec' THEN '12'
                                         END) || '-01') <= DATE((SELECT CurrentMonth FROM Pmo)) AND
                                    bl.ProjectTeamId = (SELECT ProjectTeamId FROM Baseline WHERE BaselineId = fblBaselineId))) as actualForeHcm_pre) AS afh_p
        WHERE ProjectId = projId) as T;

Дайте мне знать, если это полезно, или вам нужно больше объяснений.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...