und*_*man 6 mysql group-by sum
我GROUP BY在 MySQL中遇到困难。
我的数据库设置:
client_visit
- id
- member_id
- status_type_id (type_of_visit table)
- visit_starts_at
- visit_ends_at
member
- id
schedule_event
- id
- member_id
- starts_at
- ends_at
type_of_visit
- id
- type (TYPE_BOOKED, TYPE_PRESENT etc)
Run Code Online (Sandbox Code Playgroud)
就这个问题而言:a在给定时间member教一门课或领导一项活动 (a schedule_event)。Aclient报名参加此课程或活动。
例如:
客户 A、B 和 C 预订访问,而那些访问client_visit由schedule_event_id和组成的表member_id,因此我们知道哪个班级和哪个成员正在教授/或进行活动。
现在,我们想知道给定成员花费在客户注册的教学/领导活动上的总时间(基于client_visit type_of_visit相当于“预订”或“出席”的列)。我们将把成员 ID 82 作为我们的测试用例。
会员 ID 82 在两个不同的班级有 4 个客户,所以如果每个班级花费 2 小时 15 分钟(8100 秒),那么总时间应该是 16200 秒。
首先是我的查询:
SELECT cv.member_id AS `member_id`,
sch.id AS `scheduleId`,
cv.visit_starts_at AS `visitStartsAt`,
TIMESTAMPDIFF(SECOND, sch.starts_at, sch.ends_at) AS `totalTime`
FROM `schedule_event` AS `sch`
LEFT JOIN `client_visit` AS `cv` ON cv.schedule_event_id = sch.id
INNER JOIN `type_of_visit` AS `tov` ON tov.id = cv.status_type_id
WHERE (tov.type = 'TYPE_BOOKED' OR tov.type = 'TYPE_PRESENT') and cv.member_id = 82
Run Code Online (Sandbox Code Playgroud)
这向我展示了第一类的客户,以及第二类的客户。我只想要两行,每个班级。所以,我补充一下:
GROUP BY sch.id
Run Code Online (Sandbox Code Playgroud)
到现在为止还挺好,
我知道这个成员有两个时间表 ID,所以我修改了组,将它们合并为一个:
GROUP BY sch.id AND cv.member_id
Run Code Online (Sandbox Code Playgroud)
我希望它会首先根据sch.id(结果已经在上图中显示)和cv.member_id(我们有两行,所以合并后应该是一行)
结果是(我通过添加 GROUP_CONCAT 修改了 scheduleId,所以我们可以看到两个调度 ID 都在那里):

