Doctrine / SQL query MAX with GroupBy - php

My goal is to select the column with the highest createdAt, grouped by a user.
If i do the following:
$qb = $this->createQueryBuilder('qb1');
$q = $qb
->select('pbs, MAX(pbs.createdAt) AS HIDDEN pbs_created')
->add('from', 'MyEntity pbs')
->groupBy('pbs.user')
->orderBy('pbs_created', 'ASC')
->getQuery();
return $q->getResult();
I get:
+------+---------------------+---------------------+--------------+
| id_0 | createdAt_3 | sclr_5 | project_id_7 |
+------+---------------------+---------------------+--------------+
| 2137 | 2014-10-07 10:52:29 | 2017-04-25 15:42:42 | 116 |
+------+---------------------+---------------------+--------------+
+------+---------------------+---------------------+--------------+
| 5123 | 2015-11-02 15:02:55 | 2017-05-02 05:36:18 | 118 |
+------+---------------------+---------------------+--------------+
The Problem is that i get the max value but the returned row contains not the value from max.
How can i get the newest createdAt grouped by a user with associated entity data?

It has nothing todo with ordering.
MAX() gives you the maximal value but not the row with maximal value
Look at these SO questions
Retrieving the last record in each group
Returning the 'last' row of each 'group by' in MySQL
The problem is - both solutions are pretty hard to achieve with Doctine's QueryBuilder.
Subqueries are possible though - http://melikedev.com/2013/06/07/php-doctrine-dql-select-subquery/

Have you tried to change the orderBy?
Like this:
->orderBy('pbs_created', 'DESC')

Related

How to sum a colum from a related model efficiently on Laravel

Ok I got this table
affiliates_referral_clicks
id | affiliate_id | clicks | date
1 | 1 | 10 | 2021-07-14
2 | 1 | 2 | 2021-07-11
3 | 2 | 1 | 2021-07-11
4 | 2 | 14 | 2021-07-10
...
Of course my Model Affiliate has a relationship with referralClicks
Affiliate.php
public function referralClicks(){
return $this->hasMany(AffiliateReferralClick::class,'affiliate_id');
}
Now I want to bring all Affiliates with the SUM of all their clicks that have a date between a given date. I implemented it like this
$affiliate = Affiliate::with(['referralClicks' => function($query) use($params) {
$query->whereDate('date','>=', $params['dateFrom'])
->whereDate('date','<=', $params['dateTo'])
->select('clicks')
;
}])->get();
foreach ($affiliates as $affiliate){
$affiliate->totalClicks = $affiliate->referralClicks->sum('clicks');
}
this works fine, but since the affiliates_referral_clicks table is waaaay too big and the request ends up being too slow, I think if you do the query without using Eloquent's helpers you can get a much faster query.
So my question would be...how can I do the same I just did but with raw querys (or whatever the most efficient way is)? Im using a MySQL DB I hope you guys can help me!
Haven't tried that yet but that's how I'd solve this (if we assume, you only need the sum and nothing else from the relationship):
$affiliate = Affiliate::withSum(['referralClicks.clicks as totalClicks' => function($query) use($params) {
$query->whereDate('date','>=', $params['dateFrom'])
->whereDate('date','<=', $params['dateTo'])
->select('clicks')
;
}])->get();

Laravel orderBy with same value result keep changing

I've try to sort my result within the updated_at value. Because I seed the value all the updated_at value is same:
// Amount Table
-----------------------------------------------------
| id | amount | updated_at |
+----------+--------------------+-------------------+
| 1 | 232319 |2016-02-02 13:17:29|
| 2 | 232319 |2016-02-02 13:17:29|
| 3 | 100000 |2016-02-02 13:17:29|
| 4 | 231111 |2016-02-02 13:17:29|
| 5 | 12345 |2016-02-02 13:17:29|
+----------+--------------------+-------------------+
The problem is everytime I query :
$lists = Amount::orderBy('updated_at', 'DESC')->lists('id')->toArray();
I expect the result to be like this: [1,2,3,4,5] but it appear the result is sometime like that but sometime [2,3,1,4,5]. it keep randoming the result.
As far as i know, yes i can put another orderBy statement to the query, but isn't it suppose to be default it will order by the id?
Thanks
You are only ordering by updated_at which appear to all have the same value. If you want consistency, include a second column
$lists = Amount::orderBy('updated_at', 'DESC')->orderBy('id', 'ASC')->lists('id')->toArray();

Calculate sum using Laravel Query Builder by applying groupBy on Table1.id but without soft deleted rows on Table2?

