2012-11-28 44 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中查看该技术。