laravel ->count() vs ->get()->count() - laravel

Why are the two statement below behaving differentlY? The first returns 3 the second returns 1 for $progress. I thought that the first aggregates on the DB and the second on the server. So they should still return the same value.
$progress = $this->user->userActivities()->select('date')
->groupBy('date')
->get()->count();
$progress = $this->user->userActivities()->select('date')
->groupBy('date')
->count();

->get()->count() will load Eloquent model objects into memory and then will count those.
->count() will use DB aggregate function, so it will definitely be more efficient:
select count(*) as aggregate ...

It's quite late answer but I had faced the same issue. These two examples are returning two different counts because of groupBy you have used.
$progress = $this->user->userActivities()->select('date')
->groupBy('date')
->get()->count();
This takes the rows first, then count the row numbers and returns the count.
$progress = $this->user->userActivities()->select('date')
->groupBy('date')
->count();
Whereas this counts the rows by its group in the DB and returns the first group's row record count only from the list.
the solution I used instead of get()->count() for my case is like
$progress = $this->user->userActivities()
->distinct('date')
->count('date');

Use ->get()->count() is wrong. I used ->get()->count() in my web app and when my database records have more than 80000 records I get error 500 in my app.

The first counts the number of returned records, the second counts the records and returns the number. If you are using a paging of 10, for example, then the first will yield 10 and the second the actual number if the number of mathcing elements is greater than 10.

$count = Model::all()->count();
return view('home',compact('count'));
this will function for laravel 6;

Both of them return the same result. But as said for preventing to crash or decrease the speed of response it's better to use count() instead of get()->count()

Related

How do I get both a COUNT() and SUM() LEFT JOIN in Laravel Eloquent?

I'm pretty new to joins so excuse me as I get my head round it! I'm trying to join 2 tables onto a links table.
One table is clicks. I want the SUM of any data in the clicks.clicks column where link_id matches. (ie 2 columns, One with 1 in the column and a second with 4 in the clicks column both for link_id=1 would return 5)
And the second table is suggestions which I want the COUNT of any occurrence of link_id to be displayed (ie 4 rows where link_id=1 would return 4)
I'm using eloquent with 2 left joins for this and have managed to get both working independently, however when I put both together, clicks_sum which is the name of my SUM join goes wildly high (Seeming as if the suggestion_count is interfering with what's going on there)
Here's my code so far for what I'm trying to achieve. I'm loading Link ID 2872719 just to test with. It should return 585 for suggestion_count and 4 for clicks_sum but I am getting totally different results than expected
use App\Models\Link;
use App\Models\Suggestion;
return Link::select(
'links.id',
DB::raw('SUM(clicks.clicks) AS click_sum'),
DB::raw('COUNT(suggestions.link_id) AS suggestion_count'),
)
->leftJoin('clicks', 'clicks.link_id', '=', 'links.id')
->leftJoin('suggestions', 'suggestions.link_id', '=', 'links.id')
->where('links.id', 2872719)
->first();
Returned is the following:
App\Models\Link {#1278
id: 2872719,
click_sum: "2340", // Should be 4
suggestion_count: 585, // The presence of this join appears to affect click_sum
}
Any ideas on where I am going wrong?
All my best!
Rather than having two leftJoin instances in your eloquent query you need to combine the two joins together into one leftJoin.
return Link::select(
'links.id',
// get the count of suggestions for each link
DB::raw('(SELECT COUNT(*) FROM suggestions WHERE suggestions.link_id = links.id) AS suggestion_count'),
DB::raw('SUM(clicks.clicks) AS click_sum'),
)
->leftJoin('clicks', 'clicks.link_id', '=', 'links.id', 'suggestions.link_id', '=', 'links.id')
->where('links.id', 2872719)
->groupBy('links.id')
->first();
This should give you the correct result as below:
#original: array:3 [▼
"id" => 2872719
"suggestion_count" => 586
"click_sum" => "4"
]
When performing two leftJoin as you were previously, the click_sum was being multiplied by the total count of suggestions leading to the inflated number being returned.

Laravel - How to manual paginate but with max number of total count?

