【问题标题】:Multiple GROUP BY's & sort by SUM'd group values多个 GROUP BY 并按 SUM 的组值排序
【发布时间】: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

我的问题:

  1. 实现像 »project_minutes« 这样的列的最佳方法是对一组分钟求和并将结果添加为一个额外的列?有没有我不知道的巧妙的 SQL 技巧?
  2. 如果您看不到我的第一个问题的方法,您认为为每个查询创建一个额外的表有意义吗?它是否比在代码中手动执行此逻辑更快?我们使用 Rails,以防万一。

非常感谢您的帮助!

更新

感谢您到目前为止的回复。我将它们总结为一个要点,以获得更好的概述: http://gist.github.com/553560

我说得对吗?除了每个 group by 语句查询一次 time_entries 表之外别无他法?如果是,您是否发现性能问题是因为以下事实:

  1. time_entries 表是迄今为止行数最多的表(约 400 万行)
  2. 用户最多可以按 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,它不需要。

标签: mysql group-by


【解决方案1】:

我假设time_entries中的project_id总是NOT NULL,而services_id可以为null

Select t.date, t.project_name, t.service_name, p.minutes as Project_minutes, t.minutes
FROM
(SELECT                             
  time_entries.date_at,
  time_entries.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 
LEFT JOIN services ON services.id = time_entries.service_id 
GROUP BY 
  time_entries.date_at
  time_entries.project_id, 
  time_entries.service_id    
) t
JOIN
  (Select date_at, project_Id, Sum(minutes) minutes
  from time_entries
  group by date_at, project_id) p
ON (p.date_at = t.date_at AND p.project_id = t.project_id)

【讨论】:

    【解决方案2】:

    这样的事情应该做你想做的事:

    SELECT ilv1.date_at, ilv1.project_name, ilv1.service_name, ilv1.minutes
    FROM 
    ( SELECT                             
      te1.date_at,
      p1.name as project_name,
      s1.name as service_name,
      SUM(minutes) AS minutes 
    FROM time_entries te1             
    LEFT OUTER JOIN projects p1 ON p1.id = te1.project_id 
    LEFT OUTER JOIN services s1 ON s1.id = te1.service_id 
    GROUP BY 
      te1.project_id, 
      te1.service_id) AS ilv1,
    ( SELECT                             
      te2.date_at,
      p2.name as project_name,
      SUM(minutes) AS minutes 
    FROM time_entries te1             
    LEFT OUTER JOIN projects p1 ON p1.id = te1.project_id  
    GROUP BY 
      te1.project_id) AS ilv2
    

    在哪里 ilv1.date_at=ilv2.date_at 和 ilv1.project_name=ilv2.project_name 按 ilv2.minutes 订购;

    (你真的,真的需要所有这些外连接 - 它们会严重影响性能)

    根据您的原始查询(以及具有上述不同分组的两遍查询)使用物化视图可能会更有效。但是中途旅行可能是两次使用相同的查询基础查询并将一个包装在合并块中,例如

    SELECT ilv1.date_at, ilv1.project_name, ilv1.service_name, ilv1.minutes
    FROM 
     (....) ilv1,
     (SELECT ilv3.date_at, ilv3.project_name, sum(ilv3.minutes) as minutes 
      FROM (...copy of ilv1) ilv3
      GROUP BY ilv3.date_at, ilv3.project_name
     ) ilv2
    WHERE ilv1.date_at=ilv2.date_at
    

    AND ilv1.project_name=ilv2.project_name 按 ilv2.minutes 订购;

    C.

    【讨论】:

      猜你喜欢
      • 2021-05-01
      • 1970-01-01
      • 2021-04-22
      • 1970-01-01
      • 2011-05-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多