MySQL JOIN and LIMIT TO 1 - php

Almost perfect code... but It's getting duplicates from actions_logs table because every user have multiple login entries for history purpose.
I've tested a lot of options but ther's no working example on the web.
select `users`.* from `users` left join `action_logs` on `action_logs`.`user_id` = `users`.`id` and `action_logs`.`type` = 'login' order by `action_logs`.`created_at` desc, `id` desc
Expected result will be full user list sorted by loggedin date.
That sql is a result of Laravel code:
if($value == 'last_login') {
return $this->builder->select('users.*')
->leftJoin('action_logs', function ($join) {
$join->on('action_logs.user_id', '=', 'users.id')
->where('action_logs.type', '=', 'login');
})->orderBy('action_logs.created_at', $this->request->get('sort'))
->orderBy('id', $this->request->get('sort'));
}

You should used DISTINCT Query but in order for you to remove duplicate results you should remove the "created_at" in the select since every logged in is created at different time.

Related

Laravel OrderBy by related table column

I have a table (A) that has a One to Many relation with another table (B).
I want to query Table A and eager load Table B with the Table A results - but I also want to sort Table A by a value in Table B.
I have tried using OrderBy in the query and also trying SortBy on the resultant collection but cannot get the Table A data to be sorted by the value found in Table B.
Example of what I have tried:
$query = ModelA::with("ModelB"])->get()->sortByDesc('ModelB.sortValue');
Keep in mind, I am only interested in the LATEST record from Table B. So I need to query Table A and sort by a value in the LATEST records of Table B.
How can I achieve this?
EDIT:
The below (as suggested by #ljubadr) works pretty close, but the issue is that there are many record in Table B which means that it doesn't reliably sort as it doesn't seem to sortby the latest records in Table B. Can I have the join return ONLY the latest record for each ID?
$query = ModelA::select('TableA.*')
->join('TableB', 'TableA.id', '=', 'TableB.col_id')
->groupBy('TableA.id')->orderBy('TableB.sortCol', 'desc')
->with(['x'])
->get();
EDIT 2:
#Neku80 answer has gotten me closest but it seems to not sort the column with the greatest accuracy.. I'm sorting a Decimal column and for the most part it is in order but in some places the items are out of order..
$latestTableB = ModelB::select(['TableA_id', 'sortByColumnName'], DB::raw('MAX(created_at) as created_at'))
->groupBy('TableA_id');
$query = ModelA::select('TableA.*')
->joinSub($latestTableB, 'latest_TableB', function ($join) {
$join->on('TableA.id', '=', 'latest_TableB.TableA_id');
})
->orderBy('latest_TableB.sortByColumnName')
->get();
For example, the ordering is like:
0.0437
0.0389
0.0247 <-- -1
0.025 <-- +1
0.0127
When I delete all rows except for the 'latest' rows, then it orders correctly, so it still must be ordering with old data...
I have found a solution:
ModelA::select('TableA.*', 'TableB.sortByCol as sortByCol')
->leftJoin('TableB', function ($query) {
$query->on('TableB.TableA_id', '=', 'TableA.id')
->whereRaw('TableB.id IN (select MAX(a2.id) from TableB as a2 join TableA as u2 on u2.id = a2.TableA_id group by u2.id)');
})
->orderBy('TableB.sortByCol')
->get();
Another alternative to order is like this:
$users = User::orderBy(
Company::select('name')
->whereColumn('companies.user_id', 'users.id'),
'asc'
)->get();
Here we are ordering in asc order by company name field.
In this article it is explained in detail.
You can simply execute a left join query:
ModelA::query()->leftJoin('model_b_table', 'model_a_table.primary_key', '=', 'model_b_table.foreign_key')->orderBy('model_a_table.target_column')->get();
This should work if you only need TableB's ID and created_at columns:
$latestTableB = ModelB::select('TableA_id', DB::raw('MAX(created_at) as created_at'))
->groupBy('TableA_id');
$query = ModelA::select('TableA.*')
->joinSub($latestTableB, 'latest_TableB', function ($join) {
$join->on('TableA.id', '=', 'latest_TableB.TableA_id');
})
->orderBy('latest_TableB.created_at')
->get();

Laravel return latest histories from all users

I have login history table where i save each user login/logout, therefore each user can have many rows.
I am trying to make filter to get last row of each user at once (getting latest row of all users) but not sure how to.
Code
controller
$histories = LoginHistory::with(['user','user.roles' => function($q) {
return $q->latest()->first();
}])->get();
this return all rows from all users instead of only latest rows.
screenshot
table
In this case my function should return 3 results as my user with id 1 has 2 rows (i only need to get latest one row 2 ) but yet i get all 4 rows with my function.
Any idea?
The raw MySQL query you might use here would look something like this:
SELECT lh1.*
FROM login_history lh1
INNER JOIN
(
SELECT user_id, MAX(login) AS max_login
FROM login_history
GROUP BY user_id
) lh2
ON lh1.user_id = lh2.user_id AND lh1.login = lh2.max_login
Your updated Laravel/Eloquent code:
$subquery = DB::table('login_history')
->select(DB::raw("user_id, MAX(login) AS max_login"))
->groupBy('user_id');
$rs = LoginHistory::joinSub($subquery, 'lh2', function($join) {
$join->on('login_history.user_id', '=', 'lh2.user_id');
$join->on('login_history.login', '=', 'lh2.max_login');
})
->select(['login_history.*'])
->get();

WhereHas with WhereRaw on one to many relationship (Laravel)

I have an order table and an order_details table in my system.
Relationship between order table and order details table is one to many, means One order has many order details.
Now the problem is i am trying to filter the order with the quantity of items a that are stored in order_details table.
what i doing right know trying to access with whereHas
if ($request->has('quantity') && $request->quantity != null){
$query = $query->whereHas('orderDetails',function ($q) use ($request){
$q->whereRaw('SUM(Quantity) >= '.$request->quantity);
});
}
$orders = $query->orderBy('OrderID','desc')->get();
But it throws an error
General error: 1111 Invalid use of group function (SQL: select * from `orders` where `AddedToCart` = 0 and `PaymentSucceeded` = 1 and exists (select * from `order_details` where `orders`.`OrderID` = `order_details`.`OrderID` and SUM(Quantity) >= 12) order by `OrderID` desc)
I will be vary thankful if i get the solution
To be able to use sum function you need to group by data and as I see you are trying to group them by orderID.
An approach like this might help:
$ordersIDs = DB::table('orderDetails')
->groupBy('OrderID')
->havingRaw('SUM(Quantity)', '>=', 12)
->pluck('orderID')->toArray();
$orders = DB::table('orders')
->whereIn($ordersIDs)
->get();
The above code executes two SQL queries, you can mix them easily to make one.
Hope it helps.

Laravel: MYSQL query in laravel

I have a working query goes like this
SELECT s.name as status, q.name as quality, p.name process, count(*)
FROM plates
JOIN equipment_status_codes s on equipment_status_code_id = s.id
JOIN plate_qualities q on plate_quality_id = q.id
JOIN processes p on process_id = p.id WHERE project_id in
(SELECT id
from projects
WHERE name like 'SPIRIT')
GROUP BY s.name, q.name, p.name ASC with ROLLUP
This works just and returns results just fine.
Now I am trying to put this in laravel syntax, but having some difficulties.
So I was thinking something along these lines.
return Plate::select('equipment_status_codes.name as Status', 'plate_qualities.name as Quality', 'processes.name as Process')
->join('equipment_status_codes', 'plates.equipment_status_code_id', '=', 'equipment_status_codes.id')
->join('plate_qualities', 'plates.plate_quality_id', '=', 'plate_qualities.id')
->join('processes', 'plates.process_id', '=', 'processes.id')
->groupBy(DB::raw('equipment_status_code_id WITH ROLLUP'))
...
...
->get();
Would someone help out. Thanks in advance!
Update:
#Govind Samrow
I have tried this query. It works (with couple of small adjustment) But I am not getting the same results as the one I get when I run the sql query.
I included screen shots.
So when I run the sql query.
I get the following results.
When I run the laravel query.
return DB::table('plates')
->join('equipment_status_codes', 'equipment_status_code_id', '=', 'equipment_status_codes.id')
->join('plate_qualities', 'plate_quality_id', '=', 'plate_qualities.id')
->join('processes', 'process_id', '=', 'processes.id')
->whereRaw("project_id IN(SELECT id from projects WHERE name like 'SPIRIT')")
->select(DB::raw('equipment_status_codes.name as Status'), DB::raw('IFNULL(plate_qualities.name, NULL) as Quality'), DB::raw('IFNULL(processes.name, NULL) as process'), DB::raw("COUNT(*) as Total" ))
->groupBy(DB::raw('equipment_status_codes.name WITH ROLLUP', 'plate_qualities.name WITH ROLLUP', 'processes.name WITH ROLLUP', 'asc'))
->get();
I get the following.
Almost there, but I am not sure what's going on?! Any ideas?
Try following Query for Joining with where Condition
return Plate::select('equipment_status_codes.name as Status', 'plate_qualities.name as Quality', 'processes.name as Process')
->join('equipment_status_codes', 'plates.equipment_status_code_id', '=', 'equipment_status_codes.id')
->join('plate_qualities', 'plates.plate_quality_id', '=', 'plate_qualities.id')
->join('processes', function($join)
{
$join->on('plates.process_id', '=', 'processes.id')
->whereIn('project_id', DB::table('projects')->where('name','LIKE','SPIRIT')->select('id')->get()->toArray());
})
->groupBy(DB::raw('equipment_status_code_id WITH ROLLUP'))
->get();
Hope this will help.
Use whereRaw for sub query in where clause Try this:
DB::table('plates')
->join('equipment_status_codes', 'equipment_status_code_id', '=', 'equipment_status_codes.id')
->join('plate_qualities', 'plate_quality_id', '=', 'plate_qualities.id')
->join('processes', 'process_id', '=', 'processes.id')
->whereRaw("project_id IN(SELECT id from projects WHERE name like 'SPIRIT')")
->select('equipment_status_codes.name as status', 'plate_qualities.name as quality', 'q.name as quality', 'processes.name as Process', DB::raw("COUNT(*) as Total"))
->groupBy(DB::raw('equipment_status_codes.name, plate_qualities.name, processes.name ASC with ROLLUP'))->get();
Here is raw sql result of above that got with toSql():
select `equipment_status_codes`.`name` as `status`, `plate_qualities`.`name` as `quality`, `q`.`name` as `quality`,
`processes`.`name` as `Process`, COUNT(*) as Total from `plates`
inner join `equipment_status_codes` on `equipment_status_code_id` = `equipment_status_codes`.`id`
inner join `plate_qualities` on `plate_quality_id` = `plate_qualities`.`id`
inner join `processes` on `process_id` = `processes`.`id`
where project_id IN(SELECT id from projects WHERE name like 'SPIRIT')
group by equipment_status_codes.name, plate_qualities.name, processes.name ASC with ROLLUP
Note: You can use SQL output with $query->toSql() and then compare with your actual SQL query.
You may use the table method on the DB facade to begin a query. The table method returns a fluent query builder instance for the given table, allowing you to chain more constraints onto the query and then finally get the results using the get method:
Check its link and get knowladge for laravel query builder:-
https://laravel.com/docs/5.4/queries

Left join to get a single row in Laravel

I have been unsuccessfully trying to leftjoin and get the required data
Here is my code:
$album = Albums::->where('users_id',$user_id)
->leftJoin('photos',function($query){
$query->on('photos.albums_id','=','albums.id');
$query->where('photos.status','=',1);
//$query->limit(1);
//$query->min('photos.created_at');
})
->where('albums.status',1)->get();
The comments are some of my several trying...
I want to get only a single record from the photos table matching the foreign key album_id which was updated first and also with status 1
pls help...
I have used DB::raw() in order to achieve this
$album = Albums::select( 'albums.*',
DB::raw('(select photo from photos where albums_id = albums.id and status = 1 order by id asc limit 1) as photo') )
->where('users_id',$user_id)
->where('albums.status',1)->get();
#JarekTkaczyk 's coding was similar and displayed the same result as I needed, so a special thanks to him for his time and effort...
But comparing the execution time for the quires I stayed to mine as my above snippet
select `albums`.*, (select photo from photos where albums_id = albums.id and status = 1 order by id asc limit 1) as photo from `albums` where `users_id` = '1' and `albums`.`status` = '1'
took 520μs - 580μs
and #JarekTkaczyk 's
select `albums`.*, `p`.`photo` from `albums` left join `photos` as `p` on `p`.`albums_id` = `albums`.`id` and `p`.`created_at` = (select min(created_at) from photos where albums_id = p.albums_id) and `p`.`status` = '1' where `users_id` = '1' and `albums`.`status` = '1' group by `albums`.`id`
took 640μs - 750μs But both did the same...
You can achieve it using either leftJoin or rightJoin (but the latter would return Photo models, so probably you won't need that):
Albums::where('users_id', $user_id)
->leftJoin('photos as p', function ($q) {
$q->on('photos.albums_id', '=', 'albums.id')
->on('photos.updated_at', '=',
DB::raw('(select min(updated_at) from photos where albums_id = p.albums_id)'))
->where('photos.status', '=', 1);
})
->where('albums.status', 1)
->groupBy('albums.id')
->select('albums.*', fields from photos table that you need )
->get();
Are you trying to check for albums that have the status of '1'? If this is the case you are missing an equals sign from your final where.
Try:
->where('albums.status','=',1)->first();
Alternatively you may be able to achieve this with an 'on' instead of a 'where' inside the join function. You also don't need to split up query inside of the function and can do it as one line with the '->' :
$album = Albums::->where('users_id',$user_id)
->leftJoin('photos',function($query){
$query->on('photos.albums_id','=','albums.id')
->on('photos.status','=',1);
})
->where('albums.status','=',1)->first();
You need to make sure that you are using 'first', as it will return a single row of the first result. Get() will return an array.
As a straightforward answer which results in a single object I suggest the following query:
$album = DB::table('albums')->select('albums.*', 'photos.photo')
->join('photos', 'photos.id', '=', 'albums.id')
->where('users_id',$user_id)
->where('albums.status',1)
->first();

Categories