laravel inner join or left join? - php

i have a piece of code that is supposed to join two tables Enumerator Data and export the result. Here is the controller code for handling that.
$joinTableName = (new Enumerator())->getTable();
$fromTableName = (new FarmData())->getTable();
$selectedColumns = [
"{$fromTableName}.first_name",
"{$fromTableName}.middle_name",
"{$fromTableName}.last_name",
"{$fromTableName}.farm_phone_number",
"{$fromTableName}.age",
"{$fromTableName}.farmer_bvn",
"{$joinTableName}.state",
"{$joinTableName}.lga",
"{$fromTableName}.enum_unique_id"
];
$v2_farm_data = FarmData::whereNotNull('enum_unique_id')->select($selectedColumns)
->leftJoin(
$joinTableName,
"{$joinTableName}.unique_id",
'=',
"{$fromTableName }.enum_unique_id"
)
->get();
The resulting csv data has some empty state and lga fields. I presume it has to do with the fact that the Enumerator table has about 20,000 records while the Data table has about 100,000 records. Using just a join statement returns data records of just 20,000. I want to get all the data records which are about 100,000. What am i doing wrong?

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();

Joining multiple tables in laravel query builder results in duplication

I'm trying to use Laravel Query Builder to join 2 tables.
Here's my query for joining 2 tables
$instructors = DB::table('instructors')
->join('instructor_courses', 'instructors.id', '=','instructor_courses.instructor_id')
->where('instructor_courses.course_id', $schedule->course_id)
->where('instructor_courses.position', 'Instructor')
->whereNull('instructor_courses.deleted_at')
->whereNull('instructors.deleted_at')
->get();
The code let me join the table properly however this results to multiple duplication. Take a look at the result passed in select tag
Here's the schema I have
Instructor Table
Instructor Courses Table
Basically what I want to achieve is to display a record without duplication. Is there a better approach in joining multiple table like this one? Thank you
You should use groupBy for instructors.id
$instructors = DB::table('instructors')
->join('instructor_courses', 'instructors.id', '=','instructor_courses.instructor_id')
->where('instructor_courses.course_id', $schedule->course_id)
->where('instructor_courses.position', 'Instructor')
->whereNull('instructor_courses.deleted_at')
->whereNull('instructors.deleted_at')
->groupBy('instructors.id') //add a line
->get();
Try to use the ->distinct() function in laravel to prevent duplicates in selecting data.
$instructors = DB::table('instructors')
->join('instructor_courses', 'instructors.id', '=','instructor_courses.instructor_id')
->where('instructor_courses.course_id', $schedule->course_id)
->where('instructor_courses.position', 'Instructor')
->whereNull('instructor_courses.deleted_at')
->whereNull('instructors.deleted_at')
->groupBy('instructors.id')
->distinct()
->get();

Display Json data in laravel view with Joins

I'm trying to retrieve data from database to laravel blade view with join associate table.
in my case I have two tables called interesting_courses and courses. here some student can have many interesting courses. Therefore courses_id stored as json array in database as follows.
["1","11","15","16"]
but I need to join the courses table to get the associate course name as follows.
["Hospitality","Business Management","Auto Mobile","Health Care"]
Below is my controller
$intresting_courses = DB::table('intresting_courses')
->join('courses','courses.id','=','intresting_courses.courses_id')
->where('intresting_courses.youth_id',$id)
->first();
How can I join the tables.
There is no way you can join it in query natively.
The solution is to write two queries
select * from intresting_courses where youth_id = ? limit 1
select * from courses where id in (1, 2, 3, 4, 5, ...)
that way you can get courses that student intresting.
Here the laravel code for thoses queries.
$intresting_courses = DB::table('intresting_courses')
->where('intresting_courses.youth_id', $id)
->first();
$courses = DB::table('courses')
->whereIn('id', json_decode($intresting_courses->courses_id))
->get();

laravel query builder: join dependent on primary content

