Eloquent query to search over two tables - php

I am struggling with the following query, using Eloquent in Laravel 5.6.
I need to return all issues that have a tag_id of 5 assigned to them, where the project_id and item_id from the issues table matches the project_id and issue_id from my pivot table.
issues table:
issues_tags pivot table:
I have tried the following code, but it returns all issues from the issue table, however the expectation is 3 results.
Expected results
The results returned from the issues table should be ID 1, 4 and 5.
$issues = Issue::join('issues_tags', 'issues_tags.project_id', 'issues_tags.issue_id')->where('issues_tags.tag_id', 5)->select('issues.*')->get();

You need to specify the issues table instead of issues_tags on the join. A left join will also help reduce the results. Since you're joining on two different keys, you have to use a closure.
$issues = Issue::leftJoin('issues_tags', function($join) {
$join->on('issues.project_id', '=', 'issues_tags.project_id');
$join->on('issues.item_id', '=', 'issues_tags.issue_id');
})
->where('issues_tags.tag_id', 5)->select('issues.*')->get();
If the table is really supposed to match on project_id->project_id and issues.id -> issues_tags.issues_id, you can modify the 2nd join clause.

Related

Laravel join two tables on Json Column

I have two tables(models), one with a json column that may contain the id on other table. I want to join these two together, how do i chain a laravel join to my query.
SO it turns out the join wasn't working because the json column was storing the id field as a string and the joined table's id is stored as an integer.
Casting the result into an integer fixed this. Let's say table 'a' is the first table that contains a json column with an id on second table which is table 'b'.So I did.
DB::table('a')->join('b','b.id',DB::Raw("CAST(a.data->'$.id' AS UNSIGNED)"))->get();
Apply the JSON logic inside the join query like:
Post::select('posts.id', 'posts.field1','posts.field2','samples.id as sample_id', ..)
->join('samples', 'posts.json_col_name.id', '=', 'samples.id')
->get();
Make sure that you make no conflict for the query builder while picking columns (use field_name as another_name) when both models have fields with common name.

Laravel Querybuilder join on LIKE

I am attempting to join two tables using the Laravel's query builder however I seem to be having an issue getting the desired result using the query builder, I can however get it quite simply using a raw SQL statement. I simply want to return all mod rows that have the corrosponding value in the tag column in the tags table.
Schema
--------------
mods
--------------
id - int - (primary key)
name - varchar
--------------
tags
--------------
id - int
modid - int - (primary key of its parent mod)
tag - varchar
Working SQL query
SELECT * FROM mod JOIN tags ON tags.tag LIKE '%FPS%'
Query Builder
DB::table('mods')
->join('tags', function ($join) {
$join->on('tags.tag', 'like', '%FPS%');
})
->get();
Currently this is telling me: Unknown column '%FPS%' in 'on clause' but I am unsure how else to structure this. I intend on adding more orOn clauses as well as I will want to get results on multiple tags but firstly I just want to get a single tag working.
Appreciate any help.
SELECT * FROM mod JOIN tags ON tags.tag LIKE '%FPS%'
Your query builder is refusing to generate this query because it's nonsense!
To work correctly, a JOIN clause needs to compare two columns for equality -- one column on each side of the join table. A JOIN clause that doesn't do this is functionally "downgraded" to a WHERE clause. In the case of this query, the two tables end up cross joined.
What you probably want is:
SELECT * FROM mod
JOIN tags ON tags.modid = mod.id
WHERE tags.tag LIKE '%FPS%';
$join->on('tags.tag', 'like', '%FPS%');
Try by replacing
$join->where('tags.tag', 'like', '%FPS%');
This because the on method waiting a name of a field not a query value if you want it to deal with it in this way, you should use DB::raw('%FPS%').
Maybe you are trying to do something like the following:
DB::table('mods')
->select(DB::raw('mods.id as modid, mods.name, tags.id as tagid, tags.tag'))
->join('tags', function ($join) {
$join->on('tags.modid', '=', 'mods.id');
})
->where('tags.tag', 'like', '%FPS%')
->get();
If you want to see what is run in the database use
dd(DB::getQueryLog())
to see what queries were run.
Try this
Thank you

Laravel pagination not working with join where condtion

