yii模型查询去重需显式调用distinct(),置于select()后、all()前;多字段组合去重须用expression包裹distinct;关联查询中distinct()仅作用于主表,笛卡尔积重复需改用innerjoinwith+distinct()或groupby。

在Yii模型查询中,需要从结果集中剔除重复行时,必须显式启用去重机制,否则即使SQL语句含DISTINCT关键字,ActiveRecord也不会自动识别或生效。
基础去重:使用distinct()方法
调用distinct()是开启去重最直接的方式,它会将DISTINCT关键字注入生成的SELECT语句头部。
执行User::find()->select('status')->distinct()->all(),生成SQL为:SELECT DISTINCT `status` FROM `user`。
这一步操作起来很简单,直接链式调用即可,但注意【distinct()必须放在select()之后、all()之前】,否则会被忽略——若写成User::find()->distinct()->select('status')->all(),DISTINCT仍有效;但若写成User::find()->all()->distinct(),则完全无效,因为all()已执行并返回PHP数组,后续调用无意义。
字段级去重:select()内嵌Expression实现多列组合唯一
当需对多个字段组合去重(如name+email联合唯一),仅靠distinct()不够,必须配合Expression绕过ActiveRecord对字段名的自动转义。
方法一:用new yii\db\Expression包裹原始SQL片段
User::find()->select(new yii\db\Expression('DISTINCT name, email'))->from('user')->asArray()->all();此写法强制生成SELECT DISTINCT name, email FROM user,不加反引号包裹,避免字段别名被误解析。
方法二:在select()中传入数组并混用Expression
User::find()->select([new yii\db\Expression('DISTINCT CONCAT(name, "_", email) AS uniq_key'), 'id'])->from('user')->asArray()->all();该方式适用于需保留主键同时按组合字段去重的场景,但注意【Expression中的DISTINCT不能与普通字段混在同一数组层级,否则会被忽略】。
关联查询去重:with() + distinct()的正确配合顺序
涉及关联表(如User→Profile)时,若直接User::find()->with('profile')->distinct()->all(),DISTINCT作用于主表字段,无法消除因JOIN导致的笛卡尔积重复。
第一步:先明确去重目标——是主表记录唯一,还是关联后整行唯一?
第二步:若目标是主表ID唯一(即每个User只出现一次,不管Profile有多少条),则必须用innerJoinWith替代with,并手动指定JOIN条件,再加distinct():
User::find()->innerJoinWith('profile')->select('user.id, user.name')->distinct()->asArray()->all();此写法确保生成SELECT DISTINCT user.id, user.name FROM user INNER JOIN profile ON user.id = profile.user_id。
第三步:若需保留所有关联数据但去重主表,改用group by代替distinct()更稳妥,例如->groupBy('user.id'),避免MySQL严格模式下DISTINCT与非聚合字段共存报错。











