我已经升级了我的系统,并为我正在开发的web应用程序安装了MySql 5.7.9和php。我有一个查询,是动态创建的,当运行在旧版本的MySQL它工作得很好。自从升级到5.7,我得到这个错误:
SELECT列表的表达式#1不在GROUP BY子句中,并且包含
未聚合列的support_desk.mod_users_groups。Group_id '就是
在功能上不依赖于GROUP BY子句中的列;这是
sql_mode=only_full_group_by不兼容
请注意Mysql 5.7的手册页中关于Server SQL Modes的主题。
这个问题让我很困扰:
SELECT mod_users_groups.group_id AS 'value',
group_name AS 'text'
FROM mod_users_groups
LEFT JOIN mod_users_data ON mod_users_groups.group_id = mod_users_data.group_id
WHERE mod_users_groups.active = 1
AND mod_users_groups.department_id = 1
AND mod_users_groups.manage_work_orders = 1
AND group_name != 'root'
AND group_name != 'superuser'
GROUP BY group_name
HAVING COUNT(`user_id`) > 0
ORDER BY group_name
我不理解only_full_group_by,不足以弄清楚我需要做什么来修复查询。我是否可以关闭only_full_group_by选项,或者还有其他需要做的事情?
这有助于我理解整个问题:
https://stackoverflow.com/a/20074634/1066234
https://dev.mysql.com/doc/refman/5.7/en/group-by-handling.html
下面是另一个问题查询的例子。
问题:
SELECT COUNT(*) as attempts, SUM(elapsed) as elapsedtotal, userid, timestamp, questionid, answerid, SUM(correct) as correct, elapsed, ipaddress FROM `gameplay`
WHERE timestamp >= DATE_SUB(NOW(), INTERVAL 1 DAY)
AND cookieid = #
通过在最后加上这个来解决:
GROUP BY timestamp, userid, cookieid, questionid, answerid, elapsed, ipaddress
注意:查看PHP中的错误消息,它会告诉您问题出在哪里。
例子:
MySQL查询错误1140:在没有GROUP BY的聚合查询中,SELECT列表中的表达式#4包含非聚合列'db.gameplay.timestamp';这与sql_mode=only_full_group_by不兼容-查询:SELECT COUNT(*) as attempts, SUM(elapsed) as elapsedtotal,用户id,时间戳,questionid, answerid, SUM(correct) as correct, elapsed, ipaddress FROM gameplay
WHERE时间戳>= DATE_SUB(NOW(),间隔1天)
AND userid = 1
在本例中,表达式#4在GROUP BY中缺失。
增加/etc/mysql/my.cnf文件中的行(如下所述)
[mysqld]
sql_mode = STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION
对我来说没问题。
服务器版本:5.7.18-0ubuntu0.16.04.1 - (Ubuntu)
我只需将group_id添加到GROUP BY。
When SELECTing a column that is not part of the GROUP BY there could be multiple values for that column within the groups, but there will only be space for a single value in the results. So, the database usually needs to be told exactly how to make those multiple values into one value. Commonly, this is done with an aggregate function like COUNT(), SUM(), MAX() etc... I say usually because most other popular database systems insist on this. However, in MySQL prior to version 5.7 the default behaviour has been more forgiving because it will not complain and then arbitrarily choose any value! It also has an ANY_VALUE() function that could be used as another solution to this question if you really needed the same behaviour as before. This flexibility comes at a cost because it is non-deterministic, so I would not recommend it unless you have a very good reason for needing it. MySQL are now turning on the only_full_group_by setting by default for good reasons, so it's best to get used to it and make your queries comply with it.
那么为什么我的答案如此简单呢?我做了几个假设:
1) group_id是唯一的。看起来很合理,毕竟这是一个“ID”。
2) group_name也是唯一的。这可能不是一个合理的假设。如果不是这种情况,您有一些重复的group_name,然后按照我的建议将group_id添加到GROUP BY,您可能会发现现在得到的结果比以前更多,因为具有相同名称的组现在将在结果中有单独的行。对我来说,这比隐藏这些重复的组要好,因为数据库已经悄悄地任意选择了一个值!
当涉及多个表时,使用表名或别名限定所有列也是一种很好的实践……
SELECT
g.group_id AS 'value',
g.group_name AS 'text'
FROM mod_users_groups g
LEFT JOIN mod_users_data d ON g.group_id = d.group_id
WHERE g.active = 1
AND g.department_id = 1
AND g.manage_work_orders = 1
AND g.group_name != 'root'
AND g.group_name != 'superuser'
GROUP BY
g.group_name,
g.group_id
HAVING COUNT(d.user_id) > 0
ORDER BY g.group_name
可以通过以下命令禁用only_full_group_by setting。
mysql> set global sql_mode='STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION';
mysql> set session sql_mode='STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION';
MySQL 8不接受NO_AUTO_CREATE_USER,所以需要删除。
如果您不想在当前查询中进行任何更改,那么请遵循以下步骤-
流浪汉SSH进你的盒子
类型:sudo vim /etc/mysql/my.cnf
滚动到文件底部并键入A以进入插入模式
复制粘贴
(mysqld)
sql_mode = STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION
按esc退出输入模式
输入:wq保存并关闭vim。
输入sudo service mysql restart重启mysql。
我会试着解释这个错误是关于什么的。
从MySQL 5.7.5开始,选项ONLY_FULL_GROUP_BY默认启用。
因此,根据标准SQL92和更早的版本:
不允许查询select列表,HAVING条件,
或ORDER BY列表引用既没有命名也没有聚合的列
在GROUP BY子句中也不依赖于(唯一的
按列分组
(在文档中阅读更多)
举个例子:
SELECT * FROM `users` GROUP BY `name`;
执行上述查询后,您将得到错误消息。
#1055 - SELECT列表的表达式#1不在GROUP BY子句中,并且包含非聚合列的testsite.user。我不是
函数依赖于GROUP BY子句中的列;这是
sql_mode=only_full_group_by不兼容
为什么?
因为MySQL不能确切地理解,从分组记录中检索哪些特定值,这就是问题所在。
例如,假设你在用户表中有这样的记录:
您将执行上面显示的无效查询。
你会得到如上所示的错误,因为,有3条记录的名字是John,这很好,但是它们都有不同的email字段值。
因此,MySQL根本不知道在结果分组记录中返回哪一个。
你可以通过修改你的查询来解决这个问题:
SELECT `name` FROM `users` GROUP BY `name`
此外,你可能想要添加更多的字段到SELECT节,但你不能这样做,如果他们没有聚合,但有拐杖你可以使用(但强烈不推荐):
SELECT ANY_VALUE(`id`), ANY_VALUE(`email`), `name` FROM `users` GROUP BY `name`
现在,您可能会问,为什么强烈不建议使用ANY_VALUE ?
因为MySQL不确切地知道要检索分组记录的值,并且通过使用这个函数,您要求它获取其中任何一个(在本例中,获取了name = John的第一条记录的电子邮件)。
确切地说,我想不出你为什么想要这种行为存在。
如果你不明白我的意思,请阅读更多关于如何在MySQL中分组工作,这是非常简单的。
最后,这里有一个更简单但有效的查询。
如果希望根据可用年龄查询用户总数,可能需要写下这个查询
SELECT `age`, COUNT(`age`) FROM `users` GROUP BY `age`;
根据MySQL规则,这是完全有效的。
等等。
重要的是要了解问题到底是什么,然后才写下解决方案。