shineraj's avatar

Compare sum of relational column

Hi, Can i compare sum of the relationship column with my parent table column without using joins ?

0 likes
10 replies
squiaios's avatar

Something like this ?

$countA = $model->relations()->where('column', '=', 'a')->count();
$countB = $model->relations()->where('column', '=', 'b')->count();

$countA < $countB ...
shineraj's avatar

@squiaios No, I have 1M records, I need to compare parent column value and the sum of a relational column by query

MarianoMoreyra's avatar

Hi @shineraj

There is no solution that won't internally use joins, although if what you mean is just using Eloquent and no Query Builder joins, you might try something like this:

Post::has('comments', '>=', DB::raw('posts.column_to_compare'))->get();

Of course I'm using Post and Comments and a >= operator as an example in this case as you didn't mentioned any models or the type of comparison that you want to do.

Also, if you want to get the relationship count as a field too, you can do:

Post::withCount('comments')
    ->has('comments', '>=', DB::raw('posts.column_to_compare'))
    ->get();

Hope this works for you!

2 likes
shineraj's avatar

Hi @marianomoreyra ,

I like your example.

my exact case is :

User Model has column "amout_to_be_paid" & User has multiple payments in Payment model.

I need to compare if the user paid the amount in full or not.

I wrote a Hasmany relation From User to Payment.

In this case what is your idea to resolve this ?

Please not i have 1M records & this is a case of filter to dril down user list to fully paid & not paid!

kingmaker_bgp's avatar

Hi @shineraj, you can achieve this using Nested WHERE Clause in the Eloquent Query.

use Illuminate\Database\Query\Builder;
use Illuminate\Support\Facades\DB;
use App\Model\User;

$fully_paid_users = User::where(function (Builder $query) {
		return $query->selectRaw('SUM(`amount`)')
			->from('payments')
			->whereColumn('user_id', 'users.id');
	}, DB::raw('amount_to_be_paid'))
	->get();

This works only on Laravel 7.x & above

Documentation

MarianoMoreyra's avatar

I'm sorry @shineraj

I got mislead by a previous reply that talked about counts, but you needed sums, in which case my previous solution won't work.

In addition to @kingmaker_bgp solution, you can try with other options to get only those that are fully paid, and in another query those that are not.

fully paid

User::query()
    ->addSelect(['paid' => App\Payment::selectRaw('SUM(amount)')
        ->whereColumn('user_id', 'users.id')])
    ->having('amount_to_be_paid' , '=', DB::raw('paid'))
    ->get();

and for those with pending payments

User::query()
    ->addSelect(['paid' => App\Payment::selectRaw('SUM(amount)')
        ->whereColumn('user_id', 'users.id')])
    ->having('amount_to_be_paid' , '>', DB::raw('paid'))
    ->get();

Both are similar solutions, so it's just a matter of taste! :)

MarianoMoreyra's avatar

@shineraj also you can just get the amount to pay (difference between both fields) so you can then show those with a value of 0 as fully paid:

User::query()
    ->addSelect([
        'to_pay' => App\Payment::selectRaw('users.amount_to_be_paid - SUM(amount)')
            ->whereColumn('user_id', 'users.id')
        ])
    ->get();

I think this is cleaner than my last reply and allows you to know the amount left in each case

mitsyara's avatar

@MarianoMoreyra Hi there. I'm just having a little bit complex to this problem, hope you can help. I'm trying to get the sum of 2 child table, then comparing them using having() method. It worked.

But then, when I'm trying to add another where condition after that, it doesn't work.

For ex: I have the User, which has many Order, each Order has its amount to paid (based on quantity and price), and then, each Order has many Payments. I can return the result filter using having method with total_to_paid <=> total_paid. But when the User didn't even had any Payment yet, I need to return that as well. But it seems like it doesn't work.

I've tried like this:

$query->where(...something...) //The part where some pre condition to applied
  ->withSum('shipments as total_to_paid', 'total_value')
  ->withSum('payments as total_paid', 'amount')
  ->havingRaw('total_paid < total_to_paid') //The part when comparing those total sum
  ->orWhereDoesntHave('payments') //The part where I wish the result returning the null Payment

Please or to participate in this conversation.