I am using laravel pagination for list. There is two tables lets say table1 & table2.
table2 id is primary key & used as foreign key in table1 t2_id
I want to get all unique t2_id but need to check is_deleted is not set for that id in table2.
I have written function in my model:
public function getDistinctIds() {
return DB::connection('pgsql')
->table('table1 AS t1')
->distinct()
->select('t1.t2_id')
->join('table2 AS t2', 't2.id', '=', 't1.t2_id')
->where('t2.is_deleted', '!=', 1)
->orWhereNull('t2.is_deleted')
->paginate(3);
}
This function will give me accurate result. But there is problem with pagination. There are 12 records in table1 getDistinctIds() gives only 4 record so there should be two pages but it is showing 4 pages. I think pagination is not taking where condition into consideration.
Please help thanks in advance!!
I got the solution instead of distinct()used groupBy(). Now it is working fine. :)
Referenced from laracast post : [L4.2] distinct() and pagination returns total number of results not distinct total

Laravel / MYSQL (Eloquent) - Querying large table

I am making an online directory, this directory contains businesses, this is how the current table structure is set out:
1) "Business"
ID (PK)
Name
Phone_Number
Email
2) Tags
id (PK)
tag
3) Business_tags
id (PK)
business_id (FK)
tag_id (FK)
There are over 9k rows inside of the business table, and over 84,269 rows and there are over 29k rows inside the ("Business_tags") table (As a business can have multiple tags).
Inside the business model, is the following:
public function tags()
{
return $this->belongsToMany('App\Tags');
}
The issue is when I am trying to do a search, so for example, let's say that someone wants to search for a "Chinese" then it's takes more time than it probably should to return a value. For example, I am using:
$business = Business::where(function ($business) use ($request) {
$business->whereHas('tags', function ($tag) use ($request) {
});
})->paginate(20);
Searching takes on average: 35 seconds to display the results.
Here is the raw sql:
select * from `businesses` where (exists (select * from `tags` inner join `business_tags` on `tags`.`id` = `business_tags`.`tags_id` where `business_tags`.`business_id` = `businesses`.`id` and `name` in ('chinese')))
This takes on average: 52.4s to run inside Sequel pro (Using the raw SQL statement)
Any ideas how I can improve the performance of this query so that it's a lot faster? I want to have this functionality, but the user is not going to wait this long for a response!
EDIT:
1 PRIMARY businesses NULL ALL NULL NULL NULL NULL 8373 100.00 Using where
2 DEPENDENT SUBQUERY business_tags NULL ALL NULL NULL NULL NULL 30312 10.00 Using where
2 DEPENDENT SUBQUERY tags NULL eq_ref PRIMARY PRIMARY 4 halalhands.business_tags.tags_id 1 10.00 Using where
You're over-complicating this, and not using eloquent relationships correctly. You should be using JOINs instead:
$businesses = Business::join('business_tags', 'business_tags.business_id', '=', 'business.id')
->join('tags', function($join) {
$join->on('business_tags.tag_id', '=', 'tags.id')
->where('tags.name', '=', 'chinese');
})->get();
Or in raw SQL:
SELECT *
FROM `business`
INNER JOIN `business_tags` ON `business_tags`.`business_id` = `business`.`id`
INNER JOIN `tags` ON `business_tags`.`tag_id` = `tags`.`id` AND `tags`.`name` = 'chinese'
(Note that you could put that tags.name = 'chinese' part in the WHERE clause and yield the same effect)
Your current query does an exists subquery to get all the records from the pivot table that match the criteria, then passing that back to the main query. It's an extra step, and it's unnatural.
Eloquent relationships are NOT for complex queries like this, but are rather there to provide additional, related information about a record without having to write another query manually.
For instance, if you want to view a business, you might query with() phone numbers and addresses from other tables. You might want to list out their tags, or sync() them. But eloquent does not build and filter queries, that's what query builder is for.
Let me know if you need more explanation.
As a lot of others are also going to tell you.
Have you run EXPLAIN on your query?
Have you added indexes to your tables?
Because even with the amount of data you have mentioned the query should have been faster than what you have reported.
Also see if a JOIN can work here and if faster?(just a thought)

JOIN table with subselect using Query Builder

Is it possible to JOIN a subselect with another table in Laravel 5 Query Builder?
I mean - theoretically - something like this:
$sub = DB::table('A')->select(DB::Raw('id, MAX(date)'))->groupBy('id')->get();
$query = DB::table('B')->join($sub, 'B.id', '=', $sub->id)->get();
In my case, in table A I have duplicated rows. I need the ones with max date per id. Then I need to join the result with table B.
As adviced in comments, a workaround follows.
$idArray= DB::table('A')->select(DB::Raw('id, MAX(date)'))->groupBy('id')->lists('id');
$query = DB::table('B')->whereIn('id', $idArray)->get();
But, again, just a workaround.

Categories