2015-10-20 62 views
0

我正在開發項目來管理現場項目關鍵績效指標(KPI)的提交。每份提交文件都附有績效評估標準(PMs),這些都是在現場提交時提交的。需要幫助減少非常大的MySQL查詢

PM根據完成程度由用戶給出分數。我現在正在製作一份使用所有這些數據的報告。我有一個可行的查詢,但它非常龐大和繁瑣。我試圖找到一種簡化它的方法。

我有一個查詢生成KPI列表。有了這個清單,我就可以應用一些子查詢和簡單的數學運算來產生那個時期的「分數」。

有13個時期。分數是根據當年完成提交的業績衡量標準計算得出的。有兩種類型的提交「面板」和「程序」。

需要爲每個期間的兩種提交類型計算得分。然後取這些分數的平均值並計算最終分數。

最終得分稱爲疼痛/增益指標。我們計算得分,除以100,然後乘以0.3。如果數字小於1,那麼我們從疼痛/增益中分別減去1,如果數字大於0,我們加1。

週期1是不同的,因爲我們只需要計算程序提交,見下圖:

週期然而1

(
    SELECT 
    ROUND(AVG(sp.progress), 2) 
    FROM `submissions` AS sub 
    LEFT JOIN `sub_performancemeasures` sp ON sp.sub_id = sub.id 
    LEFT JOIN `performance_measures` pm ON pm.id = sp.pm_id 
    WHERE sub.programme = 'programme' 
    AND sub.submission_year = '2015/2016' //current year 
    AND sub.state = '2' //state 2 is complete 
    AND sub.period = '1' 
    AND pm.kpi_id = kpi.id 
) AS p1 

其他時段內需要根據雙方提交類型比分:

PERIOD 2 - 12

CASE 
WHEN 

( 
     IFNULL((
       SELECT 
       ROUND(AVG(sp.progress), 2) 
       FROM `submissions` AS sub 
       LEFT JOIN `sub_performancemeasures` sp ON sp.sub_id = sub.id 
       LEFT JOIN `performance_measures` pm ON pm.id = sp.pm_id 
       WHERE sub.period = '2' 
       AND sub.submission_year = '2015/2016' 
       AND sub.state = '2' 
       AND sub.panel = 'panel' 
       AND pm.kpi_id = kpi.id 
      ), 0) + 
     IFNULL((
       SELECT 
       ROUND(AVG(sp.progress), 2) 
       FROM `submissions` AS sub 
       LEFT JOIN `sub_performancemeasures` sp ON sp.sub_id = sub.id 
       LEFT JOIN `performance_measures` pm ON pm.id = sp.pm_id 
       WHERE sub.programme = 'programme' 
       AND sub.submission_year = '2015/2016' 
       AND sub.state = '2' 
       AND sub.period = '2' 
       AND pm.kpi_id = kpi.id 
      ), 0)/2)/100 * 0.3 < 0 THEN 

     1- ROUND(( 
       IFNULL((
         SELECT 
         ROUND(AVG(sp.progress), 2) 
         FROM `submissions` AS sub 
         LEFT JOIN `sub_performancemeasures` sp ON sp.sub_id = sub.id 
         LEFT JOIN `performance_measures` pm ON pm.id = sp.pm_id 
         WHERE sub.period = '2' 
         AND sub.submission_year = '2015/2016' 
         AND sub.state = '2' 
         AND sub.panel = 'panel' 
         AND pm.kpi_id = kpi.id 
        ), 0) + 
       IFNULL((
         SELECT 
         ROUND(AVG(sp.progress), 2) 
         FROM `submissions` AS sub 
         LEFT JOIN `sub_performancemeasures` sp ON sp.sub_id = sub.id 
         LEFT JOIN `performance_measures` pm ON pm.id = sp.pm_id 
         WHERE sub.programme = 'programme' 
         AND sub.submission_year = '2015/2016' 
         AND sub.state = '2' 
         AND sub.period = '2' 
         AND pm.kpi_id = kpi.id 
         ), 0)/2)/100 * 0.3, 2) 
     WHEN 

       ( 
       IFNULL((
         SELECT 
         ROUND(AVG(sp.progress), 2) 
         FROM `submissions` AS sub 
         LEFT JOIN `sub_performancemeasures` sp ON sp.sub_id = sub.id 
         LEFT JOIN `performance_measures` pm ON pm.id = sp.pm_id 
         WHERE sub.period = '2' 
         AND sub.submission_year = '2015/2016' 
         AND sub.state = '2' 
         AND sub.panel = 'panel' 
         AND pm.kpi_id = kpi.id 
         ), 0) + 
       IFNULL((
         SELECT 
         ROUND(AVG(sp.progress), 2) 
         FROM `submissions` AS sub 
         LEFT JOIN `sub_performancemeasures` sp ON sp.sub_id = sub.id 
         LEFT JOIN `performance_measures` pm ON pm.id = sp.pm_id 
         WHERE sub.programme = 'programme' 
         AND sub.submission_year = '2015/2016' 
         AND sub.state = '2' 
         AND sub.period = '2' 
         AND pm.kpi_id = kpi.id 
         ), 0)/2)/100 * 0.3 > 0 THEN 

     1+ ROUND(( 
       IFNULL((
         SELECT 
         ROUND(AVG(sp.progress), 2) 
         FROM `submissions` AS sub 
         LEFT JOIN `sub_performancemeasures` sp ON sp.sub_id = sub.id 
         LEFT JOIN `performance_measures` pm ON pm.id = sp.pm_id 
         WHERE sub.period = '2' 
         AND sub.submission_year = '2015/2016' 
         AND sub.state = '2' 
         AND sub.panel = 'panel' 
         AND pm.kpi_id = kpi.id 
        ), 0) + 
       IFNULL((
         SELECT 
         ROUND(AVG(sp.progress), 2) 
         FROM `submissions` AS sub 
         LEFT JOIN `sub_performancemeasures` sp ON sp.sub_id = sub.id 
         LEFT JOIN `performance_measures` pm ON pm.id = sp.pm_id 
         WHERE sub.programme = 'programme' 
         AND sub.submission_year = '2015/2016' 
         AND sub.state = '2' 
         AND sub.period = '2' 
         AND pm.kpi_id = kpi.id 
        ), 0)/2)/100 * 0.3, 2) 
     END 
