Different results using same query with DB::raw and Eloquent - php

I'm getting some unexpected results when running an Eloquent join query. I get two different results from using the exact same query. One running with DB::raw(), the second with Eloquent.
In the Eloquent query, the Users that matches the
where squad_user.leave_time >= seasons.start_time
are missing and will not be included in the result set. The users that matches the
or squad_user.leave is null
will be included, however.
That's the only difference in the results from the two queries. The raw query actually produces the desired result set.
What really puzzles me is, if I check the query logs, both Laravel's and MySQL, I get the exact same query when running both the raw and Eloquent query.
Raw query (the actual query i get from the query log when running the Eloquent query)
return \DB::select(\DB::raw('
select users.*
from users
inner join squad_user on users.id = squad_user.user_id
inner join seasons on squad_user.squad_id = seasons.squad_id
where squad_user.join_time <= seasons.end_time
and (squad_user.leave_time >= seasons.start_time or squad_user.leave_time is null)
and seasons.id = :seasonId
'),
['seasonId' => 3]
);
Eloquent query
return User::join('squad_user', 'users.id', '=', 'squad_user.user_id')
->join('seasons', 'squad_user.squad_id', '=', 'seasons.squad_id')
->where('squad_user.join_time', '<=', 'seasons.end_time')
->where(function ($query)
{
$query->where('squad_user.leave_time', '>=', 'seasons.start_time')
->orWhereNull('squad_user.leave_time');
})
->where('seasons.id', 3)
->get(['users.*']);
Laravel's Eloquent query log
select `users`.*
from `users`
inner join `squad_user` on `users`.`id` = `squad_user`.`user_id`
inner join `seasons` on `squad_user`.`squad_id` = `seasons`.`squad_id`
where `squad_user`.`join_time` <= seasons.end_time
and (`squad_user`.`leave_time` >= seasons.start_time or `squad_user`.`leave_time` is null)
and `seasons`.`id` = 3
{"bindings":["seasons.end_time","seasons.start_time",3],"time":0.38,"name":"mysql"}
MySQL's general_log on the Eloquent query
select `users`.*
from `users`
inner join `squad_user` on `users`.`id` = `squad_user`.`user_id`
inner join `seasons` on `squad_user`.`squad_id` = `seasons`.`squad_id`
where `squad_user`.`join_time` <= ?
and (`squad_user`.`leave_time` >= ? or `squad_user`.`leave_time` is null)
and `seasons`.`id` = ?
MySQL's general_log on the Raw query
select users.*
from users
inner join squad_user on users.id = squad_user.user_id
inner join seasons on squad_user.squad_id = seasons.squad_id
where squad_user.join_time <= seasons.end_time
and (squad_user.leave_time >= seasons.start_time or squad_user.leave_time is null)
and seasons.id = ?
I would appreciate any pointers here, as I am very lost.

where binds 3rd param and treats it usually as a string, unless you tell it not to by using raw statement. DB::raw or whereRaw will work for you:
return User::join('squad_user', 'users.id', '=', 'squad_user.user_id')
->join('seasons', 'squad_user.squad_id', '=', 'seasons.squad_id')
->where('squad_user.join_time', '<=', DB::raw('seasons.end_time'))
->where(function ($query)
{
$query->where('squad_user.leave_time', '>=', DB::raw('seasons.start_time'))
->orWhereNull('squad_user.leave_time');
})
->where('seasons.id', 3)
->get(['users.*']);

Since Laravel Verion 5.2 you can also use whereColumn to verify that two columns are equal (or pass a comparison operator to the method):
->whereColumn('squad_user.join_time', '<=', 'seasons.end_time')

Related

raw query join left with select to laravel 8 query

i'm using raw query on my laravel function, and i wanna make it into laravel query builder, but i really have no idea how to do it, i've read laravel documentary about advanced join clause or subquery joins, but still cant figuring it out how to convert it
raw query :
$buku = DB::select(
DB::raw('
SELECT buku.*, kategory, tag
FROM buku
LEFT JOIN kategori_buku ON buku.id_kategori = kategori_buku.id
LEFT JOIN detail_buku_tag ON buku.id = detail_buku_tag.id_buku
LEFT JOIN (SELECT tag_buku.id, GROUP_CONCAT(tag) AS tag FROM tag_buku) AS tag_buku ON tag_buku.id = detail_buku_tag.id_tag
GROUP BY buku.id')
);
there some sql chaining issue. #Ramiz Kongulov forget to split group by clause try this.
$buku = \DB::table('buku')
->select(['buku.*','kategory.*', 'tag.*'])
->leftJoin('kategori_buku', 'kategori_buku.id', '=', 'buku.id_kategori')
->leftJoin('detail_buku_tag', 'detail_buku_tag.id_buku', '=', 'buku.id')
->leftJoin(\DB::raw('(SELECT tag_buku.id, GROUP_CONCAT(tag) AS tag FROM tag_buku) AS tag_buku'),
'tag_buku.id', '=', 'detail_buku_tag.id_tag')
->groupBy('buku.id')
->get();
try this
$buku = \DB::table('buku')
->select([
'buku.*',
'kategory.*',
'tag.*'
])
->leftJoin('kategori_buku', 'kategori_buku.id', '=', 'buku.id_kategori')
->leftJoin('detail_buku_tag', 'detail_buku_tag.id_buku', '=', 'buku.id')
->leftJoin(DB::raw('SELECT tag_buku.id, GROUP_CONCAT(tag) AS tag FROM tag_buku) AS tag_buku ON tag_buku.id = detail_buku_tag.id_tag GROUP BY buku.id'))
->get();

Convert SQL to Eloquent to Join multiple table and Count

I have been this SQL working fine, but trying to convert it to Eloquent format, it keeps returning a wrong, SQL entirely which flags error.
Coding with Laravel 5.5
Select arm_articles.article_topic, arm_articles.id, arm_articles.article_id,
COUNT(arm_article_views.view_article_id) AS TotalViews,
COUNT( arm_article_likes.liked_article_id) AS TotalLikes,
COUNT( arm_article_comments.comment_article_id) AS TotalComments
FROM arm_articles
LEFT JOIN arm_article_views ON arm_articles.article_id = arm_article_views.view_article_id
LEFT JOIN arm_article_likes ON arm_articles.article_id = arm_article_likes.liked_article_id
LEFT JOIN arm_article_comments ON arm_articles.article_id = arm_article_comments.comment_article_id
GROUP BY arm_articles.article_id
ORDER BY TotalLikes, TotalLikes, TotalComments ASC`
HOW I MADE ELOQUENT QUERY BUILDER Not working though:
return Datatables::of(PostModel::leftJoin('arm_article_views', 'arm_article_views.view_article_id', '=', 'arm_articles.article_id')
->leftJoin('arm_article_likes','arm_article_likes.liked_article_id', '=', 'arm_articles.article_id')
->leftJoin('arm_article_comments', 'arm_article_comments.comment_article_id','=','arm_articles.article_id')
->selectRaw(
'arm_articles.*,
count(arm_article_views.view_article_id) AS ViewCount'
)
->groupBy('arm_articles.article_id')->orderBy('ViewCount','DESC')
->where('arm_articles.article_contributor_id','=',$contributor_id)
->getQuery())->make(true);
Any hint would be appreciated
Try with this:
DB::table('arm_articles')
->leftJoin('arm_article_views', 'arm_articles.article_id', '=', 'arm_article_views.view_article_id')
->leftJoin('arm_article_likes', 'arm_articles.article_id', '=', 'arm_article_likes.liked_article_id')
->leftJoin('arm_article_comments', 'arm_articles.article_id', '=', 'arm_article_comments.comment_article_id')
->groupBy('arm_articles.article_id', 'article_topic')
->select(
DB::raw('count(arm_article_views.view_article_id) as TotalViews'),
DB::raw('count(arm_article_likes.liked_article_id) as TotalLikes'),
DB::raw('count(arm_article_comments.comment_article_id) as TotalComments'),
'arm_articles.article_id',
'article_topic'
)->get();

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

Laravel 5: Native SQL to Query Builder

I am trying to join 2 tables on Laravel 5 and have to use Query Builder. I have already got the sql for it but i am not able to convert it to Query Builder syntax. SQL is below
SELECT v.id, r.full_name, b.full_name, s.full_name
FROM vehicles v
LEFT JOIN clients r ON v.representive_client_id = r.id
LEFT JOIN clients b ON v.buyer_client_id = b.id
LEFT JOIN clients s ON v.seller_client_id = s.id
and what i tried is
$query_result = DB::table('vehicles')
->selectRaw($query)
->leftJoin('clients', 'vehicles.representive_client_id', '=', 'clients.id')
->leftJoin('clients', 'vehicles.buyer_client_id ', '=', 'clients.id')
->leftJoin('clients', 'vehicles.seller_client_id ', '=', 'clients.id')
->paginate(30);
The problem is i dont know how to use AS caluse for Query Builder as i need to retrive 3 different types of full_name columns from vehicles table.Anybody can help me about how to write it in a proper Query Builder syntax ?. Any help would be appreciated.
You can use aliases with select columns and tables as well as joins, because the Query Builder will know to quote them correctly. So you can do this without any problems:
$query_result = DB::table('vehicles v')
->select('v.id', 'r.full_name as r_name', 'b.full_name as b_name', 's.full_name as s_name')
->leftJoin('clients r', 'vehicles.representive_client_id', '=', 'r.id')
->leftJoin('clients b', 'vehicles.buyer_client_id ', '=', 'b.id')
->leftJoin('clients s', 'vehicles.seller_client_id ', '=', 's.id')
->paginate(30);
Of course, you can use whatever aliases you want for the selected columns. Actually one of the examples in the Query Builder Selects Documentation uses aliases: email as user_email.
To check the SQL query generated by the Query Builder you can use the toSql method. So in your case instead of ->paginate(30) you can have ->toSql(), which will return a string with the SQL generated by the Query Builder, which you can compare to your raw query and see if it matches.

Why do Eloquent has and whereHas methods use subqueries instead of joins?

Why does Laravel use subqueries for the Eloquent has and whereHas methods instead of using joins? For example, I have constructed an Eloquent query like this:
Order::whereHas('orderProducts', function($query)
{
$query->where('product_id', $this->id);
})
->whereHas('status', function($query)
{
$query->where('sales_data', 1);
})
->where('ext_created_at', '>=', $startDate)
->where('ext_created_at', '<=', $endDate.' 23:59:59')
->distinct()
->count('id');
The resulting query is:
select count(distinct `id`) as aggregate from `orders` where `orders`.`deleted_at` is null and (select count(*) from `order_products` where `order_products`.`order_id` = `orders`.`id` and `product_id` = ? and `order_products`.`deleted_at` is null) >= 1 and (select count(*) from `order_statuses` where `orders`.`order_status_id` = `order_statuses`.`id` and `sales_data` = ? and `order_statuses`.`deleted_at` is null) >= 1 and `ext_created_at` >= ? and `ext_created_at` <= ?
This query returns a result of 16. According to DB::getQueryLog(), this takes 23222.14 ms to execute.
I then constructed another query like this:
DB::table('orders')
->leftJoin('order_products', 'orders.id', '=', 'order_products.order_id')
->leftJoin('order_statuses', 'orders.order_status_id', '=', 'order_statuses.id')
->whereNull('orders.deleted_at')
->where('order_products.product_id', $this->id)
->where('orders.ext_created_at', '>=', $startDate)
->where('orders.ext_created_at', '<=', $endDate.' 23:59:59')
->whereNull('order_products.deleted_at')
->where('order_statuses.sales_data', 1)
->distinct()
->count('orders.id')
The resulting query is:
select count(distinct `orders`.`id`) as aggregate from `orders` left join `order_products` on `orders`.`id` = `order_products`.`order_id` left join `order_statuses` on `orders`.`order_status_id` = `order_statuses`.`id` where `orders`.`deleted_at` is null and `order_products`.`product_id` = ? and `orders`.`ext_created_at` >= ? and `orders`.`ext_created_at` <= ? and `order_products`.`deleted_at` is null and `order_statuses`.`sales_data` = ?
This query also returns 16, but takes 3.52 ms to run. Obviously, using joins has much better performance here, but is that only specific to my use case, or are joins generally better performance? If so, why wouldn't Eloquent use joins over subqueries? I would much rather work with models and make use of all the excellent Eloquent methods like has and whereHas, but the performance is so dramatically different that I am being forced to construct the query myself using the Query Builder.
Am I missing something here?
I am using Laravel Framework version 4.2.16.

Categories