【发布时间】:2011-04-04 20:51:30
【问题描述】:
我正在为我们的时间跟踪应用编写报告。每个时间条目都与一个项目和一项服务相关。这是按项目和服务对时间条目进行分组的简化查询。
SELECT
projects.name as project_name,
services.name as service_name,
SUM(minutes) AS minutes
FROM `time_entries`
JOIN `projects` ON `projects`.id = `time_entries`.project_id
JOIN `services` ON `services`.id = `time_entries`.service_id
GROUP BY
time_entries.project_id,
time_entries.service_id
ORDER BY
max(minutes) DESC
这将产生一个像这样的表格:
+---------------+--------------+---------+
| project_name | service_name | minutes |
+---------------+--------------+---------+
| Business Card | Consulting | 4800 |
| Microsite | Coding | 3200 |
| Microsite | Consulting | 2400 |
| Microsite | Design | 2400 |
| Business Card | Design | 800 |
+---------------+--------------+---------+
我尝试实现的是按 SUM 的项目分钟数排序的可能性。不是“名片”项目,而是“微型网站”项目,因为它有更多的时间。
+---------------+--------------+-----------------+---------+
| project_name | service_name | project_minutes | minutes |
+---------------+--------------+-----------------+---------+
| Microsite | Coding | 8000 | 3200 |
| Microsite | Consulting | 8000 | 2400 |
| Microsite | Design | 8000 | 2400 |
| Business Card | Consulting | 5600 | 4800 |
| Business Card | Design | 5600 | 800 |
+---------------+--------------+-----------------+---------+
我发现获取»project_minutes« 列的唯一方法是先创建一个表并将其与自身连接。我提出的查询:
DROP TABLE IF EXISTS group2;
CREATE TABLE group2 SELECT
projects.id as project_id,
projects.name as project_name,
services.name as service_name,
SUM(minutes) AS minutes
FROM `time_entries`
JOIN `projects` ON `projects`.id = `time_entries`.project_id
JOIN `services` ON `services`.id = `time_entries`.service_id
GROUP BY
time_entries.project_id,
time_entries.service_id
ORDER BY
max(minutes) DESC
LIMIT 0, 30;
SELECT
project_name, service_name, project_minutes, minutes
FROM
group2
LEFT JOIN
(
SELECT project_id as project_id, sum(minutes) AS project_minutes
FROM group2
GROUP BY project_id
) as group1 on group1.project_id = group2.project_id
ORDER BY
project_minutes DESC,
minutes DESC;
由于 mySQL 错误 (?),我什至无法创建临时表: http://www.google.com/search?&q=site:bugs.mysql.com+reopen+temporary+table
我的问题:
- 实现像 »project_minutes« 这样的列的最佳方法是对一组分钟求和并将结果添加为一个额外的列?有没有我不知道的巧妙的 SQL 技巧?
- 如果您看不到我的第一个问题的方法,您认为为每个查询创建一个额外的表有意义吗?它是否比在代码中手动执行此逻辑更快?我们使用 Rails,以防万一。
非常感谢您的帮助!
更新
感谢您到目前为止的回复。我将它们总结为一个要点,以获得更好的概述: http://gist.github.com/553560
我说得对吗?除了每个 group by 语句查询一次 time_entries 表之外别无他法?如果是,您是否发现性能问题是因为以下事实:
- time_entries 表是迄今为止行数最多的表(约 400 万行)
- 用户最多可以按 6 列进行分组。看看这个截图: http://dl.dropbox.com/u/732913/time_entries_grouped_by_customer_project_service_user.png
【问题讨论】:
-
您需要所有的 LEFT JOIN 操作吗?为什么不只是加入?第二部分中的子查询或多或少是正确的方法,但应该适用于基本表而不是您的“group2”临时表。
-
@Gregor,服务和项目如何相互关联?
-
乔纳森,感谢您的回复和您的错字修正!我简化了连接。原始查询更复杂,这就是为什么它们一直是 LEFT OUTER JOIN。如果我在基本表的第二部分中进行子查询,我不需要重复相同的选择两次吗?它最终会是这样的:gist.github.com/553560 或者我错过了什么?
-
@Michael 服务和项目之间没有关系。每个时间条目属于一个项目和一项服务。两种关系都是可选的。服务和项目都属于帐户
-
我从示例查询中删除了 date_at,它不需要。