END AS p2 

此,目前正在重複進行的剩餘週期。我得到的數據很好,但查詢是巨大的和醜陋的。最重要的是,我希望在結果的末尾有一列爲所有13個時段的每個KPI創建平均分數。我知道我無法引用由其別名創建的列,所以我現在必須重複子查詢以獲得此分數。

無論如何,我可以使這個查詢更有效率或循環週期2-13?

的結果完整查詢低於:

完整的查詢UP TO週期2

 SELECT 
     kra.id AS id, 
     kra.kra_name AS name, 
     kra.panel_weighting AS panel_weighting, 
     kra.programme_weighting AS programme_weighting, 
     kpi.id AS id, 
     kpi.kpi_name AS name, 
     kpi.panel_weighting AS panel_weighting, 
     kpi.programme_weighting AS programme_weighting, 
     (
      SELECT 
      ROUND(AVG(sp.progress), 2) 
      FROM `submissions` AS sub 
      LEFT JOIN `sub_performancemeasures` sp ON sp.sub_id = sub.id 
      LEFT JOIN `performance_measures` pm ON pm.id = sp.pm_id 
      WHERE sub.programme = 'programme' 
      AND sub.submission_year = '2015/2016' 
      AND sub.state = '2' 
      AND sub.period = '1' 
      AND pm.kpi_id = kpi.id 
    ) AS p1, 
    CASE 
     WHEN (
       SELECT 
       ROUND(AVG(sp.progress), 2) 
       FROM `submissions` AS sub 
       LEFT JOIN `sub_performancemeasures` sp ON sp.sub_id = sub.id 
       LEFT JOIN `performance_measures` pm ON pm.id = sp.pm_id 
       WHERE sub.period = '2' 
       AND sub.submission_year = '2015/2016' 
       AND sub.state = '2' 
       AND sub.panel = 'panel' 
       AND pm.kpi_id = kpi.id 
    ) IS NULL 
     AND 
     (
      SELECT 
      ROUND(AVG(sp.progress), 2) 
      FROM `submissions` AS sub 
      LEFT JOIN `sub_performancemeasures` sp ON sp.sub_id = sub.id 
      LEFT JOIN `performance_measures` pm ON pm.id = sp.pm_id 
      WHERE sub.programme = 'programme' 
      AND sub.submission_year = '2015/2016' 
      AND sub.state = '2' 
      AND sub.period = '2' 
      AND pm.kpi_id = kpi.id 
      ) IS NULL 
      THEN NULL 
    ELSE 

    CASE 
    WHEN 

      ( 
      IFNULL((
       SELECT 
       ROUND(AVG(sp.progress), 2) 
       FROM `submissions` AS sub 
       LEFT JOIN `sub_performancemeasures` sp ON sp.sub_id = sub.id 
       LEFT JOIN `performance_measures` pm ON pm.id = sp.pm_id 
       WHERE sub.period = '2' 
       AND sub.submission_year = '2015/2016' 
       AND sub.state = '2' 
       AND sub.panel = 'panel' 
       AND pm.kpi_id = kpi.id 
     ), 0) + 
     IFNULL((
      SELECT 
      ROUND(AVG(sp.progress), 2) 
      FROM `submissions` AS sub 
      LEFT JOIN `sub_performancemeasures` sp ON sp.sub_id = sub.id 
      LEFT JOIN `performance_measures` pm ON pm.id = sp.pm_id 
      WHERE sub.programme = 'programme' 
      AND sub.submission_year = '2015/2016' 
      AND sub.state = '2' 
      AND sub.period = '2' 
      AND pm.kpi_id = kpi.id 
     ), 0)/2)/100 * 0.3 < 0 THEN 

     1- ROUND(( 
     IFNULL((
      SELECT 
      ROUND(AVG(sp.progress), 2) 
      FROM `submissions` AS sub 
      LEFT JOIN `sub_performancemeasures` sp ON sp.sub_id = sub.id 
      LEFT JOIN `performance_measures` pm ON pm.id = sp.pm_id 
      WHERE sub.period = '2' 
      AND sub.submission_year = '2015/2016' 
      AND sub.state = '2' 
      AND sub.panel = 'panel' 
      AND pm.kpi_id = kpi.id 
     ), 0) + 
     IFNULL((
      SELECT 
      ROUND(AVG(sp.progress), 2) 
      FROM `submissions` AS sub 
      LEFT JOIN `sub_performancemeasures` sp ON sp.sub_id = sub.id 
      LEFT JOIN `performance_measures` pm ON pm.id = sp.pm_id 
      WHERE sub.programme = 'programme' 
      AND sub.submission_year = '2015/2016' 
      AND sub.state = '2' 
      AND sub.period = '2' 
       AND pm.kpi_id = kpi.id 
      ), 0)/2)/100 * 0.3, 2) 
    WHEN 

     ( 
      IFNULL((
       SELECT 
       ROUND(AVG(sp.progress), 2) 
       FROM `submissions` AS sub 
       LEFT JOIN `sub_performancemeasures` sp ON sp.sub_id = sub.id 
       LEFT JOIN `performance_measures` pm ON pm.id = sp.pm_id 
       WHERE sub.period = '2' 
       AND sub.submission_year = '2015/2016' 
       AND sub.state = '2' 
       AND sub.panel = 'panel' 
       AND pm.kpi_id = kpi.id 
     ), 0) + 
     IFNULL((
      SELECT 
      ROUND(AVG(sp.progress), 2) 
      FROM `submissions` AS sub 
      LEFT JOIN `sub_performancemeasures` sp ON sp.sub_id = sub.id 
      LEFT JOIN `performance_measures` pm ON pm.id = sp.pm_id 
      WHERE sub.programme = 'programme' 
      AND sub.submission_year = '2015/2016' 
      AND sub.state = '2' 
      AND sub.period = '2' 
      AND pm.kpi_id = kpi.id 
     ), 0)/2)/100 * 0.3 > 0 THEN 

     1+ ROUND(( 
     IFNULL((
      SELECT 
      ROUND(AVG(sp.progress), 2) 
      FROM `submissions` AS sub 
      LEFT JOIN `sub_performancemeasures` sp ON sp.sub_id = sub.id 
      LEFT JOIN `performance_measures` pm ON pm.id = sp.pm_id 
      WHERE sub.period = '2' 
      AND sub.submission_year = '2015/2016' 
      AND sub.state = '2' 
      AND sub.panel = 'panel' 
      AND pm.kpi_id = kpi.id 
     ), 0) + 
     IFNULL((
      SELECT 
      ROUND(AVG(sp.progress), 2) 
      FROM `submissions` AS sub 
      LEFT JOIN `sub_performancemeasures` sp ON sp.sub_id = sub.id 
      LEFT JOIN `performance_measures` pm ON pm.id = sp.pm_id 
      WHERE sub.programme = 'programme' 
      AND sub.submission_year = '2015/2016' 
      AND sub.state = '2' 
      AND sub.period = '2' 
       AND pm.kpi_id = kpi.id 
      ), 0)/2)/100 * 0.3, 2) 
    END 
    END AS p2 

    FROM `kpis` kpi 
    LEFT JOIN `key_reporting_areas` kra ON kra.id = kpi.kra_id 
    GROUP BY kpi.id 
