背景

使用ThinkPHP 8框架,遇到了一个1055异常错误。错误是由数据库查询引起的,具体来说是因为MySQL的一个特性——only_full_group_by设置导致的

场景

正在本地环境中执行一条命令:

➜  code git:(main) ✗ php think scan test -vvv

这条命令用于执行一个详细的测试案例,但是它触发了一个异常:

[think\db\exception\PDOException]                                                  
SQLSTATE[42000]: Syntax error or access violation: 1055 Expression #1 of SELECT    
list is not in GROUP BY clause and contains nonaggregated column 'qscan.sast_cod   
eql.id' which is not functionally dependent on columns in GROUP BY clause; this    
is incompatible with sql_mode=only_full_group_by                                   

显然,这个错误与SQL语句中的GROUP BY子句有关,因为某些列没有被包含在GROUP BY中,而这些列又不是聚合函数的一部分,这就与数据库服务器的only_full_group_by模式不兼容了。

修复原理

在MySQL中,only_full_group_by是一个SQL模式设置,它要求所有的非聚合列都必须出现在GROUP BY子句中,否则查询将失败。如果我们的应用程序依赖于旧的行为(即允许不完全的GROUP BY),我们需要禁用这个设置或修改查询来满足新规则。

实践测试

为了解决这个问题,我们首先尝试临时更改数据库的SQL模式。可以在MySQL命令行工具中执行如下命令:

SET sql_mode=(SELECT REPLACE(@@sql_mode,'ONLY_FULL_GROUP_BY',''));

这会临时去掉only_full_group_by设置的影响。为了在ThinkPHP 8中实现同样的行为,可以使用框架提供的数据库操作方法来执行这个设置:

Db::connect('local_test')->execute("SET sql_mode=(SELECT REPLACE(@@sql_mode,'ONLY_FULL_GROUP_BY',''));");

这样做的好处是可以在代码层面控制SQL模式,而不是全局地改变数据库的配置。这种方法更适合在测试环境或者调试阶段使用,确保我们不会影响生产环境的数据库设置。

总结

虽然通过上述方法可以解决眼前的问题,但长期来看,我们应该调整查询逻辑,使其符合现代数据库的最佳实践。例如,我们可以确保所有非聚合列都被正确地包含在GROUP BY子句中,或者添加必要的聚合函数。此外,还可以考虑使用更灵活的SQL模式设置,比如STRICT_TRANS_TABLES,它允许部分GROUP BY,但仍然保持了一定程度的数据完整性检查。

编写查询时要考虑到only_full_group_by设置,以避免将来再次遇到类似问题。


作者: 汤青松
日期:2024年8月22日
微信:songboy8888


汤青松
5.2k 声望8.3k 粉丝

《PHP Web安全开发实战》 作者