Laravel 9 withSum查询报错:where子句中credit_balance列不存在
问题解决:Laravel 9中withSum后无法用where过滤聚合字段的问题
错误原因
你用withSum生成的credit_balance是Eloquent在主查询执行完成后,对每个模型的关联数据做求和计算得到的字段,这个字段并不存在于原始SQL查询中,所以直接用where('credit_balance', '<', 100)会触发数据库报错,因为数据库找不到这个列。
正确实现方式
方式一:使用having过滤聚合结果
having是SQL中专门用于过滤聚合函数结果的语句,Laravel支持直接用having来过滤withSum生成的别名:
/** * 获取所有可自动充值的密钥 */ public function getEnabledTopUpKeys() { return PersonalAccessToken::whereNotNull('region_code') ->where('auto_topup_enabled', true) ->whereNotNull('auto_topup_slug') ->withSum('credit_transactions AS credit_balance', 'delta') ->having('credit_balance', '<', 100) ->get(); }
如果上述写法在某些Laravel版本中不生效,可以改用havingRaw确保语法正确:
->havingRaw('credit_balance < ?', [100])
方式二:用子查询提前计算聚合值并过滤
如果需要在SQL层面直接完成过滤(避免事后集合过滤的性能损耗),可以用子查询在select中计算余额,再通过having过滤:
/** * 获取所有可自动充值的密钥 */ public function getEnabledTopUpKeys() { return PersonalAccessToken::whereNotNull('region_code') ->where('auto_topup_enabled', true) ->whereNotNull('auto_topup_slug') ->selectRaw('personal_access_tokens.*, (SELECT SUM(delta) FROM credit_transactions WHERE credit_transactions.personal_access_token_id = personal_access_tokens.id) AS credit_balance') ->having('credit_balance', '<', 100) ->get(); }
这种方式的优势是聚合计算和过滤都在一次SQL查询中完成,性能更优,同时也能直接返回credit_balance字段。
方式三:结合whereRaw和子查询过滤
如果你只需要过滤结果,同时还要保留withSum生成的字段,可以在whereRaw中嵌入子查询:
/** * 获取所有可自动充值的密钥 */ public function getEnabledTopUpKeys() { return PersonalAccessToken::whereNotNull('region_code') ->where('auto_topup_enabled', true) ->whereNotNull('auto_topup_slug') ->whereRaw('(SELECT SUM(delta) FROM credit_transactions WHERE credit_transactions.personal_access_token_id = personal_access_tokens.id) < ?', [100]) ->withSum('credit_transactions AS credit_balance', 'delta') ->get(); }
内容的提问来源于stack exchange,提问作者Ryan H
相关产品推荐
相关产品推荐

