请教一下,三个模型的关联查询,好像挺复杂的。

请教大家一个问题,好像很复杂:

合同模型:Contract
订单模型:Order
收款模型:Receipt

1,一个合同,关联多个订单,关联的多个订单的总金额加起来,是这个合同的总额。
2,一个合同,关联多条收款。关联的多条收款总额加起来,是这个合同总共目前收回来的款。

现在需要查询,10条 ( 收款总额没达到订单总额 )的合同

这个得怎么查呢?

乌鸦嘴新手社区 wyz.xyz 为技术新手提供服务
《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。
《L04 微信小程序从零到发布》
从小程序个人账户申请开始,带你一步步进行开发一个微信小程序,直到提交微信控制台上线发布。
最佳答案

需要用子查询解决,但是这种查询性能比较差,如果经常用到这种查询建议添加两个字段保存统计金额数据到合同表

Contract::query()
            ->withCount(['orders' => function ($query) {
                $query->select(DB::raw("sum(amount)"));
            }])
            ->withCount(['receipts' => function ($query) {
                $query->select(DB::raw("sum(amount)"));
            }])
            ->havingRaw('CAST(orders_count AS DECIMAL(10, 3)) < CAST(receipts_count AS DECIMAL(10, 3))')
            ->get();
3年前 评论
shebaoting (楼主) 3年前
讨论数量: 7

需要用子查询解决,但是这种查询性能比较差,如果经常用到这种查询建议添加两个字段保存统计金额数据到合同表

Contract::query()
            ->withCount(['orders' => function ($query) {
                $query->select(DB::raw("sum(amount)"));
            }])
            ->withCount(['receipts' => function ($query) {
                $query->select(DB::raw("sum(amount)"));
            }])
            ->havingRaw('CAST(orders_count AS DECIMAL(10, 3)) < CAST(receipts_count AS DECIMAL(10, 3))')
            ->get();
3年前 评论
shebaoting (楼主) 3年前

楼上大佬已经写的很清楚了,我这里再补充一下,最新版Laravel提供了新方法的withSum()(此方法是自 Laravel 8.12 新增):

$res = \App\Models\Contract::query()
  ->withSum('hasManyOrders', 'money')
  ->withSum('hasManyReceipts', 'money')
  ->havingRaw('has_many_receipts_sum_money < has_many_orders_sum_money')
  ->get();
dd($res);

测试结果:

file

分页什么的,正常加就行了。
再补充一下吧,如果你的Laravel版本低,那再给你写个addSelect()的方式:

$res = \App\Models\Contract::query()
  ->addSelect([
    'receipts_money' => \App\Models\Receipt::whereColumn('receipts.contract_id', 'contracts.id')->selectRaw('SUM(money)'),
    'orders_money' => \App\Models\Order::whereColumn('orders.contract_id', 'contracts.id')->selectRaw('SUM(money)'),
  ])
  ->havingRaw('receipts_money < orders_money')
  ->get()
  ->toArray();
3年前 评论
shebaoting (楼主) 3年前

@LiamHao 大神, 为了提高查询效率,我准备在合同表里面,增加一个收款总额(Total))字段。但是因为是新增的字段,之前的合同数据这个字段都是空的。需要把之前已有的收款记录计算一下总额,然后更新到Contract表的总额字段中(Total)中。

这个update该怎么写呢?

3年前 评论
LiamHao 3年前
shebaoting (作者) (楼主) 3年前

@牛铁柱 洗数据的 SQL ,将receipts表中相同contract_id的数据对money字段求和,然后更新到contracts表中:

UPDATE `contracts` 
LEFT JOIN ( 
    SELECT 
        SUM(`receipts`.`money`) AS `receipts_total`, 
        `receipts`.`contract_id` 
    FROM `receipts` 
    GROUP BY `receipts`.`contract_id`
) AS `tmp_receipts` ON `tmp_receipts`.`contract_id` = `contracts`.`id`
SET `contracts`.`total` = `tmp_receipts`.`receipts_total`

参考下吧,by a way: 200多声望都算「新手」?

3年前 评论
shebaoting (楼主) 3年前

更新冗余字段,如果完全没有数据库基础,其实也可以用笨办法, 就把所有订单选择出来,根据合同ID,循环累加后,再循环一次,更新进去。

有点基础就用 group by 合同ID ,sum 出来。再循环一次,更新进去。

这样又好理解,又好记忆。 左联更新加子查询,估计你会一脸懵逼。

3年前 评论
shebaoting (楼主) 3年前

你说你是新手,那提醒你一下,更新收款总额的时候,不要用PHP计算然后执行update,要用 increment,转换成SQL就是

UPDATE TABLE SET total = total + 本次收款金额 WHERE xxx

防止并发导致收款额错误

3年前 评论

@lengqy 被你说中了,我的确是用orm查询出来所有数据然后 sum('total')。最后save的。我学一下你的方式。谢谢指教。

3年前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!