+0

如果您喜歡,請考慮遵循以下簡單的兩步式操作:1.如果您尚未這樣做,請提供適當的CREATE和INSERT語句(和/或sqlfiddle),以便我們可以更輕鬆地複製問題。 2。如果您尚未這樣做,請提供與步驟1中提供的信息相對應的所需結果集。 – Strawberry

+0

@Strawberry,能不能不斷髮布有關DDL/Sample/SqlFiddle的信息。我在過去的10天裏見過大約十幾次。如果你不能弄清楚或者在心理上理解它,不要。 – DRapp

回答

1

由於所有潛在子查詢的基礎上,相應的「kpi.id 「,它與你的」pm.kpi_id「相關聯,現在左連接已經通過WHERE子句將所有內容都拉到了一個隱含的INNER連接中。這是你的預期嗎?

現在,如何可能簡化它。我會將每個週期/狀態的一個大查詢合併到一個「預查詢」中,然後根據ID將它連接到您的「kpi」表中,並提取最終的值。這只是一個示例,但應該是我所指的足夠好的指導。

索引,我會確保你的「意見」表上(submission_year,狀態,週期,程序),以幫助優化索引開始。

SELECT 
     kra.id AS id, 
     kra.kra_name AS name, 
     kra.panel_weighting AS panel_weighting, 
     kra.programme_weighting AS programme_weighting, 
     kpi.id AS id, 
     kpi.kpi_name AS name, 
     kpi.panel_weighting AS panel_weighting, 
     kpi.programme_weighting AS programme_weighting, 
     PreAgg.AvgProgrammePer1, 
     PreAgg.AvgPanelPer1, 
     PreAgg.AdjFactorPer1, 
     CASE when PreAgg.AdjFactorPer1 < 0 then 1 - PreAgg.AdjFactorPer1 
      when PreAgg.AdjFactorPer1 > 0 then 1 + PreAgg.AdjFactorPer1 
      END as FinalAdjPer1, 
     PreAgg.AvgProgrammePer2, 
     PreAgg.AvgPanelPer2, 
     PreAgg.AdjFactorPer2, 
     CASE when PreAgg.AdjFactorPer2 < 0 then 1 - PreAgg.AdjFactorPer2 
      when PreAgg.AdjFactorPer2 > 0 then 1 + PreAgg.AdjFactorPer2 
      END as FinalAdjPer2, 
     PreAgg.AvgProgrammePer3, 
     PreAgg.AvgPanelPer3, 
     PreAgg.AdjFactorPer3, 
     CASE when PreAgg.AdjFactorPer3 < 0 then 1 - PreAgg.AdjFactorPer3 
      when PreAgg.AdjFactorPer3 > 0 then 1 + PreAgg.AdjFactorPer3 
      END as FinalAdjPer3 
    from 
     kpis kpi 
     LEFT JOIN 
     (select 
       KPI_Period.kpi_id, 
       CASE when KPI_Period.Period = 1 
         then AvgProgramme END as AvgProgrammePer1, 
       CASE when KPI_Period.Period = 1 
         then AvgPanel END as AvgPanelPer1, 
       CASE when KPI_Period.Period = 1 
         then ((coalesce(AvgProgramme, 0) 
          + coalesce(AvgPanel, 0))/2)/100 * .3 as AdjFactorPer1, 
       CASE when KPI_Period.Period = 2 
         then AvgProgramme END as AvgProgrammePer2, 
       CASE when KPI_Period.Period = 2 
         then AvgPanel END as AvgPanelPer2, 
       CASE when KPI_Period.Period = 2 
         then ((coalesce(AvgProgramme, 0) 
          + coalesce(AvgPanel, 0))/2)/100 * .3 as AdjFactorPer2, 
       CASE when KPI_Period.Period = 3 
         then AvgProgramme END as AvgProgrammePer3, 
       CASE when KPI_Period.Period = 3 
         then AvgPanel END as AvgPanelPer3, 
       CASE when KPI_Period.Period = 3 
         then ((coalesce(AvgProgramme, 0) 
          + coalesce(AvgPanel, 0))/2)/100 * .3 as AdjFactorPer3 
       from 
       (SELECT 
         pm.kpi_id, 
         sub.period, 
         AVG(CASE when sub.programme = 'programme' then sp.progress end) as AvgProgramme, 
         AVG(CASE when sub.programme = 'panel' then sp.progress end) as AvgPanel, 
         FROM 
         submissions AS sub 
          LEFT JOIN sub_performancemeasures sp 
           ON sub.id = sp.sub_id 
           LEFT JOIN performance_measures pm 
            ON sp.pm_id = pm.id 
         WHERE 
          sub.submission_year = '2015/2016' 
         AND sub.state = '2' 
         AND sub.programme IN ('programme', 'panel') 
         GROUP BY 
         pm.kpi_id, 
         sub.period) as KPI_Period 
       group by 
       KPI_Period.kpi_id) PreAgg 

     ON kpi.id = PreAgg.kpi_id 

