Yii2单查询实现:按created筛选用户并独立统计fail_date范围内cons_debit
在Yii2中单个查询实现多条件统计(不受主筛选条件约束)
你当前的问题核心是主查询通过created条件过滤了数据集,导致cons_debit的统计只能基于过滤后的行,无法覆盖该user_id所有行中fail_date符合条件的数据。要在单个查询中实现需求,需要调整查询逻辑,确保user_id的筛选基于created条件,但统计cons_debit时使用该user_id的全量数据。
方案一:子查询筛选目标user_id,关联全量数据统计
先通过子查询获取所有created在时间范围内的user_id,再基于这些user_id统计两个指标:
// 子查询:获取符合created时间条件的user_id(去重) $userSubQuery = self::find() ->select('mrd.user_id') ->where(['>=', 'mrd.created', $start_date]) ->andWhere(['<=', 'mrd.created', $end_date . ' 23:59:59']) ->groupBy('mrd.user_id'); // 主查询:统计指定user_id的两个指标 $result = self::find() ->select([ 'mrd.user_id', // 统计created在范围内的actual_revenue总和 'total_revenue' => new \yii\db\Expression( 'SUM(CASE WHEN mrd.created BETWEEN :start_date AND :end_date_full THEN mrd.actual_revenue ELSE 0 END)', [ ':start_date' => $start_date, ':end_date_full' => $end_date . ' 23:59:59', ] ), // 统计fail_date在范围内的cons_debit总和(不受created约束) 'cons_debit_total' => new \yii\db\Expression( 'SUM(CASE WHEN mrd.fail_date BETWEEN :start_date AND :end_date_full THEN mrd.cons_debit ELSE 0 END)', [ ':start_date' => $start_date, ':end_date_full' => $end_date . ' 23:59:59', ] ) ]) ->where(['in', 'mrd.user_id', $userSubQuery]) ->groupBy('mrd.user_id') ->offset(max($params['page'] - 1, 0) * $params['limit']) ->orderBy(['total_revenue' => SORT_DESC]) ->limit($params['limit']) ->asArray() ->all();
方案二:使用HAVING过滤user_id,无需子查询
通过条件聚合统计total_revenue,同时用HAVING子句确保user_id至少有一行created在时间范围内,这样cons_debit的统计可以覆盖该user_id的全量数据:
$result = self::find() ->select([ 'mrd.user_id', 'total_revenue' => new \yii\db\Expression( 'SUM(CASE WHEN mrd.created BETWEEN :start_date AND :end_date_full THEN mrd.actual_revenue ELSE 0 END)', [':start_date' => $start_date, ':end_date_full' => $end_date . ' 23:59:59'] ), 'cons_debit_total' => new \yii\db\Expression( 'SUM(CASE WHEN mrd.fail_date BETWEEN :start_date AND :end_date_full THEN mrd.cons_debit ELSE 0 END)', [':start_date' => $start_date, ':end_date_full' => $end_date . ' 23:59:59'] ) ]) ->groupBy('mrd.user_id') // 过滤出至少有一行created在时间范围内的user_id ->having(['>', 'SUM(CASE WHEN mrd.created BETWEEN :start_date AND :end_date_full THEN 1 ELSE 0 END)', 0]) ->addParams([ ':start_date' => $start_date, ':end_date_full' => $end_date . ' 23:59:59' ]) ->offset(max($params['page'] - 1, 0) * $params['limit']) ->orderBy(['total_revenue' => SORT_DESC]) ->limit($params['limit']) ->asArray() ->all();
关键说明
- 两种方案都避免了在主查询
WHERE中直接过滤created条件,确保cons_debit统计可以访问该user_id的所有行数据。 - 方案一适合数据量较大的场景,子查询可以快速筛选出目标
user_id,减少主查询处理的数据量;方案二更简洁,无需额外子查询。
内容的提问来源于stack exchange,提问作者Stern
相关产品推荐
相关产品推荐

