Three join and groupby ln laravel 5.4 - php

Hello I am new with laravel and I need to do two join between three tables then group by and count in another table then join the result of the group by with the two join result.I try to use two query the first one for the two join and the second query for the group by but i don't know how to join the two query.
This is my first query three tables: members, members_courses_assign and courses
$temp = DB::table('members')
->join('members_courses_assign', 'members.externalPersonKey', '=', 'members_courses_assign.externalPersonKey')
->join('courses', 'members_courses_assign.referenceNumber', '=', 'courses.referenceNumber')->where(['courses.termkey'=>$termK,'members_courses_assign.termkey'=>$termK])->get();
This is my second query
$count= DB::table('files')
->select('referenceNumber', DB::raw('count(*) as total'))
->where(['termkey'=>$termK])
->groupBy('referenceNumber')
->get();
Then i need to join the result of the first with the second, I try to join the 4 table first but the 4th table 'files' will ambiguity because the 'referenceNumber' is'not unique in the 'files' table.

If I understand what you are trying to do, using join() with the 'count' subquery as the first argument, and passing a Closure as the second argument specifying the link between your main query and the subquery should work.
$temp = DB::table('members')
->join('members_courses_assign', 'members.externalPersonKey', '=', 'members_courses_assign.externalPersonKey')
->join('courses', 'members_courses_assign.referenceNumber', '=', 'courses.referenceNumber')
->join(DB::raw('(SELECT COUNT(*) AS count, referenceNumber FROM files WHERE termkey='.$termK.' GROUP BY referenceNumber) FileCount'), function($join)
{
$join->on('courses.referenceNumber', '=', 'FileCount.referenceNumber');
})
->where(['courses.termkey'=>$termK,'members_courses_assign.termkey'=>$termK])
->get();

Related

How to select from a table based on another table's values Eloquent?

I am trying to get the data on some students that are still active. Even tho I have data from inactive students in the same table.
This is the StudentAttendance
This is the StudentClass
This is the Eloquent query that I came up with:
StudentAttendance::
select('student_classes.active', 'student_attendances.student_id', 'student_attendances.classroom_id', 'classrooms.classroom', 'attendance_rules.option')
->join('classrooms', 'classrooms.id', '=', 'student_attendances.classroom_id')
->join('attendance_rules','attendance_rules.id', '=', 'student_attendances.attendance_id')
->join('student_classes', 'student_attendances.student_id', '=', 'student_classes.student_id')
->where('attendance_date', date("Y-m-d"))
->orderBy('classrooms.classroom', 'ASC')
->get();
SQL:
select `student_classes`.`active`, `student_attendances`.`student_id`, `student_attendances`.`classroom_id`, `classrooms`.`classroom`, `attendance_rules`.`option`
from `student_attendances`
inner join `classrooms` on `classrooms`.`id` = `student_attendances`.`classroom_id`
inner join `attendance_rules` on `attendance_rules`.`id` = `student_attendances`.`attendance_id`
inner join `student_classes` on `student_attendances`.`student_id` = `student_classes`.`student_id`
where `attendance_date` = '2020-02-11'
order by `classrooms`.`classroom` asc
Now my Eloquent query results into this:
As you can see the student_id 22 with the classroom_id of 2 is inactive but it appears to be inactive once and the rest active. If I remove the student_classes join I won't get all the repeated results.
The goal is to display all the attendances of today where the student is active (active=1) in the StudentClass even if I query in the StudentAttendance.
You will want to scope your join to student_classes to only look at active records.
You can do this by using a callback in your join method:
->join('student_classes', function ($join) {
$join->on('student_attendances.student_id', '=', 'student_classes.student_id')
->on('student_classes.classroom_id', '=', 'student_attendances.classroom_id')
->where('student_classes.active', 1);
})
This is covered under the 'Advanced Join Clauses' in the Query Builder docs - https://laravel.com/docs/5.8/queries#joins

Duplicate rows with multiple join

