2012-11-28 77 views
0

SQL fiddle說明了我遇到的問題。已加入子查詢中的mysql sum()返回意外總數

作爲背景:我有工作,元素,工作時間和費率。作業可以由幾個元素組成。元素(通常)由一個或多個角色小時(即角色和小時數)組成。每個角色的小時費率根據日期不同而不同,並根據客戶的工作情況而定。

在上面的查詢中,我試圖獲得一份工作的財務細目:一份工作的所有元素及其總成本。實際上,目前它也是按角色分解這些元素,但這對最終查詢並不一定是必需的。

您可以看到,「角色成本」列正確地將小時費率與預算小時數相乘,以達到該角色的小計。但是,當我嘗試SUM這些字段(在「元素小計」列中)時,我越來越......好吧,這不是我期待的數字。

我懷疑問題是我的子查詢得到最新的費率,我已經設置爲separate SQL Fiddle here作爲參考。它爲一個角色返回多個可能的比率:當它被重新加入到主查詢中時,它將合計太多的行。

因此扭曲我的瓜的問題是這樣的:我需要匹配給定客戶的「最佳」費率。也就是說,如果有與客戶ID相匹配的公司ID 的費率​​,我想要那個。但如果沒有,我只想要一個與公司ID相匹配的人。如果沒有其中之一,我只想要角色的「基礎」費率。因此,我所有的「OR __ IS NULL」在我的連接。

我不知道該怎麼做,結合我需要使SUM()部分工作的「只返回一條記錄」。

道歉的長期職位。如果你有這麼遠,謝謝。

+0

就我所知,在比較'rates'表和'jobs'表中的三個'client_ *'列時,有四種可能的結果:它們匹配,不匹配,一個一邊是「NULL」,另一邊是「NULL」。在三欄中,這是64個可能的結果。您試圖對這64種可能的結果進行排序,其中'rates.date_from'列以某種方式被用於打破關係。然而,對於我來說,這個順序應該是什麼,有點不清楚:倒數第二段似乎與您的查詢不一致。請澄清。 – eggyal

+0

我猜不一致是因爲子查詢沒有做到我想要的:)它應該最好匹配rates表中的* single *記錄,但是確切地說哪一個取決於它找到的匹配。 (1)同一公司,集團和客戶 (2)同一公司,集團(空客戶) (2)同一公司(空集團和客戶) 客戶(客戶) 客戶匹配應始終優先於日期。因此,所有三列上匹配的舊記錄應優於剛剛匹配的新記錄,比如公司。 – Wintermute

回答

0

一種方式將涉及correlated subquery

SELECT e.id AS element_id, 
     h.role, 
     SUM(h.hours_budgeted) AS total_hours_budgeted, 
     r.hourly_rate, 
     e.pm_amount, 
     e.revenue AS fixed_revenue, 
     e.revenue_extra, 
     SUM(h.hours_budgeted) * r.hourly_rate AS element_subtotal 
FROM  job     j 
    JOIN job_element   e ON e.job  = j.id 
    JOIN job_element_role_hours h ON h.element = e.id 
    JOIN rate     r ON r.id  = (
      SELECT id 
      FROM  rate 
      WHERE rate.role = h.role 
       AND IFNULL(rate.client_company = j.client_company, TRUE) 
       AND IFNULL(rate.client_group = j.client_group , TRUE) 
       AND IFNULL(rate.client_contact = j.client_contact, TRUE) 
      ORDER BY rate.client_company DESC, 
        rate.client_group DESC, 
        rate.client_contact DESC, 
        rate.date_from  DESC 
      LIMIT 1 
     ) 
WHERE j.id = 1 
GROUP BY e.id, h.role 

看到它的sqlfiddle

但是,相關的子查詢效率低下,可能會很慢。正如手冊所述:

將查詢重寫爲連接可能會提高性能。

爲了做到這一點,一個人必須要獲得groupwise maximum

SELECT e.id AS element_id, 
     h.role, 
     SUM(h.hours_budgeted) AS total_hours_budgeted, 
     r.hourly_rate, 
     e.pm_amount, 
     e.revenue AS fixed_revenue, 
     e.revenue_extra, 
     SUM(h.hours_budgeted) * r.hourly_rate AS element_subtotal 
FROM  job     j 
    JOIN job_element   e ON e.job  = j.id 
    JOIN job_element_role_hours h ON h.element = e.id 
    JOIN rate     r ON r.role = h.role 
      AND IFNULL(r.client_company = j.client_company, TRUE) 
      AND IFNULL(r.client_group = j.client_group , TRUE) 
      AND IFNULL(r.client_contact = j.client_contact, TRUE) 
    JOIN (
     SELECT j.client_company, j.client_group, j.client_contact, r.role, 
       MAX(
       IF(r.client_company <=> j.client_company, 1<<34, 0) 
       | IF(r.client_group <=> j.client_group , 1<<33, 0) 
       | IF(r.client_contact <=> j.client_contact, 1<<32, 0) 
       | UNIX_TIMESTAMP(r.date_from) 
       ) AS relevance 
     FROM  rate r JOIN job j ON 
        IFNULL(r.client_company = j.client_company, TRUE) 
       AND IFNULL(r.client_group = j.client_group , TRUE) 
       AND IFNULL(r.client_contact = j.client_contact, TRUE) 
     GROUP BY j.client_company, j.client_group, j.client_contact, r.role 
    ) t  ON t.role = r.role 
      AND t.client_company = j.client_company 
      AND t.client_group = j.client_group 
      AND t.client_contact = j.client_contact 
      AND t.relevance = IF(r.client_company <=> j.client_company, 1<<34, 0) 
          | IF(r.client_group <=> j.client_group , 1<<33, 0) 
          | IF(r.client_contact <=> j.client_contact, 1<<32, 0) 
          | UNIX_TIMESTAMP(r.date_from) 
WHERE j.id = 1 
GROUP BY e.id, h.role 

看到它的sqlfiddle

在這裏,我通過計算相關性分數找到了與您的嘗試類似的徒勞分組最大值。然而,我通過一些位操作,其中,2 指示是否存在上client_company匹配,2 上client_group和2 32上client_contact去,與代表率的date_from —然後32最低階位獲取最大相關性分數將得出最佳匹配的分數,再次加入rate表使得人們能夠根據需要獲得hourly_rate

人們甚至可以進一步提高這一點,以避免計算相關性分數,通過嵌套來按順序查找每列上的分組最大值;然而,除非您遇到無法以其他方式解決的性能問題,否則可能不值得沿着這條路走下去。您可以在my answer to another question中查看該技術。