我在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的了解有限。
答案 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将按值对组进行排序。