表达式不在GROUP BY子句中

时间:2018-09-24 16:29:06

标签: mysql sql group-by aggregate

我在MySQL(v5.7)中有一个日志表,其中记录了用户请求,我从中提取了活动细目,以显示每月的用户数和总点击数,例如:

Date            Users   Hits
September 2018  20      1,839
August 2018     23      2,723
July 2018       21      1,632
June 2018       22      2,981

当前使用以下查询来实现:

SELECT month(l.time) m, year(l.time) y, date_format(l.time, '%M %Y') monthyear, 
  (select count(distinct userid) from log lm 
    where month(lm.time) = month(l.time) and year(lm.time) = year(l.time)) users,
  count(u.name) hits
FROM log l left join users u on u.id=l.userid
group by date_format(l.time, '%M %Y')
order by l.time desc, l.id desc

此SQL失败,仅启用了full_group_by,这是MySQL中的默认值,因为并非所有表达式都在GROUP BY子句中。我发现的解决方案通常涉及使用诸如MAX()之类的聚合函数或将所有表达式添加到GROUP BY子句中,但是'users'子查询使这些方法成为问题:我不能使用MAX()方法(无效的语法)并将其添加到GROUP BY子句中会导致查询如此缓慢,以至于我尚未看到测试完成。

我觉得可能有一种优雅而高效的解决方案,而不必求助于only_full_group_by的禁用,但是我对SQL的了解有限。

2 个答案:

答案 0 :(得分:1)

我不确定为什么要为此使用子查询。这不是您想要的吗?

SELECT month(l.time) as m, year(l.time) as y, date_format(l.time, '%M %Y') as monthyear, 
       count(distinct l.userid) as users,
       count(u.name) as hits
FROM log l left join
     users u
     on u.id = l.userid
GROUP BY m, y, monthyear
ORDER BY max(l.time) desc, l.id desc;

答案 1 :(得分:1)

这是一个简化的查询:

SELECT DATE_FORMAT(l.time, '%M %Y') AS monthyear, 
  COUNT(DISTINCT l.userid) AS users,
  COUNT(*) AS hits
FROM log l
GROUP BY monthyear

您不需要选择列表中的单个月份或年份,因为您不会在期望的结果中显示它。

您根本不需要加入users表,除非您打算只对具有非NULL name列的用户的点击次数进行计数(COUNT会忽略NULL,我猜是您的意思是要计算日志中的所有匹配,这意味着您应该使用COUNT(*)而不是COUNT(u.name)

我删除了ORDER BY子句,因为它引用了不在结果中的列。如果要按月年份进行订购,则应考虑以一种可以格式化所需方式的方式格式化月年份:

SELECT DATE_FORMAT(l.time, '%Y-%m') AS monthyear, 
  COUNT(DISTINCT l.userid) AS users,
  COUNT(*) AS hits
FROM log l
GROUP BY monthyear

默认情况下,GROUP BY将按值对组进行排序。

相关问题