这SQL fiddle说明了我遇到的问题。
作为背景:我有工作,元素,角色时间和费率。作业可以包含多个元素。元素(通常)由一个或多个角色小时(即角色和小时数)组成。每个角色都有一个小时费率,根据日期而不同,并根据工作的客户。
在上面的查询中,我正在尝试获取作业的财务细目:作业的所有元素列表及其总成本。实际上,目前它也正在按角色打破这些元素,但这并不一定是最终查询所必需的。
您可以看到“角色成本”列正确地将每小时费率乘以预算小时数,以达到该角色的小计。但是,当我尝试对这些字段进行求和时(在“元素小计”列中),我得到......好吧,这不是我期待的数字。
我怀疑问题在于我的子查询获得了最新的费率,我将其设置为separate SQL Fiddle here作为参考。它为角色返回了多个可能的速率:当它加入到主查询中时,它因此会消息太多行。
因此扭曲我的甜瓜的问题是:我需要匹配给定客户的“最佳”费率。也就是说,如果某个速率与公司ID 和都匹配,那么我想要那个。但如果没有,我只想要一个与公司ID匹配的那个。如果没有其中一个,我只想要角色的“基础”费率。因此,我的连接中的所有“OR __ IS NULL”。
我不知道怎么做就是把它与“只返回一条记录”结合起来,我需要让SUM()部分工作。
长篇大论道歉。如果你已经走到这一步,谢谢你。
答案 0 :(得分: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 34 表示client_company
上是否匹配,2 {sup> 33 client_group
和2 <在client_contact
上的sup> 32 ,其中32个最低位表示速率date_from
- 然后取最大相关性得分将得出最佳匹配的得分,并加入{ {1}}表再次允许一个人根据需要获得rate
。
甚至可以进一步改进这一点,以避免计算相关性分数,通过嵌套来按顺序查找每列的分组最大值;但是,除非您遇到无法以任何其他方式解决的性能问题,否则可能不值得沿着这条路走下去。您可以在my answer to another question中看到该技术。