Currently, I have a lot of Joins and SubJoins with Group By etc.
To make it short: I cannot use paginate, because it is too slow (because of Group by). That is why I need to use pagination manually, however, I need to limit the total number of rows as well. With rows way above 10k, I want to have the maximum of a total of 300!
My current approach works suboptimal and I don't really know how to fix it:
return \DB::table('offers')
->alotOfQueries/joins
->...
->limit(300) //we show only a maximum of 300 offers
->get();
After the return, I'm manually creating the pagination.
I'll just leave the code as well here:
protected function paginate(Collection $offerings): LengthAwarePaginator
{
$page = request()->page ?? 1;
$perPage = $this->config->take;
$offset = ($page * $perPage) - $perPage;
return new LengthAwarePaginator(
array_slice($offerings->all(), $offset, $perPage, true),
$offerings->count(),
$perPage,
$page,
[
'path' => request()->url(),
'query' => request()->query()
]
);
}
This current approach is much faster than using paginate.
But my problem is it is still loading 300 rows.
My goal is to load only 18 rows per page. And calculate the correct total until it hits 300. It won't go above.
The numbers are taken from Airbnb, so I can show you this picture, so you maybe know what I'm after at:
Airbnb is always limiting it's max total to 300. Because 18 rows are loaded, we get 17 pages.
Any idea how to solve this?
Just for you, readers in the future. I'm lucky in that sense that I could limit my sub join ->limit(300)
With that I could now use paginate. Now it is not slow anymore at all!
I tested it against manual pagination like with:
return $offersQuery->offset($this->offset)
->limit($this->perPage)
->get();
and it does not matter what you are going to use.

Laravel get row with records above and below it

I have a Laravel 4.2 project where I get data from a SQL DB and I can display onto the page. I can select the single record just fine, but I want to also show the records around the one selected.
For example, I want to show the 5 records above and below the one selected. Im not sure how to do this in Laravel.
$gradschoolrange = MOGRadschool::where('Title', '=', $gradschool)->get();
In the above example $gradschool might be "Test College", it will return that with a value, but I want to show all the other related records around it with those values too. The results should look something like this:
ABC College
Another College
Blah College
Go To College
Test College
Yet Another College
Yo Yo College
College College
Something College
Eating College
As there's no ordering specified in your initial query, I'm assuming you want 5 next/previous records according to primary key (id? - if not, you would obviously need to change that) in the table?
Given that IDs may not be numerically sequential, we can't simply assume that the previous 5 rows will be the ID of the row with title = $gradschool minus 5, so wondered if this might work:
$initial = MOGRadschool::where('Title', $gradschool)->first(); // get the initial row with the title of $gradschool
$result = MOGRadschool::where('id', '<', $initial->id)->take(5)->orderBy('id', 'DESC') // new query getting the previous 5 rows, by ID
->union(MOGRadschool::where('id', '>', $initial->id)->take(5)) // union a second query getting the next 5 rows by ID
->get() // get the result as a collection
->add($initial) // add the initial row to the collection
->sort(); // sort the collection (by id) so that the initial row is in the middle
So the output is a collection containing the initial row in the middle, with up to 5 records either side. You also have the initial row to highlight the output, if you need that.
If you want it based on the IDs, which is what I understand from your issue, something like this should work:
$selectedGradSchool = MOGRadschool::where('Title', '=', $gradschool)->get()->first();
$aboveSelected = MOGRadschool::where('id', '<=', $selectedGradSchool->id)
->orderBy('id', 'desc')
->take('5')
->get();
$belowSelected = MOGRadschool::where('id', '>' $selectedgradSchool->id)
->take('5')
->get();
//Concatenate both results
$schoolRange = $aboveSelected->concat($belowSelected);
Now the collection should look similar to your desired result.

Extract more rows from a left join with Laravel

I have to extract the rows where the created_at is inside the week. Unfortunately, only one line is extracted from me and no more lines as I expected. Why?
Query:
$scadenze = DB::table('processi')
->leftJoin('scadenze', 'processi.id', '=', 'scadenze.processo_id')
->where('responsabile',$utente->id)
->whereNotIn('scadenze.stato', [4,5])
->whereBetween('scadenze.termine_stimato',[\Carbon::now()->startOfWeek(), Carbon::now()->endOfWeek()])
->avg('tempistica');
This query extract just one row, but in reality many more lines should be extracted.
Because ->avg('tempistica'); return average value from all your rows in this query, i.e. return just one value.
Solution:
I was wrong to use the avg with sum function. The rows were extracted correctly but instead of being added (by timing) an average was made. Thank you all for your help.

Laravel sum on ->first() sums more than 1 result

I have the following eloquent query
$raw = Model::select('out', 'in')->orderBy('created_at', 'DESC')->first();
That returns a collection of a single item, where Out = 0.0 and In = 90.0.
If I then do this:
$sumO = $raw->sum('out');
$sumI = $raw->sum('in');
I get $sumO = 13,651.41 and $sumI = 13371.69
I don't understand, because those sums don't even equal the sum of my entire table for those colums.
But it seems like->sum() is being called on the entire table/query instead of just the first result like I thought it would.
Now, I know sum of a single row is weird, and I'm not actually doing this in production. I just want to know what it is doing.
Shouldn't it still just sum the 1 number to equal itself?
It's just one row when using ->first(), so there's no need to use ->sum() just use $raw->in and $raw->out.
Also, ->sum() used with a single column at a time.

Resources