现在,就像我将两个计划 ID 放在一起一样,我想将两个计划课程的时间相加。
我现在修改查询:
SUM(TIMESTAMPDIFF(SECOND, sch.starts_at, sch.ends_at)) AS `totalTime`
Run Code Online (Sandbox Code Playgroud)
我有32400!出于某种原因,SUM 仍然可以看到所有 4 行,而不仅仅是唯一的 2 行。
我预计最终的结果是
+-----------+------------+
| member_id | total_time |
+-----------+------------+
| 82 | 16200 |
+-----------+------------+
Run Code Online (Sandbox Code Playgroud)
不需要所有其他列,我只是让它们看看发生了什么
怎么了?
我相信您对 GROUP BY 的作用有误解。毫不奇怪,我在第一次学习时遇到了问题,很大程度上是因为 MySQL 手册实际上并没有明确说明 GROUP BY 的作用,至少不是我能找到的(我刚才搜索了很多;很多警告和特殊行为,而不是实际定义)。
我的(即时)定义:
GROUP BY 压缩您的 SELECT 结果,以便为 GROUP BY 子句中指定的列的每个不同的值组合仅返回 1 行。从这个意义上说,它类似于 DISTINCT,但适用于 GROUP BY 中的列而不是 SELECT 语句。
在非 MySQL 领域,您只能选择您在 GROUP BY 子句中指定的列,以及您想要的任何聚合函数。这些聚合函数,包括 SUM,以每行为基础进行操作,仅报告现在“隐藏”的额外行的结果。
如您所见,这就是您的查询实际执行的操作(或者将执行此操作,但我认为您提供了一个不准确的示例,正如 ypercube 在评论中指出的那样)。它汇总了所有现在隐藏的额外行,并报告给定的sch.id.
如果您只想要 each 的不同值的总和,则sch.id必须以不同的方式做事才能获得所需的信息。
它不简单的一个原因是 MySQL 不知道您要在总和中包含哪一行。在您的示例 (8100) 中,它们可能完全相同,但不能保证这一点。
由于 MySQL 允许您选择既不在 GROUP BY 子句中指定也不是聚合函数的列,因此它本质上是“随机”选择一个并将其显示给您。虽然实际上不是随机的,但它是不确定的,并且可以随时更改相同的查询和数据,即使在您看来总是给出相同的结果。
因此,在继续之前,您需要决定如何确定每行的哪一行sch.id包含要求和的值。
如果您知道值始终相同,那么一个简单(尽管不一定优化)的解决方案是将原始 GROUP BY 查询包装在另一个查询中(使原始查询成为子查询),然后在外部查询中使用 SUM 函数,没有 GROUP BY 子句。子查询将删除您的重复项,外部查询将总结去重行的总数。
正如威廉·伦泽马(Willem Renzema)所说,你误解了GROUP BY它的工作原理。既然你似乎没有理解他说的话,那么让我试着换一种说法。
GROUP BY从逻辑上讲,它用于将结果集中的行分组在一起。通常,您会提供一个列列表,用于将行分组在一起。GROUP BY sch.id, cv.member_id告诉 SQL 识别这两列的唯一值集,并根据这些值对结果集中的行进行分组。在您的情况下,这两个值有两个唯一值对:
cv.member_id= 82, sch.id= 17101cv.member_id= 82, sch.id= 17153因此,您将得到两组行 - 三组具有第一对值,一组具有第二对值。
向子句添加附加列永远GROUP BY不会导致组数减少 - 要么所有行中的新列都相同(在这种情况下,组数相同),要么新列具有不同的值形式一个或多个小时原始组中的某些行(在这种情况下,您现在将拥有更多组)。
另外(正如威廉所指出的),你有一个语法错误。列表中的列GROUP BY以逗号分隔。在您的 中GROUP BY sch.id AND cv.member_id,您按计算进行分组: ,或将两者sch.id AND cv.member_id视为布尔值的结果。由于两者都不是 0,因此当转换为布尔值时,两者的计算结果均为 1(真),并且组合为真。因此,最终您只得到一组 4 行。sch.idcv.member_id(true AND true)
让我们退后一步,考虑一下您实际上想要做什么(看起来像)。对于给定的member_id,您需要他们参与“已预订”或“当前”类型活动的总时间。
请注意,总时间是从schedule_event表中计算出来的。另请注意,给定的内容可以多次member_id与相同的内容关联。schedule_event因此,为了获得总时间,我们需要识别与schedule_event我们member_id相关的不同行,并对这些唯一值的时间求和。
在这种情况下,最简单的方法是使用子查询来获取schedule_events我们member_id所绑定的不同事件的列表,然后对这些不同事件的总时间求和。
这是一个可以做到这一点的查询:
SELECT `member_id`
,SUM(`totalTime`) as `totalTime`
FROM (
SELECT DISTINCT
cv.member_id AS `member_id`,
sch.id AS `scheduleId`,
TIMESTAMPDIFF(SECOND, sch.starts_at, sch.ends_at) AS `totalTime`
FROM
`schedule_event` AS `sch`
INNER JOIN `client_visit` AS `cv` ON cv.schedule_event_id = sch.id
INNER JOIN `type_of_visit` AS `tov` ON tov.id = cv.status_type_id
WHERE
(tov.type = 'TYPE_BOOKED' OR tov.type = 'TYPE_PRESENT')
AND cv.member_id = 82
) sq
GROUP BY `member_id`;
Run Code Online (Sandbox Code Playgroud)
子查询(富有想象力地标记为sq)基本上是您的原始查询。我将您的更改LEFT JOIN为INNER JOIN,因为我们必须有client_visit记录来识别member_id、 和访问类型。但是,我删除SUM了totalTime; 此时,我们只想知道每次schedule_event需要花费的时间。我还补充说DISTINCT- 我们不在乎 thisschedule_event与 this 一起出现多少次member_id;无论出现一次、三次、还是207次,总时间都是一样的。
一旦我们确定了连接到schedule_event的数据member_id,我们就需要所有这些行的总时间schedule_event。因此,我们获取子查询的结果,将它们分组member_id(以防有必要将其拉回多个member_id值),并对每行的计算时间求和schedule_event。
由于 joanolo 不厌其烦地为你的问题设置了一个 dbfiddle,所以我接受了他的工作并在最后添加了这个查询,这样你就可以看到结果就是你想要的;更新后的 dbfiddle 链接在这里。
我希望这有助于阐明如何GROUP BY真正适合您。
| 归档时间: |
|
| 查看次数: |
29210 次 |
| 最近记录: |