You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Laravel 10高级Has One Of Many关联引发SQLSTATE[42000]错误

Laravel 高级Has One Of Many关联的GROUP BY问题解决

问题场景

我用「高级方式」定义了Has One Of Many关联,实现逻辑和Laravel官方的高级Has One Of Many示例几乎一致。

模型代码如下:

class DocumentType extends Model
{
    use HasFactory, SoftDeletes;

    // 其他代码...

    public function currentRevision () {

       return $this->hasOne(DocumentTypeRevision::class, 'document_type_id')
                   ->ofMany (['revision_number' => 'max', 'revision_date' => 'max'], 
                              function ($qry) {
                                  $qry->where('revision_date', '<=', now()->startOfDay());
                     });
    }
}

执行以下代码时:

$revision = DocumentType::find(1)->currentRevision;

触发了SQLSTATE[42000]错误,问题根源是生成的SQL中,SELECT部分包含未出现在GROUP BY中的字段。目前临时解决方案是在database.php中关闭数据库连接的ONLY_FULL_GROUP_BY配置,但希望从根源解决问题。

生成的错误SQL

select `document_types_revisions`.* 

from `document_types_revisions` 

inner join (

    select max(`document_types_revisions`.`id`) as `id_aggregate`, 
           `document_types_revisions`.`revision_date` as `revision_date_aggregate`, 
           `document_types_revisions`.`revision_number` as `revision_number_aggregate`, 
           `document_types_revisions`.`document_type_id` 
       
    from `document_types_revisions` 
    
    inner join (
    
        select max(`document_types_revisions`.`revision_date`) as `revision_date_aggregate`, 
               `document_types_revisions`.`revision_number` as `revision_number_aggregate`, 
               `document_types_revisions`.`document_type_id` 
               
            from `document_types_revisions` 
       
            inner join (
       
                select max(`document_types_revisions`.`revision_number`) as `revision_number_aggregate`, 
                          `document_types_revisions`.`document_type_id` 
                
                from `document_types_revisions` 
                
                where `revision_date` <= '2023-03-08 00:00:00' 
                  and `document_types_revisions`.`document_type_id` = 3 
                  and `document_types_revisions`.`document_type_id` is not null 
                  and `document_types_revisions`.`deleted_at` is null 
              
                group by `document_types_revisions`.`document_type_id`
            
        ) as `currentRevision` on `currentRevision`.`revision_number_aggregate` = `document_types_revisions`.`revision_number` 
                              and `currentRevision`.`document_type_id` = `document_types_revisions`.`document_type_id` 
                              
        where `revision_date` <= '2023-03-08 00:00:00' 
          and `document_types_revisions`.`deleted_at` is null 
          
        group by `document_types_revisions`.`document_type_id`
        
    ) as `currentRevision` on `currentRevision`.`revision_date_aggregate` = `document_types_revisions`.`revision_date` 
                          and `currentRevision`.`revision_number_aggregate` = `document_types_revisions`.`revision_number` 
                          and `currentRevision`.`document_type_id` = `document_types_revisions`.`document_type_id` 
                          
    where `revision_date` <= '2023-03-08 00:00:00' 
      and `document_types_revisions`.`deleted_at` is null 
      
    group by `document_types_revisions`.`document_type_id`
    
) as `currentRevision` on `currentRevision`.`id_aggregate` = `document_types_revisions`.`id` 
                      and `currentRevision`.`revision_date_aggregate` = `document_types_revisions`.`revision_date` 
                      and `currentRevision`.`revision_number_aggregate` = `document_types_revisions`.`revision_number` 
                      and `currentRevision`.`document_type_id` = `document_types_revisions`.`document_type_id` 
                      
where `document_types_revisions`.`document_type_id` = 3 
  and `document_types_revisions`.`document_type_id` is not null 
  and `document_types_revisions`.`deleted_at` is null 
  
limit 1;

根源解决方案

问题出在Laravel生成子查询时,直接选取了revision_date和revision_number字段,但这两个字段未包含在GROUP BY中,违反了ONLY_FULL_GROUP_BY的SQL规则。可以通过以下两种方式彻底修复:

方式一:改用排序逻辑筛选目标记录

放弃多字段聚合的写法,通过排序直接筛选出符合条件的最新版本,Laravel会自动生成符合GROUP BY规则的SQL:

public function currentRevision()
{
    return $this->hasOne(DocumentTypeRevision::class, 'document_type_id')
        ->where('revision_date', '<=', now()->startOfDay())
        ->orderByDesc('revision_number')
        ->orderByDesc('revision_date');
}

方式二:确保所有SELECT字段都被聚合或分组

如果要保留ofMany的多字段聚合写法,需要将非分组字段用聚合函数包裹,确保SELECT中的字段要么是分组字段,要么是聚合结果:

public function currentRevision()
{
    return $this->hasOne(DocumentTypeRevision::class, 'document_type_id')
        ->ofMany(
            [
                'revision_number' => 'max',
                'revision_date' => 'max',
                'id' => 'max'
            ],
            function ($query) {
                $query->where('revision_date', '<=', now()->startOfDay());
            }
        );
}

原理说明

使用多字段ofMany时,Laravel默认生成的多层子查询中,revision_date和revision_number被直接选取但未分组,导致违反SQL模式规则。上述两种方式要么通过排序逻辑让Laravel生成正确的关联查询,要么确保所有SELECT字段都满足GROUP BY要求,从根源解决语法错误。

内容的提问来源于stack exchange,提问作者TUPKAP

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 22:52:49