Yii2中按广告商ID与服务商ID分组筛选最大日期对应记录的实现问题
解决Yii2中GROUP BY后获取每组最大日期对应完整记录的问题
你的问题核心在于:当你在SELECT中包含非聚合且未出现在GROUP BY中的字段(比如id、companyID)时,MySQL在非严格模式下会随机返回这些字段的值,而非对应MAX(date)的那条记录的值,这就是你得到错误id和companyID的原因。
下面提供两种可靠的解决方案,你可以根据MySQL版本和需求选择:
方法一:子查询关联(兼容所有MySQL版本)
先通过子查询获取每个(advertiserID, providerID)组的最大日期,再关联原表筛选出对应完整记录:
// 第一步:构建子查询,得到每组的最大日期 $subQuery = CompaniesAdvertisersProvidersAssoc::find() ->select(['advertiserID', 'providerID', 'MAX(date) as max_date']) ->groupBy(['advertiserID', 'providerID']); // 第二步:主查询关联子查询,匹配出对应最大日期的完整记录 $currentAssoc = CompaniesAdvertisersProvidersAssoc::find() ->select([ 'companiesAdvertisersProviders.id as bmAssocID', 'companiesAdvertisersProviders.date', 'advertiserID', 'providerID', 'companyID', 'advertisers.name as advertiser', 'providers.name as provider', 'companies.id as companyID', 'companies.name as company', 'advertisers.image as advertiserImage', 'providers.image as providerImage', 'companies.companyLogo as bmImage' ]) ->joinWith(['advertiser', 'provider', 'company'], false) ->innerJoin( ['sub' => $subQuery], 'companiesAdvertisersProviders.advertiserID = sub.advertiserID AND companiesAdvertisersProviders.providerID = sub.providerID AND companiesAdvertisersProviders.date = sub.max_date' ) ->orderBy([ 'company' => SORT_ASC, 'advertiser' => SORT_ASC ]) ->asArray() ->all();
这个方法逻辑清晰:先锁定每组的最大日期,再通过关联条件精准定位到对应的完整记录,彻底避免了随机取值的问题。
方法二:窗口函数(MySQL 8.0+ 推荐)
如果你的MySQL版本是8.0及以上,用ROW_NUMBER()窗口函数可以更简洁地实现需求:
$currentAssoc = CompaniesAdvertisersProvidersAssoc::find() ->select([ 'bmAssocID' => 't.id', 't.date', 't.advertiserID', 't.providerID', 't.companyID', 'advertisers.name as advertiser', 'providers.name as provider', 'companies.id as companyID', 'companies.name as company', 'advertisers.image as advertiserImage', 'providers.image as providerImage', 'companies.companyLogo as bmImage', // 给每个(advertiserID, providerID)组按日期降序编号 'ROW_NUMBER() OVER (PARTITION BY t.advertiserID, t.providerID ORDER BY t.date DESC) as rn' ]) ->from(['t' => CompaniesAdvertisersProvidersAssoc::tableName()]) ->joinWith(['advertiser', 'provider', 'company'], false) // 只保留每组的第一条(即日期最大的记录) ->having('rn = 1') ->orderBy([ 'company' => SORT_ASC, 'advertiser' => SORT_ASC ]) ->asArray() ->all();
窗口函数会给每个分组内的记录按日期降序分配序号,rn=1就代表该组日期最大的那条记录,代码更简洁高效。
额外说明
如果某个(advertiserID, providerID)组内存在多条日期相同且都是最大的记录,上述两种方法会返回所有符合条件的记录。如果需要只保留其中一条(比如按id最大的),可以在子查询或窗口函数的排序条件里加上id DESC即可。
内容的提问来源于stack exchange,提问作者farrusete
相关产品推荐
相关产品推荐

