Laravel 使用 MySQL 的 ONLY_FULL_GROUP_BY 问题

Laravel 使用 MySQL 的 ONLY_FULL_GROUP_BY 问题
Photo by fabio / Unsplash

定位问题

最近在使用 Laravel Relationship 查询关联模型时遇到了如下错误:

SQLSTATE[42000]: Syntax error or access violation: 1055 Expression #6 of SELECT list is not in GROUP BY clause and contains nonaggregated column 'echo_dev.domain_links.domain_id' which is not functionally dependent on columns in GROUP BY clause; this is incompatible with sql_mode=only_full_group_by

相关的表结构如下:

keywords:

  • id

articles:

  • id
  • category_id

articles_keywords:

  • article_id
  • keyword_id
  • category_id

调用方式:

$category->keywords()->groupBy('articles_keywords.keyword_id');

关联表中的 keyword_id 有重复的情况,所以使用了 groupBy 来去重,结果出现了上面的错误,大意是 ONLY_FULL_GROUP_BY 的查询模式要求 SELECT 的字段必须全部出现在 GROUP BY 中,这是因为 5.7 之后 Mysql 的 查询模式有所变化,默认值如下:

ONLY_FULL_GROUP_BY
STRICT_TRANS_TABLES
NO_ZERO_IN_DATE
NO_ZERO_DATE
ERROR_FOR_DIVISION_BY_ZERO
NO_AUTO_CREATE_USER
NO_ENGINE_SUBSTITUTION

关于这些模式的作用,可以查阅 MySQL 官方文档

解决方法

以 Laravel 5.4 为例,如果要解决这个问题,可以找到 Laravel 的配置文件 database.php,修改如下部分:

'connections' => [
     'mysql' => [
         'strict' => true
         //默认值,表示使用 5.7 的默认模式。

         'strict' => false
         //表示使用 5.6 的默认模式。
     ]
 ]

如果需要显式指定 sql_mode,可以如下设置:

'connections' => [
     'mysql' => [
         //忽略该值,根据 strict 值确定查询模式
         'modes' => null,

         //禁用所有模式
         'modes' => [],

         //显示指定模式
         'modes' => [
              'STRICT_TRANS_TABLES',
              'ONLY_FULL_GROUP_BY',
         ],
     ]
 ]

也可以通过 SQL 语句来修改 sql_model

SET sql_mode = '';
SET sql_mode = 'ONLY_FULL_GROUP_BY';

需要注意的是,这种方式在 MySQL 重启后会失效,如果需要永久生效,请修改 MySQL 的配置文件。