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
Related
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.
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)
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.
I have been reading on full text search for some time now, I have read some articles that say its possible but they don't provide sample query,
I'm stuck finding a workaround on how to search on multiple table joined by an ID using full text search
this is my minified table look like
here is what i have so far
$users = User::select('users.*','departments.department_name')
->join('departments', 'departments.id', ' =', 'users.dept_id')
->whereRaw(
"MATCH(user_name) AGAINST(? IN BOOLEAN MODE)",
array($q)
)
->paginate(10);
}
how can I include department_name when searching? please help, thanks in advance
You could create a 'materialized view'.
http://en.wikipedia.org/wiki/Materialized_view
basically a table that is the results of the JOIN, and the create a full text index on that.
CREATE TABLE materialized (FULLTEXT idx (user_name,department_name))
SELECT u.id,user_name,department_name
FROM users u INNER JOIN departments d ON (d.id = dept_id)
You can then run queries on that table instead..
SELECT * FROM materialized WHERE MATCH(user_name,department_name) AGAINST('test' IN BOOLEAN MODE)
but...
You will need to updatethe table periodically (or when the underlying tables update) - easiest is just to DROP and recreate - Or you can use TRUNCATE then INSERT INTO ... SELECT ... FROM ... format.
(more elaborate schemes involve triggers to keep the 'view' updated, or even watching the binlog, and replaying updates against the 'view')
I think you are too close to your result. Using query-builder (but not tested) this code should work:
$user = DB::table('users')
->selectRaw('users.*, departments.department_name')
->join('departments', 'departments.id', ' =', 'users.dept_id')
->whereRaw(
"MATCH(users.user_name) AGAINST(? IN BOOLEAN MODE) OR
MATCH(departments.department_name) AGAINST(? IN BOOLEAN MODE)",
array($q, $q))
)->paginate(10);
There are also some restrictions on where you could use FULLTEXT in MySQL tables and where not http://dev.mysql.com/doc/refman/5.6/en/fulltext-restrictions.html. It is good practise to have FULLTEXT index on column or column list which you use in MATCH() clause.
I have a DB table with product information, and a DB table with tax rates.
My problem is that I am joining these two tables together, which works great.. until I disable "taxable" on a row for the product DB. Now my query is trying to join, but doesn't find a foreign key and I get no result at all.. I want to grab a result either way. I am using code igniter syntax, but it should be pretty obvious whats going on here:
$this->db->from('inventoryTaxRates a');
$this->db->join('inventory_items q', 'q.inventoryTaxRateID = a.inventoryTaxRateID');
sometimes q.inventyTaxRateID becomes 0, or disabled.. The query cannot join the two tables and gives me no result whatsoever. I want it to still give me the result from inventory_items.
I have tried left joining as well:
$this->db->join('inventory_items q', 'q.inventoryTaxRateID = a.inventoryTaxRateID', 'left');
You can specify a RIGHT join like this:
$this->db->join('inventory_items q',
'q.inventoryTaxRateID = a.inventoryTaxRateID',
'right');
You can use RIGHT JOIN, but I'd like to rewrite query like that:
SELECT .. FROM inventory_items ...
LEFT JOIN inventoryTaxRates ...