I want to get all the purchases and their sums and also I don't want to add the amount if payments.deleted_at is not null.
Here are the tables
purchases
id | name
1 | Gamerzone Book
2 | Recipe Book
3 | EngineX Book
payments
id | purchase_id | amount | deleted_at
1 1 100 2015-06-12 11:00:00
2 2 50 NULL
2 2 10 NULL
Code
$query = DB::table('purchases')
->select(['purchases.*',
DB::raw("IFNULL(sum(payments.amount),0) as total")
])
->leftJoin('payments','payments.purchase_id','=','purchases.id')
->whereNull('payments.deleted_at')
->groupBy('purchases.id')->get();
When I run the code below the 1st result is not included.
Result
id | name | total
2 | Recipe Book 60
3 | EngineX Book 0
I know why It is not included but the problem is if I remove whereNull('payments.deleted_at') that particular row in payments
will also add to the sum.How should I solve this ??
Expected Result
id | name | total
1 | Gamerzone Book 0
2 | Recipe Book 60
3 | EngineX Book 0
In this case your join condition should looks like this:
ON (payments.booking_id = purchases.id AND payments.deleted_at IS NOT NULL)
And it is not about WHERE (according to your SELECT).
You should use join-closure like this:
$query = DB::table('purchases')
->select(['purchases.*', DB::raw("IFNULL(sum(payments.amount),0) as total")])
->leftJoin('payments', function($join) {
$join->on('payments.booking_id', '=', 'purchases.id');
$join->on('payments.deleted_at', 'IS', DB::raw('NOT NULL'));
})
->groupBy('purchases.id')->get();
Just replace
->leftJoin('payments','payments.booking_id','=','purchases.id')
with
->leftJoin('payments', function($join) {
$join->on('payments.booking_id', '=', 'purchases.id');
$join->on('payments.deleted_at', 'IS', DB::raw('NOT NULL'));
})
and remove this:
->whereNull('payments.deleted_at')
it should help.

Selecting the latest row using groupby in Laravel

I have a table entries similar as follows:
+---------+---------+----------+
| Test_id | User_id | Attempts |
+---------+---------+----------+
| 12 | 5 | 1 |
| 13 | 5 | 1 |
| 12 | 5 | 2 |
+---------+---------+----------+
Now I want to select the elements group by test_id and should get the latest entry.
I tried this query:
$tests_took = Testresult::where('course_id', $courseId)
->where('user_id', Auth::id())
->groupby('test_id')
->orderBy('attempts', 'desc')
->get();
When I display the result, I'm getting the first two rows only (only one row for one test_id - which I what I want.) But instead of the last row for the test_id=12, it shows the first row. I always want the biggest attempt to be displayed.
My current output is like:
| 12 | 5 | 1 |
| 13 | 5 | 1 |
But I want this:
| 12 | 5 | 2 |
| 13 | 5 | 1 |
How can I achieve this? to get the latest row as the first array element when I use groupby or is there any other way to do this?
ORDER BY and GROUP BY don't work very well together...
If you simply want the highest attempt per test_id i suggest using the MAX() function:
$tests_took = Testresult::select('test_id', 'user_id', DB::raw('MAX(attempts) AS max_attempts'))
->where('course_id', $courseId)
->where('user_id', Auth::id())
->groupby('test_id')
->get();
You may use the following lines:
Testresult::select('*')
->join(DB::raw('(Select max(id) as last_id from Testresult group by test_id) LatestId'), function($join) {
$join->on('Testresult.id', '=', 'LatestId.last_id');
})
->orderBy('attempts', 'desc');
}

Laravel 4 Query Builder (Joins)

I have these tables in my database, namely "airport" and "route", the id of "airport" is a foreign key in "route" (i.e. Origin, Destination).
Airport
+-------+-------------+-----------------------+
| id | airportcode | Location |
+-------+-------------+-----------------------+
| 1 | CEB | Cebu |
| 2 | MAN | Manila |
+-------+-------------+-----------------------+
Routes
+-------+-------------+-----------------------+
| id | Origin | Destination |
+-------+-------------+-----------------------+
| 1 | 1 | 2 |
| 2 | 2 | 1 |
+-------+-------------+-----------------------+
So far, this is my query in my Controller and it's only returning the "Origin, Destination"
DB::table('airport')
->join('route', 'airport.id','=','route.Origin')
->join('route', 'airport.id','=','route.Destination')
->select('route.Origin', 'route.Destination')
->get();
What I would like to do is this:
SELECT 'airport.Location' from airport, route WHERE 'route.Origin' = 'airport.id' AND 'route.Destination' = 'airport.id".
Any suggestions will do!
So - you want to pull out the model for a specific airport id but only if it goes to the specified destination?
Your first query will only return the two columns as that's what you told it to return
You can get the airport easily by:
Airport::find($id);
Where $id is the id from a user input for example and should be the key. Find will return a collection
You could also do:
Airport::where('id','=', $id)->first() //will return the first record - you could also use ->get() to return a collection
Then if you have a join in your Airport model such as ->hasMany you could then do:
Airport::where('id','=', $id)
->with('routes')
->get()
Which will return the airport with the related routes model attached to it
You can then take that a stage further and query the relationship by:
Airport::find($id)->routes()->where('destination','=',$dest_id);
I think that should do the trick - as long as you create the relationship correctly in the models
If you are using a select query make sure that you have mentioned all the fields you want...
it's only returning the "Origin, Destination" because you have mentioned only those two in your select query.
try something like...
DB::table('route')
->select('route.Origin', 'route.Destination','airport.Location')
->leftjoin('airport', function($join)
{
$join->where('airport.id',array('route.Origin','route.Destination'));
// I haven't used it, if any errors pls comment
})
->get();
hope this helps you...

Categories