2016-02-16 54 views
0

Table Structure 在這篇文章中添加表是非常困難的,至少我不知道。嘗試使用HTML表格標籤,但他們不會顯得很好。因此將表結構張貼爲圖像。查詢優化以產生計算結果

考慮到圖像中顯示的3個表格,項目,BC,實際支出,作爲示例,我正在尋找一個返回報表作爲結果的最佳查詢。正如你所看到的,BC有一定的計算,與實際支出有

SELECT ProjectId, Name, Budget 
     , (SELECT b.[BC] FROM [BC] b 
       WHERE b.[BC] IN 
        (SELECT SUM(mx.[BC]) FROM [BC] mx 
         WHERE ProjectId=p.ProjectId)) AS 'BC' 
     , (SELECT sp.[ActualSpendAmount] FROM [ActualSpend] sp 
       WHERE sp.[DateSpent] IN 
        (SELECT MAX(as.[DateSpent]) FROM [ActualSpend] as 
         WHERE ProjectId=p.ProjectId)) AS 'Actual Spend' 

     , t.[Budget] - ((SELECT b.[BC] FROM [BC] b 
          WHERE b.[BC] IN 
           (SELECT SUM(mx.[BC]) FROM [BC] mx 
            WHERE ProjectId=p.ProjectId)) 
         + 
         (SELECT sp.[ActualSpendAmount] FROM [ActualSpend] sp 
          WHERE sp.[DateSpent] IN 
           (SELECT MAX(as.[DateSpent]) FROM [ActualSpend] as 
            WHERE ProjectId=p.ProjectId))) 

    FROM Projects p;         

正如你所看到的,對於選擇BC,與實際支出運行兩次。我還有其他幾個表,如BC,Actual Spend,可以產生一些計算結果。有沒有什麼辦法來優化這個。即使我把它們放在一個函數中,它也是一樣的,函數需要被調用多次。 有沒有辦法來優化這個查詢。


粘貼在下表結構:

項目表:

ProjectId Name Budget      
1 DeadRock 500000      
2 HardRock 300000 

BC表:實際支出表:

ProjectId BCId BC ApprovalDate  ProjectId ActualSpendId ActualSpendAmount DateSpent 
1 1 5000 2015/02/01  1 1 "  15000" " 2015/03/01" 
1 2 3000 2015/03/10  1 2 "  33000" " 2015/05/12" 
1 3 15000 2015/05/01  1 3 "  45000" " 2015/06/03" 
1 4 5000 2015/07/01  1 4 "  75000" " 2015/07/11" 
2 5 2000 2015/03/19  2 5 "  5000" " 2015/04/20" 
2 6 6000 2015/05/20  2 6 "  19000" " 2015/05/29" 
2 7 25000 2015/08/01  2 7 "  42000" " 2015/06/23" 
        2 8 "  85000" " 2015/07/15" 

報告:

ProjectId Name Budget BC Actual Spend ETC   
"1  " DeadRock 500,000 28,000 75,000 397,000 Budget-(BC+ActualSpend)  
"2  " HardRock 300,000 " 33,000" 85,000 182,000 Budget-(BC+ActualSpend)  
+0

什麼是您的DBMS?看起來像SQL Server,哪個版本? – dnoeth

回答

0

您的BC相關子查詢沒有任何意義:

, (SELECT b.[BC] FROM [BC] b 
      WHERE b.[BC] IN 
       (SELECT SUM(mx.[BC]) FROM [BC] mx 
        WHERE ProjectId=p.ProjectId)) AS 'BC' 

如果我們專注於專案編號1,你在這裏的數據:

ProjectId BCId BC  ApprovalDate 
-------------------------------------------- 
1   1  5000 2015/02/01  
1   2  3000 2015/03/10  
1   3  15000 2015/05/01  
1   4  5000 2015/07/01  

因此查詢的這個部分:

   (SELECT SUM(mx.[BC]) FROM [BC] mx 
        WHERE ProjectId=p.ProjectId) 

將返回28,000。那麼你基本上已經是:

, (SELECT b.[BC] FROM [BC] b 
      WHERE b.[BC] IN (28000)) AS 'BC' 

這將返回null,除非剛好有且只有1個在表中與BC特定量的任何項目記錄。如果有多個記錄,你會得到一個錯誤,因爲子查詢中返回了多個記錄。

我懷疑的基礎上,你只是想總和的報告數據,因此您可以簡單地使用:

SELECT p.ProjectId, 
     p.Name, 
     p.Budget 
     bc.BC 
FROM Projects p 
     LEFT JOIN 
     ( SELECT bc.ProjectID, SUM(bc.BC) AS bc 
      FROM BC 
      GROUP BY bc.ProjectID 
     ) AS bc 
      ON bc.ProjectID = P.ProjectID; 

要獲得BC的總和爲每個項目。

我在這裏做了一個假設,您正在使用基於方括號用於對象名稱的SQL Server,以及您以前的問題中的語法。在這種情況下,我會使用OUTER APPLY以獲得最新的實際支出排,給人的最終查詢:根據您的預計

SELECT p.ProjectId, 
     p.Name, 
     p.Budget 
     bc.BC, 
     sp.ActualSpendAmount AS [Actual Spend], 
     p.Budget - bc.BC + sp.ActualSpendAmount AS ETC 
FROM Projects p 
     LEFT JOIN 
     ( SELECT bc.ProjectID, SUM(bc.BC) AS bc 
      FROM BC 
      GROUP BY bc.ProjectID 
     ) AS bc 
      ON bc.ProjectID = P.ProjectID 
     OUTER APPLY 
     ( SELECT TOP 1 sp.ActualSpendAmount 
      FROM [ActualSpend] AS sp 
      WHERE sp.ProjectID = p.ProjectID 
      ORDER BY sp.DateSpent DESC 
     ) AS sp; 
+0

謝謝@GarethD和dnoeth對你的迴應,我創建了沒有測試的查詢,因爲這是一個較大塊的摘錄。但實際問題是,我需要在同一個查詢中再次使用BC和ActualSpendAmount進行一些計算。如果您看到我的查詢的最後一部分,以「t。[Budget] - (SELECT BC + SELECT ActualSpendAmount)」開頭的語句。我不想重複BC和ActualSpendAmount的兩個選擇。有沒有一種方法來優化查詢。我想將每個SELECT元素存儲到局部變量並將其用於計算。有沒有更好的辦法? –

+0

如果我假設你使用SQL Server是正確的,那麼這正是我發佈的查詢所做的。如果您需要在select中使用相關子查詢來計算某些內容,請將其移至「APPLY」,這意味着您可以多次重複使用該列。更好的是,正如我所描述的,將使用JOIN而不是相關的子查詢。 – GarethD

0

導致您的查詢是太複雜(並沒有錯誤將無法運行)。 假設您的DBMS支持窗口集合:

SELECT p.ProjectId, p.NAME, p.Budget, 
    BC.BC, 
    act.ActualSpendAmount, 
    p.Budget - (BC.BC + act.ActualSpendAmount) 
FROM Projects AS p 
LEFT JOIN 
(-- sum of BC per project 
    SELECT ProjectId, SUM(BC) AS BC 
    FROM BC 
    GROUP BY ProjectId 
) AS BC 
ON ProjectId=bc.ProjectId 
JOIN 
(-- latest amount per project 
    SELECT ProjectId, ActualSpendAmount, 
     ROW_NUMBER() 
     OVER (PARTITION BY ProjectId 
      ORDER BY DateSpent DESC) AS rn 
    FROM ActualSpend 
) AS Act 
ON Act.ProjectId=p.ProjectId 
AND Act.rn = 1