現在,解釋我在這裏嘗試的是什麼。最內層的查詢將直接發送到基礎表,用於根據2015/2016年,狀態= 2和程序作爲程序或面板提交進度活動。我將根據每個PERIOD的平均值進行分組。是的,你可以調整平均值的ROUNDING,但我做了平均值。所以,這個階段結束時,我會像...

KPI_ID Period AvgProgramme AvgPanel 
1  1  prog1.blah  panel1.blah 
1  2  prog2.blah  panel2.blah 
1  3  prog3.blah  panel3.blah 

隨着時間的推移休息,因此也將在記錄期間4-12(13如果四個星期的時間段VS每月週期)。此結果是「KPI_Period」別名子選擇選擇。

現在,這已經簡化了所有可能的時間爲像「年初迄今」的可能性,我建立一個交叉製表每個時期成列。現在,這部分將大大簡化您的IFNULL()條件。 COALESCE()將取值,如果爲null,則使用第二個參數作爲值來應用任何值。因此,下一級給我們帶來

CASE when KPI_Period.Period = 1 
    then AvgProgramme END as AvgProgrammePer1, 
CASE when KPI_Period.Period = 1 
    then AvgPanel END as AvgPanelPer1, 
CASE when KPI_Period.Period = 1 
    then ((coalesce(AvgProgramme, 0) 
      + coalesce(AvgPanel, 0))/2)/100 * .3 as AdjFactorPer1, 