I'm using this code to join two table on contents table then group by user
when I join only analytics table the result is correct also the same wehn joining only reactions table
but both table is causing wrong result in sum and count
->where('users.type_id', 2)
->leftJoin('contents', 'users.id', '=', 'contents.instructor_id')
->leftJoin('analytics', 'contents.id', '=', 'analytics.content_id')
->leftJoin('reactions', 'contents.id', '=', 'reactions.content_id')
->selectRaw('
users.id as id,
concat(users.name, "-", users.last_name) as name,
sum(analytics.total_watched) as sum_watched,
count(analytics.id) as times_watched,
sum(analytics.completed=1) as completed_video,
sum(reactions.reaction_type="love") as love,
sum(reactions.reaction_type="happy") as happy,
sum(reactions.reaction_type="thinking") as thinking,
sum(reactions.reaction_type="sad") as sad,
sum(reactions.reaction_type="angry") as angry
')
->groupBy('users.id')
->limit(10)
->get();

Laravel DB Join Query

I have write a query for fetch every users his/her last organization name, but it's only return organization table last organization name. how can i develop this query for every users.
Here is my Query.
$org=DB::table('careers')
->join('users','careers.user_id','=','users.id')
->pluck('org_name')
->last();
print_r($org);
You can use latest() and first() method for getting last 1 record
$org=DB::table('careers')
->join('users','careers.user_id','=','users.id')
->latest()
->first()
->pluck('org_name');
OR
use latest() and take() method
$org=DB::table('careers')
->join('users','careers.user_id','=','users.id')
->latest()
->take(1)
->pluck('org_name');
Use DB::raw to get the records.
$org=DB::raw("SELECT users.name, t.org_name
FROM users JOIN (SELECT org_name
FROM careers
WHERE users.id = careers.user_id
ORDER BY created_at DESC
LIMIT 1) as t ON t.user_id = user.id");
Hope this could work.

Convert a Laravel Builder response to a Collection

I've got 3 tables - challenge, challenge_users, and climbs with the following definitions:
table challenge
id, name, start_date, end_date
table challenge_users
id, challenge_id, user_id
table climbs
id, user_id, date
I want to pull a list of climbs that fall within the dates of the challenge for any user in the challenge. The following SQL query does what I want:
select
*
from
climbs c
join
challenge_user cu on c.user_id=cu.user_id
where
date >= '2015-03-01'
and date <= '2015-03-06'
and cu.challenge_id=1;
I can build this with a query builder like
$climbs = DB::table('climbs')
->join('challenge_user', 'climbs.user_id', '=', 'challenge_user.user_id')
->where('climbs.date', '>=', $this->start_datetime->tz($tz)->startOfDay()->toDateString())
->where('climbs.date', '<', $this->end_datetime->tz($tz)->endOfDay()->toDateString())
->where('climbs.date', '<=', Carbon::now()->tz($tz)->endOfDay()->toDateString())
->get();
The problem is, this return an array of arrays. What I really want is to get a Collection of Climb models. I know I can do something like Climb::all()->filter(...), but then I'm doing a pretty large select (the SQL query ends up just being "select * from climbs").
Is there a way to use the DB to limit my results but still get my Collection of Climb models?
You can use Eloquent with a join, but you need to manually specify the select statement so that you're only getting the fields from the parent table and not the joined table:
$climbs = Climb::select('climbs.*')
->join('challenge_user', 'climbs.user_id', '=', 'challenge_user.user_id')
->where('climbs.date', '>=', $this->start_datetime->tz($tz)->startOfDay()->toDateString())
->where('climbs.date', '<', $this->end_datetime->tz($tz)->endOfDay()->toDateString())
->where('climbs.date', '<=', Carbon::now()->tz($tz)->endOfDay()->toDateString())
->get();

How to order grouped results by last id using Eloquent and MySQL?

I tried this query but it only order id column, rest is not.
$chapters = Test::select(DB::raw('*, max(id) as id'))
->groupBy('id_equip')
->orderBy('id', 'asc')
->get();
In MySQL when using group by you can't rely on order by clause (it won't work as you expect, ie. it will not order the results in groups, but rather return random row from the group).
So in order to achieve what you want, you need a subquery or join:
// assuming tests table, group by id_equip, order by id
SELECT * FROM tests WHERE id = (
SELECT MAX(id) FROM tests as t WHERE t.id_equip = tests.id_equip
) ORDER BY id
SELECT * FROM tests
JOIN (SELECT MAX(id) as id FROM tests ORDER BY id DESC) as sub
ON sub.id = tests.id
This will get the highest id for each id_equip and return whole row for each of them.
Now, in eloquent I suggest first approach, if you want it to look more intuitive:
Test::where('id', function ($sub) {
// subquery
$sub->selectRaw('max(id)')
->from('tests as t')
->where('t.id_equip', DB::raw('tests.id_equip'));
// order by
})->orderBy('id', 'desc')->get();
but 2nd appreach is probably the way if you have big table to scan (in terms of performance):
Test::join( DB::raw(
'(select max(id) as id from tests group by id_equip order by id desc) sub'
), 'sub.id', '=', 'posts.id')
->get(['tests.*']);
Here you need to set order by clause inside the raw join statement.
You could also build that join subquery with the builder if you like:
$sub = Test::selectRaw('max(id)')
->groupBy('id_equip')
->orderBy('id', 'desc')
->toSql();
Test::join( DB::raw(
"({$sub}) as sub"
), 'sub.id', '=', 'posts.id')
->get(['tests.*']);

Categories