I'm using luman and Database Query Builder to fetch full user info from database.
First, Please Take a lock at my database structure:
I have a table called users and a series of other tables that are related to user groups (Ex: secretaries, patients, doctors and admins) which stores additional information about the users.
Also To determine user access, I have a level column on user table which can have one of this value as enum: 'admin', 'doctor', 'secretary', 'patient'.
So, I want to get this information using one query by join and select.
My training code is something like this:
$userInfo = User::where("userID", $userID)
->limit(1)
->join('[GROUP_TABLE_NAME]', function ($join) {
$join->on('user.userID', '=', '[GROUP_TABLE_NAME]' .'.'.
'[GROUP_NAME]' . 'ID');
})
->get();
The GROUP_NAME comes from level column on user table and the GROUP_TABLE_NAME can be built based on the GROUP_NAME value(Ex: ['secretary' => 'secretaries' , 'patient' => 'patients' , ...]).
Any idea to handle the join structure using laravel query builder?
First you should be aware of the fact that this code architecture is not convenient and not easy to understand for other developers.
SQL
You can achieve your goal by using union and join.
Just convert this query for laravel builder or use it directly with DB::statement: select users.*, infos.info from users left join (( select secretaries.* from secretaries ) UNION (select doctors.* from doctors)) infos ON users.id = infos.user_id where users.id=?.
But
The easiest way to do it is to fetch info in two queries, both indexed and fast: user from users by primary key and then info by indexed field user_id in it. Create Doctorinfo, Admininfo models and correspondent migrations. So user class can be smth like this:
public function getInfo() {
switch($this->level) {
'doctor':
return $this->doctorinfo;
...
}
}
private function doctorinfo() {
$this->hasOne('App\Doctorinfo');
}
Builder
You can use left join to join all sub tables as well. The following builder selects info column.
User::where("userID", $userID)->limit(1)
->leftJoin('patients', 'users.id', '=', 'patients.user_id')
->leftJoin('doctors', 'users.id', '=', 'doctors.user_id')
->leftJoin('admins', 'users.id', '=', 'admins.user_id')
->select('users.*', DB::raw('IF(users.level="admin", admins.info, (IF users.level="doctors", doctors.info, patients.info))'))

Laravel SQL missing data from Join table

I'm using the following code to create a query with a join.
$query = rs_vehicles::select(
'rs_vehicles.id',
'rs_vehicles.type',
'rs_vehicles.make',
'rs_vehicles.model',
'rs_vehicles.year',
'rs_vehicles.status',
'rs_vehicle_regs.registration'
);
$query ->leftJoin('rs_vehicle_regs', function($join){
$join->on('rs_vehicle_regs.rs_vehicles_id', '=', 'rs_vehicles.id')
->where('rs_vehicle_regs.registration_date', '=',
DB::raw("(select max(registration_date) from rs_vehicle_regs where rs_vehicles_id = rs_vehicles.id)"));
});
$rsGrid = $query->get();
This produces the following sql statement:
select rs_vehicles.id, rs_vehicles.type, rs_vehicles.make,
s_vehicles.model, rs_vehicles.year, rs_vehicles.status,
s_vehicle_regs.registration from rs_vehicles
left join rs_vehicle_regs
on rs_vehicle_regs.rs_vehicles_id = rs_vehicles.id
and rs_vehicle_regs.registration_date = (select max(registration_date) from rs_vehicle_regs where rs_vehicles_id = rs_vehicles.id)
The SQL statement generated by Laravel runs perfect when I execute it in MySQL Workbench and it brings back the expected values for ALL fields. However when I execute the code in Laravel although the script runs perfectly with no errors and brings back almost all the required data the 'rs_vehicle_regs.registration' field which is in the joined table comes back as null. I've spent ages trying to figure this out and I am getting nowhere. Anybody any idea why this wont work in Laravel? I'm at my wits end.
I have tried changing get() to toSql() and I get the following
select `rs_vehicles`.`id`, `rs_vehicles`.`type`, `rs_vehicles`.`make`, `rs_vehicles`.`model`, `rs_vehicles`.`year`, `rs_vehicles`.`vehicle_status`, `vh_type`.`display_text` as `type_text`, `vh_status`.`display_text` as `vehicle_status_text`, `rs_vehicle_regs`.`registration` as `registration` from `rs_vehicles` left join `app_params` as `vh_type` on `rs_vehicles`.`type` = `vh_type`.`list_value` and `vh_type`.`param_type` = ? left join `app_params` as `vh_status` on `rs_vehicles`.`vehicle_status` = `vh_status`.`list_value` and `vh_status`.`param_type` = ? left join `rs_vehicle_regs` on `rs_vehicle_regs`.`rs_vehicles_id` = `rs_vehicles`.`id` where `rs_vehicles`.`status` = ?
I have also tried the following:
$query ->leftJoin('rs_vehicle_regs', function($join){
$join->on('rs_vehicle_regs.rs_vehicles_id', '=', 'rs_employees.rs_vehicles_id')
->where('rs_vehicle_regs.registration_date', '=', 'max(registration_date)');
});
This produces the exact same result as the original query where the object as the field but the values are null.
Then I tried this:
$query ->leftJoin('rs_vehicle_regs', 'rs_vehicle_regs.rs_vehicles_id', '=', 'rs_vehicles.id');
This join works and brings back the data I am looking for however if the join table has more than one entry related to the parent table as can happen I get all records returned and I only want one record were max(registration_date). This has me totally stumped.

Categories