通知的情況下/時只關心週期1(或2或3分別地)。因此,對於Period = 1,我將得到可能爲null或實際值的「prog1.blah」和「panel1.blah」的AvgProgramme值。所以它們就是它們的樣子,但是分別命名爲「AvgProgrammePer1」和「AvgPanelPer1」。

現在,你瘋狂的計算。我也通過應用COALESCE預先滾動這個計算。

( (coalesce(AvgProgramme, 0) + coalesce(AvgPanel, 0)) 
    /2)/100 * .3 as AdjFactorPer1, 

所以,如果AvgProgramme OR AvgPanel是一個空值,它將被轉換爲0進行計算。所以你將永遠不會有0失敗的分割,但0/2是0,那麼0/100 = 0 ... * .3 = 0。所以,如果兩個值都爲空,那麼你的最終調整因子爲零, +/- 1在最終組中應用。這是適用於每一個「週期」給人一種結果類似...

(abbreviated columns and sample data) 
KP AvgProg1 AvgPan1 Adj1 AvgProg2 AvgPan2 Adj2 AvgProg3 AvgPan3 Adj3 
1 prog1blah pan1blah avg1 prog2blah pan2blah avg2 prog3blah pan3blah avg3 

所以,現在,對於每個KPI_ID,你有1行的所有應用,沒有直角結果程序,面板和調整計算的因素,所以不會發生重複的會計。這是將加入您的KPI表的「PreAgg」(預彙總)最終結果。

現在,所有字段的最頂層。你可以(如我抽樣),抓住所有的列,但這裏是調整因素的簡化部分。因爲它已經在預-AGG級預先計算,我們現在可以只申請保理

PreAgg.AvgProgrammePer1, 
PreAgg.AvgPanelPer1, 
PreAgg.AdjFactorPer1, 
CASE when PreAgg.AdjFactorPer1 < 0 then 1 - PreAgg.AdjFactorPer1 
    when PreAgg.AdjFactorPer1 > 0 then 1 + PreAgg.AdjFactorPer1 
    END as FinalAdjPer1, 

您可以隨時刪除你不關心的冗餘列在最終結果中有,但至少可以讓他們確認一路上的計算。而這個查詢適用於3個完整的時間段......只需要額外的案例/當時間段爲4-12時,不需要額外的subquerying結構來敲打你的頭。